Commit 0d8a7b4f authored by Georgios Bitzes's avatar Georgios Bitzes
Browse files

Implement LHGET with fallback on regular hash

parent af12ca17
Pipeline #386774 passed with stages
in 23 minutes and 34 seconds
......@@ -56,6 +56,7 @@ struct cmdMapInit {
redis_cmd_map["config_getall"] = {RedisCommand::CONFIG_GETALL, CommandType::READ};
redis_cmd_map["lhget"] = {RedisCommand::LHGET, CommandType::READ};
redis_cmd_map["lhlen"] = {RedisCommand::LHLEN, CommandType::READ};
redis_cmd_map["lhget_with_fallback"] = {RedisCommand::LHGET_WITH_FALLBACK, CommandType::READ};
redis_cmd_map["flushall"] = {RedisCommand::FLUSHALL, CommandType::WRITE};
redis_cmd_map["set"] = {RedisCommand::SET, CommandType::WRITE};
......@@ -78,6 +79,8 @@ struct cmdMapInit {
redis_cmd_map["lhset"] = {RedisCommand::LHSET, CommandType::WRITE};
redis_cmd_map["lhdel"] = {RedisCommand::LHDEL, CommandType::WRITE};
redis_cmd_map["lhmset"] = {RedisCommand::LHMSET, CommandType::WRITE};
redis_cmd_map["lhdel_with_fallback"] = {RedisCommand::LHDEL_WITH_FALLBACK, CommandType::WRITE};
redis_cmd_map["lhset_and_del_fallback"] = {RedisCommand::LHSET_AND_DEL_FALLBACK, CommandType::WRITE};
redis_cmd_map["exec"] = {RedisCommand::EXEC, CommandType::CONTROL};
redis_cmd_map["discard"] = {RedisCommand::DISCARD, CommandType::CONTROL};
......
......@@ -66,6 +66,10 @@ enum class RedisCommand {
LHLEN,
LHDEL,
LHGET_WITH_FALLBACK,
LHDEL_WITH_FALLBACK,
LHSET_AND_DEL_FALLBACK,
SADD,
SISMEMBER,
SREM,
......
......@@ -238,6 +238,22 @@ RedisEncodedResponse RedisDispatcher::dispatchWrite(StagingArea &stagingArea, Re
}
RedisEncodedResponse RedisDispatcher::dispatchHGET(StagingArea &stagingArea, const std::string &key, const std::string &field) {
std::string value;
rocksdb::Status st = store.hget(stagingArea, key, field, value);
if(st.IsNotFound()) return Formatter::null();
else if(!st.ok()) return Formatter::fromStatus(st);
return Formatter::string(value);
}
RedisEncodedResponse RedisDispatcher::dispatchLHGET(StagingArea &stagingArea, const std::string &key, const std::string &field, const std::string &hint) {
std::string value;
rocksdb::Status st = store.lhget(stagingArea, key, field, hint, value);
if(st.IsNotFound()) return Formatter::null();
else if(!st.ok()) return Formatter::fromStatus(st);
return Formatter::string(value);
}
RedisEncodedResponse RedisDispatcher::dispatchRead(StagingArea &stagingArea, RedisRequest &request) {
switch(request.getCommand()) {
case RedisCommand::GET: {
......@@ -282,12 +298,7 @@ RedisEncodedResponse RedisDispatcher::dispatchRead(StagingArea &stagingArea, Red
}
case RedisCommand::HGET: {
if(request.size() != 3) return errArgs(request);
std::string value;
rocksdb::Status st = store.hget(stagingArea, request[1], request[2], value);
if(st.IsNotFound()) return Formatter::null();
else if(!st.ok()) return Formatter::fromStatus(st);
return Formatter::string(value);
return dispatchHGET(stagingArea, request[1], request[2]);
}
case RedisCommand::HEXISTS: {
if(request.size() != 3) return errArgs(request);
......@@ -412,23 +423,32 @@ RedisEncodedResponse RedisDispatcher::dispatchRead(StagingArea &stagingArea, Red
return Formatter::vector(ret);
}
case RedisCommand::LHGET: {
std::string value;
rocksdb::Status st;
if(request.size() == 3) {
// Empty locality hint
st = store.lhget(stagingArea, request[1], request[2], "", value);
return dispatchLHGET(stagingArea, request[1], request[2], "");
}
if(request.size() == 4) {
return dispatchLHGET(stagingArea, request[1], request[2], request[3]);
}
else if(request.size() != 4) {
return errArgs(request);
return errArgs(request);
}
case RedisCommand::LHGET_WITH_FALLBACK: {
// First, try LHGET...
RedisEncodedResponse resp;
if(request.size() == 4) {
resp = dispatchLHGET(stagingArea, request[1], request[2], "");
}
else {
st = store.lhget(stagingArea, request[1], request[2], request[3], value);
else if(request.size() == 5) {
resp = dispatchLHGET(stagingArea, request[1], request[2], request[3]);
}
if(st.IsNotFound()) return Formatter::null();
else if(!st.ok()) return Formatter::fromStatus(st);
return Formatter::string(value);
// Did we succeed?
if(resp.val != Formatter::null().val) return resp;
// Nope, fallback. Look for the same field, but in the hash specified as
// last argument.
return dispatchHGET(stagingArea, request[request.size()-1], request[2]);
}
case RedisCommand::LHLEN: {
if(request.size() != 2) return errArgs(request);
......
......@@ -58,6 +58,10 @@ private:
RedisEncodedResponse dispatchingError(RedisRequest &request, LogIndex commit);
StateMachine &store;
RedisEncodedResponse dispatchHGET(StagingArea &stagingArea, const std::string &key, const std::string &field);
RedisEncodedResponse dispatchLHGET(StagingArea &stagingArea, const std::string &key, const std::string &field, const std::string &hint);
};
}
......
......@@ -955,13 +955,18 @@ TEST_F(Raft_e2e, LocalityHash) {
ASSERT_REPLY(tunnel(leaderID)->exec("lhget", "mykey", "f1"), "v1");
ASSERT_REPLY(tunnel(leaderID)->exec("lhget", "mykey", "f1", "hint1"), "v1");
ASSERT_REPLY(tunnel(leaderID)->exec("lhget", "mykey", "f1", "ayy-lmao"), "v1");
ASSERT_REPLY(tunnel(leaderID)->exec("lhlen", "mykey"), 1);
ASSERT_REPLY(tunnel(leaderID)->exec("lhget-with-fallback", "mykey", "f1", "emptykey"), "v1");
ASSERT_REPLY(tunnel(leaderID)->exec("lhget-with-fallback", "mykey", "f1", "hint1", "emptykey"), "v1");
ASSERT_REPLY(tunnel(leaderID)->exec("lhget-with-fallback", "mykey", "f1", "ayy-lmao", "emptykey"), "v1");
// Update old field, no changes to locality hint.
ASSERT_REPLY(tunnel(leaderID)->exec("lhset", "mykey", "f1", "hint1", "v2"), 0);
ASSERT_REPLY(tunnel(leaderID)->exec("lhget", "mykey", "f1"), "v2");
ASSERT_REPLY(tunnel(leaderID)->exec("lhget", "mykey", "f1", "hint1"), "v2");
ASSERT_REPLY(tunnel(leaderID)->exec("lhget", "mykey", "f1", "ayy-lmao"), "v2");
ASSERT_REPLY(tunnel(leaderID)->exec("lhget-with-fallback", "mykey", "f1", "emptykey"), "v2");
ASSERT_REPLY(tunnel(leaderID)->exec("lhget-with-fallback", "mykey", "f1", "hint1", "emptykey"), "v2");
ASSERT_REPLY(tunnel(leaderID)->exec("lhget-with-fallback", "mykey", "f1", "ayy-lmao", "emptykey"), "v2");
ASSERT_REPLY(tunnel(leaderID)->exec("lhlen", "mykey"), 1);
// Insert one more field.
......@@ -969,6 +974,9 @@ TEST_F(Raft_e2e, LocalityHash) {
ASSERT_REPLY(tunnel(leaderID)->exec("lhget", "mykey", "f2"), "v3");
ASSERT_REPLY(tunnel(leaderID)->exec("lhget", "mykey", "f2", "hint2"), "v3");
ASSERT_REPLY(tunnel(leaderID)->exec("lhget", "mykey", "f2", "hint1"), "v3");
ASSERT_REPLY(tunnel(leaderID)->exec("lhget-with-fallback", "mykey", "f2", "emptykey"), "v3");
ASSERT_REPLY(tunnel(leaderID)->exec("lhget-with-fallback", "mykey", "f2", "hint2", "emptykey"), "v3");
ASSERT_REPLY(tunnel(leaderID)->exec("lhget-with-fallback", "mykey", "f2", "hint1", "emptykey"), "v3");
ASSERT_REPLY(tunnel(leaderID)->exec("lhlen", "mykey"), 2);
// Update locality hint of first field.
......@@ -976,6 +984,9 @@ TEST_F(Raft_e2e, LocalityHash) {
ASSERT_REPLY(tunnel(leaderID)->exec("lhget", "mykey", "f1"), "v2");
ASSERT_REPLY(tunnel(leaderID)->exec("lhget", "mykey", "f1", "hint2"), "v2");
ASSERT_REPLY(tunnel(leaderID)->exec("lhget", "mykey", "f1", "hint1"), "v2");
ASSERT_REPLY(tunnel(leaderID)->exec("lhget-with-fallback", "mykey", "f1", "emptykey"), "v2");
ASSERT_REPLY(tunnel(leaderID)->exec("lhget-with-fallback", "mykey", "f1", "hint2", "emptykey"), "v2");
ASSERT_REPLY(tunnel(leaderID)->exec("lhget-with-fallback", "mykey", "f1", "hint1", "emptykey"), "v2");
ASSERT_REPLY(tunnel(leaderID)->exec("lhlen", "mykey"), 2);
// Update value and locality hint of second field.
......@@ -983,6 +994,9 @@ TEST_F(Raft_e2e, LocalityHash) {
ASSERT_REPLY(tunnel(leaderID)->exec("lhget", "mykey", "f2"), "v4");
ASSERT_REPLY(tunnel(leaderID)->exec("lhget", "mykey", "f2", "hint3"), "v4");
ASSERT_REPLY(tunnel(leaderID)->exec("lhget", "mykey", "f2", "hint1"), "v4");
ASSERT_REPLY(tunnel(leaderID)->exec("lhget-with-fallback", "mykey", "f2", "emptykey"), "v4");
ASSERT_REPLY(tunnel(leaderID)->exec("lhget-with-fallback", "mykey", "f2", "hint3", "emptykey"), "v4");
ASSERT_REPLY(tunnel(leaderID)->exec("lhget-with-fallback", "mykey", "f2", "hint1", "emptykey"), "v4");
ASSERT_REPLY(tunnel(leaderID)->exec("lhlen", "mykey"), 2);
// Insert one more field.
......@@ -990,6 +1004,9 @@ TEST_F(Raft_e2e, LocalityHash) {
ASSERT_REPLY(tunnel(leaderID)->exec("lhget", "mykey", "f3"), "v5");
ASSERT_REPLY(tunnel(leaderID)->exec("lhget", "mykey", "f3", "aaaaa"), "v5");
ASSERT_REPLY(tunnel(leaderID)->exec("lhget", "mykey", "f3", "wrong-hint"), "v5");
ASSERT_REPLY(tunnel(leaderID)->exec("lhget-with-fallback", "mykey", "f3", "emptykey"), "v5");
ASSERT_REPLY(tunnel(leaderID)->exec("lhget-with-fallback", "mykey", "f3", "aaaaa", "emptykey"), "v5");
ASSERT_REPLY(tunnel(leaderID)->exec("lhget-with-fallback", "mykey", "f3", "wrong-hint"), "v5");
ASSERT_REPLY(tunnel(leaderID)->exec("lhlen", "mykey"), 3);
// Re-read everything.
......@@ -1001,6 +1018,15 @@ TEST_F(Raft_e2e, LocalityHash) {
ASSERT_REPLY(tunnel(leaderID)->exec("lhget", "mykey", "f1", "hint2"), "v2");
ASSERT_REPLY(tunnel(leaderID)->exec("lhget", "mykey", "f1", "hint1"), "v2");
ASSERT_REPLY(tunnel(leaderID)->exec("lhget-with-fallback", "mykey", "f2", "emptykey"), "v4");
ASSERT_REPLY(tunnel(leaderID)->exec("lhget-with-fallback", "mykey", "f2", "hint3", "emptykey"), "v4");
ASSERT_REPLY(tunnel(leaderID)->exec("lhget-with-fallback", "mykey", "f2", "hint1", "emptykey"), "v4");
ASSERT_REPLY(tunnel(leaderID)->exec("lhget-with-fallback", "mykey", "f1", "emptykey"), "v2");
ASSERT_REPLY(tunnel(leaderID)->exec("lhget-with-fallback", "mykey", "f1", "hint2", "emptykey"), "v2");
ASSERT_REPLY(tunnel(leaderID)->exec("lhget-with-fallback", "mykey", "f1", "hint1", "emptykey"), "v2");
// Delete key.
ASSERT_REPLY(tunnel(leaderID)->exec("exists", "mykey"), 1);
ASSERT_REPLY(tunnel(leaderID)->exec("exists", "mykey", "mykey"), 2);
......@@ -1009,6 +1035,7 @@ TEST_F(Raft_e2e, LocalityHash) {
ASSERT_REPLY(tunnel(leaderID)->exec("lhlen", "mykey"), 0);
ASSERT_REPLY(tunnel(leaderID)->exec("del", "mykey"), 0);
ASSERT_REPLY(tunnel(leaderID)->exec("lhget", "mykey", "f3", "aaaaa"), "");
ASSERT_REPLY(tunnel(leaderID)->exec("lhget-with-fallback", "mykey", "f3", "aaaaa", "emptykey"), "");
// Recreate with five fields.
ASSERT_REPLY(tunnel(leaderID)->exec("lhset", "mykey", "f1", "hint1", "v1"), 1);
......@@ -1023,23 +1050,30 @@ TEST_F(Raft_e2e, LocalityHash) {
ASSERT_REPLY(tunnel(leaderID)->exec("lhlen", "mykey"), 4);
ASSERT_REPLY(tunnel(leaderID)->exec("lhget", "mykey", "f2"), "");
ASSERT_REPLY(tunnel(leaderID)->exec("lhget", "mykey", "f2", "hint2"), "");
ASSERT_REPLY(tunnel(leaderID)->exec("lhget-with-fallback", "mykey", "f2", "emptykey"), "");
ASSERT_REPLY(tunnel(leaderID)->exec("lhget-with-fallback", "mykey", "f2", "hint2", "emptykey"), "");
ASSERT_REPLY(tunnel(leaderID)->exec("lhdel", "mykey", "f2", "hint1"), 0);
ASSERT_REPLY(tunnel(leaderID)->exec("lhdel", "mykey", "f1", "f3"), 2);
ASSERT_REPLY(tunnel(leaderID)->exec("lhlen", "mykey"), 2);
ASSERT_REPLY(tunnel(leaderID)->exec("lhget", "mykey", "f4"), "v4");
ASSERT_REPLY(tunnel(leaderID)->exec("lhget", "mykey", "f5"), "v5");
ASSERT_REPLY(tunnel(leaderID)->exec("lhget-with-fallback", "mykey", "f4", "emptykey"), "v4");
ASSERT_REPLY(tunnel(leaderID)->exec("lhget-with-fallback", "mykey", "f5", "emptykey"), "v5");
ASSERT_REPLY(tunnel(leaderID)->exec("lhdel", "mykey", "f4", "f4", "f4", "f4"), 1);
ASSERT_REPLY(tunnel(leaderID)->exec("lhget", "mykey", "f4"), "");
ASSERT_REPLY(tunnel(leaderID)->exec("lhget-with-fallback", "mykey", "f4", "emptykey"), "");
ASSERT_REPLY(tunnel(leaderID)->exec("lhlen", "mykey"), 1);
ASSERT_REPLY(tunnel(leaderID)->exec("get", "mykey"), "ERR Invalid argument: WRONGTYPE Operation against a key holding the wrong kind of value");
ASSERT_REPLY(tunnel(leaderID)->exec("lhdel", "mykey", "f4"), 0);
ASSERT_REPLY(tunnel(leaderID)->exec("lhlen", "mykey"), 1);
ASSERT_REPLY(tunnel(leaderID)->exec("lhget", "mykey", "f5", "hint5"), "v5");
ASSERT_REPLY(tunnel(leaderID)->exec("lhget-with-fallback", "mykey", "f5", "hint5", "emptykey"), "v5");
ASSERT_REPLY(tunnel(leaderID)->exec("lhdel", "mykey", "f5"), 1);
ASSERT_REPLY(tunnel(leaderID)->exec("lhget", "mykey", "f5", "hint5"), "");
ASSERT_REPLY(tunnel(leaderID)->exec("lhget-with-fallback", "mykey", "f5", "hint5", "emptykey"), "");
ASSERT_REPLY(tunnel(leaderID)->exec("lhlen", "mykey"), 0);
ASSERT_REPLY(tunnel(leaderID)->exec("lhmset", "mykey", "f1", "hint1", "v1", "ayy"), "ERR wrong number of arguments for 'lhmset' command");
......@@ -1051,8 +1085,10 @@ TEST_F(Raft_e2e, LocalityHash) {
ASSERT_REPLY(tunnel(leaderID)->exec("lhlen", "mykey"), 1);
ASSERT_REPLY(tunnel(leaderID)->exec("lhget", "mykey", "f1"), "v1");
ASSERT_REPLY(tunnel(leaderID)->exec("lhget-with-fallback", "mykey", "f1", "emptykey"), "v1");
ASSERT_REPLY(tunnel(leaderID)->exec("lhmset", "mykey", "f1", "hint1", "v2", "f1", "hint3", "v3"), "OK");
ASSERT_REPLY(tunnel(leaderID)->exec("lhget", "mykey", "f1"), "v3");
ASSERT_REPLY(tunnel(leaderID)->exec("lhget-with-fallback", "mykey", "f1", "emptykey"), "v3");
ASSERT_REPLY(tunnel(leaderID)->exec("lhlen", "mykey"), 1);
ASSERT_REPLY(tunnel(leaderID)->exec("lhmset", "mykey", "f2", "hint2", "v5", "f3", "hint1", "v6"), "OK");
......@@ -1060,4 +1096,16 @@ TEST_F(Raft_e2e, LocalityHash) {
ASSERT_REPLY(tunnel(leaderID)->exec("lhget", "mykey", "f1"), "v3");
ASSERT_REPLY(tunnel(leaderID)->exec("lhget", "mykey", "f2"), "v5");
ASSERT_REPLY(tunnel(leaderID)->exec("lhget", "mykey", "f3"), "v6");
ASSERT_REPLY(tunnel(leaderID)->exec("lhget-with-fallback", "mykey", "f1", "emptykey"), "v3");
ASSERT_REPLY(tunnel(leaderID)->exec("lhget-with-fallback", "mykey", "f2", "emptykey"), "v5");
ASSERT_REPLY(tunnel(leaderID)->exec("lhget-with-fallback", "mykey", "f3", "emptykey"), "v6");
// Test fallback
ASSERT_REPLY(tunnel(leaderID)->exec("lhget-with-fallback", "mykey", "f9", "fb"), "");
ASSERT_REPLY(tunnel(leaderID)->exec("hset", "fb", "f9", "V"), 1);
ASSERT_REPLY(tunnel(leaderID)->exec("lhget-with-fallback", "mykey", "f9", "fb"), "V");
ASSERT_REPLY(tunnel(leaderID)->exec("lhset", "mykey", "f9", "hint1", "VVV"), 1);
ASSERT_REPLY(tunnel(leaderID)->exec("lhget-with-fallback", "mykey", "f9", "fb"), "VVV");
ASSERT_REPLY(tunnel(leaderID)->exec("lhlen", "mykey"), 4);
}
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment