diff --git a/src/db.c b/src/db.c index 27af0a53e..ec68c228c 100644 --- a/src/db.c +++ b/src/db.c @@ -1453,7 +1453,12 @@ void propagateExpire(redisDb *db, robj *key, int lazy) { incrRefCount(argv[0]); incrRefCount(argv[1]); + /* If the master decided to expire a key we must propagate it to replicas no matter what.. + * Even if module executed a command without asking for propagation. */ + int prev_replication_allowed = server.replication_allowed; + server.replication_allowed = 1; propagate(server.delCommand,db->id,argv,2,PROPAGATE_AOF|PROPAGATE_REPL); + server.replication_allowed = prev_replication_allowed; decrRefCount(argv[0]); decrRefCount(argv[1]); diff --git a/tests/modules/propagate.c b/tests/modules/propagate.c index 7a63ed807..ac04d4f9d 100644 --- a/tests/modules/propagate.c +++ b/tests/modules/propagate.c @@ -192,6 +192,19 @@ int propagateTestNestedCommand(RedisModuleCtx *ctx, RedisModuleString **argv, in return REDISMODULE_OK; } +int propagateTestIncr(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) +{ + REDISMODULE_NOT_USED(argc); + RedisModuleCallReply *reply; + + /* This test propagates the module command, not the INCR it executes. */ + reply = RedisModule_Call(ctx, "INCR", "s", argv[1]); + RedisModule_ReplyWithCallReply(ctx,reply); + RedisModule_FreeCallReply(reply); + RedisModule_ReplicateVerbatim(ctx); + return REDISMODULE_OK; +} + int RedisModule_OnLoad(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { REDISMODULE_NOT_USED(argv); REDISMODULE_NOT_USED(argc); @@ -234,5 +247,10 @@ int RedisModule_OnLoad(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) "",1,1,1) == REDISMODULE_ERR) return REDISMODULE_ERR; + if (RedisModule_CreateCommand(ctx,"propagate-test.incr", + propagateTestIncr, + "",1,1,1) == REDISMODULE_ERR) + return REDISMODULE_ERR; + return REDISMODULE_OK; } diff --git a/tests/unit/moduleapi/propagate.tcl b/tests/unit/moduleapi/propagate.tcl index 108637dd9..a8c710074 100644 --- a/tests/unit/moduleapi/propagate.tcl +++ b/tests/unit/moduleapi/propagate.tcl @@ -2,7 +2,7 @@ set testmodule [file normalize tests/modules/propagate.so] tags "modules" { test {Modules can propagate in async and threaded contexts} { - start_server {} { + start_server [list overrides [list loadmodule "$testmodule"]] { set replica [srv 0 client] set replica_host [srv 0 host] set replica_port [srv 0 port] @@ -213,6 +213,31 @@ tags "modules" { } close_replication_stream $repl } + + test {module RM_Call of expired key propagation} { + $master debug set-active-expire 0 + + $master set k1 900 px 100 + wait_for_ofs_sync $master $replica + after 110 + + set repl [attach_to_replication_stream] + $master propagate-test.incr k1 + wait_for_ofs_sync $master $replica + + assert_replication_stream $repl { + {select *} + {del k1} + {propagate-test.incr k1} + } + close_replication_stream $repl + + assert_equal [$master get k1] 1 + assert_equal [$master ttl k1] -1 + assert_equal [$replica get k1] 1 + assert_equal [$replica ttl k1] -1 + } + assert_equal [s -1 unexpected_error_replies] 0 } }