mirror of
https://codeberg.org/redict/redict.git
synced 2025-01-22 16:18:28 -05:00
Scripting: expire keys in scripts only at first access.
Keys expiring in the middle of the execution of Lua scripts are to create inconsistencies in masters and / or AOF files. See the following example: if redis.call("exists",KEYS[1]) == 1 then redis.call("incr","mycounter") end if redis.call("exists",KEYS[1]) == 1 then return redis.call("incr","mycounter") end The script executes two times the same *if key exists then incrementcounter* logic. However the two executions will work differently in the master and the slaves, provided some unlucky timing happens. In the master the first time the key may still exist, while the second time the key may no longer exist. This will result in the key incremented just one time. However as a side effect the master will generate a synthetic `DEL` command in the replication channel in order to force the slaves to expire the key (given that key expiration is master-driven). When the same script will run in the slave, the key will no longer be there, so the script will not increment the key. The key idea used to implement the expire-at-first-lookup semantics was provided by Marc Gravell.
This commit is contained in:
parent
b770079f2c
commit
b089ba98cc
16
src/db.c
16
src/db.c
@ -770,13 +770,21 @@ void propagateExpire(redisDb *db, robj *key) {
|
||||
}
|
||||
|
||||
int expireIfNeeded(redisDb *db, robj *key) {
|
||||
long long when = getExpire(db,key);
|
||||
mstime_t when = getExpire(db,key);
|
||||
mstime_t now;
|
||||
|
||||
if (when < 0) return 0; /* No expire for this key */
|
||||
|
||||
/* Don't expire anything while loading. It will be done later. */
|
||||
if (server.loading) return 0;
|
||||
|
||||
/* If we are in the context of a Lua script, we claim that time is
|
||||
* blocked to when the Lua script started. This way a key can expire
|
||||
* only the first time it is accessed and not in the middle of the
|
||||
* script execution, making propagation to slaves / AOF consistent.
|
||||
* See issue #1525 on Github for more information. */
|
||||
now = server.lua_caller ? server.lua_time_start : mstime();
|
||||
|
||||
/* If we are running in the context of a slave, return ASAP:
|
||||
* the slave key expiration is controlled by the master that will
|
||||
* send us synthesized DEL operations for expired keys.
|
||||
@ -784,12 +792,10 @@ int expireIfNeeded(redisDb *db, robj *key) {
|
||||
* Still we try to return the right information to the caller,
|
||||
* that is, 0 if we think the key should be still valid, 1 if
|
||||
* we think the key is expired at this time. */
|
||||
if (server.masterhost != NULL) {
|
||||
return mstime() > when;
|
||||
}
|
||||
if (server.masterhost != NULL) return now > when;
|
||||
|
||||
/* Return when this key has not expired */
|
||||
if (mstime() <= when) return 0;
|
||||
if (now <= when) return 0;
|
||||
|
||||
/* Delete the key */
|
||||
server.stat_expiredkeys++;
|
||||
|
Loading…
Reference in New Issue
Block a user