From 2c6fdf0f4e329a87b608c77f24c2bc7381b51650 Mon Sep 17 00:00:00 2001 From: John Sully Date: Sat, 28 Sep 2019 14:59:44 -0400 Subject: [PATCH] RREPLAY command now takes a DB argument Former-commit-id: 6e1e5bd08b59f8ad4653621a6c01fcf3a76f0692 --- src/debug.cpp | 6 +++-- src/replication.cpp | 22 ++++++++++++++++- tests/integration/replication-active.tcl | 13 ++++++++++ tests/test_helper.tcl | 1 + tests/unit/rreplay.tcl | 30 ++++++++++++++++++++++++ 5 files changed, 69 insertions(+), 3 deletions(-) create mode 100644 tests/unit/rreplay.tcl diff --git a/src/debug.cpp b/src/debug.cpp index 3246f9d19..3a4520776 100644 --- a/src/debug.cpp +++ b/src/debug.cpp @@ -682,10 +682,12 @@ NULL changeReplicationId(); clearReplicationId2(); addReply(c,shared.ok); - } else if (!strcasecmp(szFromObj(c->argv[1]),"stringmatch-test") && c->argc == 2) - { + } else if (!strcasecmp(szFromObj(c->argv[1]),"stringmatch-test") && c->argc == 2) { stringmatchlen_fuzz_test(); addReplyStatus(c,"Apparently Redis did not crash: test passed"); + } else if (!strcasecmp(szFromObj(c->argv[1]), "force-master") && c->argc == 2) { + c->flags |= CLIENT_MASTER | CLIENT_MASTER_FORCE_REPLY; + addReply(c, shared.ok); } else { addReplySubcommandSyntaxError(c); return; diff --git a/src/replication.cpp b/src/replication.cpp index e440c5440..5cd527259 100644 --- a/src/replication.cpp +++ b/src/replication.cpp @@ -323,9 +323,13 @@ void replicationFeedSlaves(list *slaves, int dictid, robj **argv, int argc) { char uuid[40] = {'\0'}; uuid_unparse(cserver.uuid, uuid); char proto[1024]; - int cchProto = snprintf(proto, sizeof(proto), "*3\r\n$7\r\nRREPLAY\r\n$%d\r\n%s\r\n$%lld\r\n", (int)strlen(uuid), uuid, cchbuf); + int cchProto = snprintf(proto, sizeof(proto), "*4\r\n$7\r\nRREPLAY\r\n$%d\r\n%s\r\n$%lld\r\n", (int)strlen(uuid), uuid, cchbuf); cchProto = std::min((int)sizeof(proto), cchProto); long long master_repl_offset_start = g_pserver->master_repl_offset; + + serverAssert(dictid >= 0); + char szDbNum[128]; + int cchDbNum = snprintf(szDbNum, sizeof(szDbNum), "$%d\r\n%d\r\n", (dictid/10)+1, dictid); /* Write the command to the replication backlog if any. */ if (g_pserver->repl_backlog) @@ -368,6 +372,7 @@ void replicationFeedSlaves(list *slaves, int dictid, robj **argv, int argc) { } const char *crlf = "\r\n"; feedReplicationBacklog(crlf, 2); + feedReplicationBacklog(szDbNum, cchDbNum); } } @@ -396,7 +401,10 @@ void replicationFeedSlaves(list *slaves, int dictid, robj **argv, int argc) { addReplyProtoAsync(slave, reply->buf(), reply->used); } if (!fSendRaw) + { addReplyAsync(slave,shared.crlf); + addReplyProtoAsync(slave, szDbNum, cchDbNum); + } } freeClient(fake); @@ -3266,6 +3274,7 @@ void replicaReplayCommand(client *c) // the replay command contains two arguments: // 1: The UUID of the source // 2: The raw command buffer to be replayed + // 3: (OPTIONAL) the database ID the command should apply to if (!(c->flags & CLIENT_MASTER)) { @@ -3298,6 +3307,17 @@ void replicaReplayCommand(client *c) return; } + if (c->argc >= 4) + { + long long db; + if (getLongLongFromObject(c->argv[3], &db) != C_OK || db >= cserver.dbnum || selectDb(c, (int)db) != C_OK) + { + addReplyError(c, "Invalid database ID"); + s_pstate->Cancel(); + return; + } + } + if (FSameUuidNoNil(uuid, cserver.uuid)) { addReply(c, shared.ok); diff --git a/tests/integration/replication-active.tcl b/tests/integration/replication-active.tcl index 99e0dc006..2ba761766 100644 --- a/tests/integration/replication-active.tcl +++ b/tests/integration/replication-active.tcl @@ -93,5 +93,18 @@ start_server {tags {"active-repl"} overrides {active-replica yes}} { assert_equal {0} [$master del testkey1] assert_equal {0} [$slave del testkey1] } + + test {Active replica different databases} { + $master select 3 + $master set testkey abcd + $master select 2 + $master del testkey + $slave select 3 + wait_for_condition 50 1000 { + [string match abcd [$slave get testkey]] + } else { + fail "Replication failed to propogate DB 3" + } + } } } diff --git a/tests/test_helper.tcl b/tests/test_helper.tcl index 6abbddbbe..a06afca3e 100644 --- a/tests/test_helper.tcl +++ b/tests/test_helper.tcl @@ -35,6 +35,7 @@ set ::all_tests { unit/quit unit/aofrw unit/acl + unit/rreplay integration/block-repl integration/replication integration/replication-2 diff --git a/tests/unit/rreplay.tcl b/tests/unit/rreplay.tcl new file mode 100644 index 000000000..2029f521d --- /dev/null +++ b/tests/unit/rreplay.tcl @@ -0,0 +1,30 @@ +start_server {tags {"rreplay"}} { + + test {RREPLAY use current db} { + r debug force-master + r select 4 + r set dbnum invalid + r rreplay "f4d5b2b5-4f07-4ee5-a4f2-5dc98507dfce" "*3\r\n\$3\r\nSET\r\n\$5\r\ndbnum\r\n\$4\r\nfour\r\n" + r get dbnum + } {four} + reconnect + + test {RREPLAY db different} { + r debug force-master + r select 4 + r set testkey four + r rreplay "f4d5b2b5-4f07-4ee5-a4f2-5dc98507dfce" "*3\r\n\$3\r\nSET\r\n\$7\r\ntestkey\r\n\$4\r\nbebe\r\n" 2 + r select 4 + assert { [r get testkey] == "four" } + r select 2 + r get testkey + } {bebe} + + reconnect + + test {RREPLAY not master} { + assert_error "*master*" {r rreplay "f4d5b2b5-4f07-4ee5-a4f2-5dc98507dfce" "*3\r\n\$3\r\nSET\r\n\$7\r\ntestkey\r\n\$4\r\nbebe\r\n" 2} + } + + r flushdb +}