Modules: Replicate lazy-expire even if replication is not allowed (#8816)
Before this commit using RM_Call without "!" could cause the master to lazy-expire a key (delete it) but without replicating to replicas. This could cause the replica's memory usage to gradually grow and could also cause consistency issues if the master and replica have a clock diff. This bug was introduced in #8617 Added a test which demonstrates that scenario.
This commit is contained in:
parent
7a3d1487e4
commit
f40ca9cb58
5
src/db.c
5
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]);
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user