diff options
author | antirez <antirez@gmail.com> | 2019-11-06 09:57:29 +0100 |
---|---|---|
committer | antirez <antirez@gmail.com> | 2019-11-06 17:31:52 +0100 |
commit | d3f4dec440501bdd4823334e6e716667692426e0 (patch) | |
tree | acefe671bb3b47efa0db57c97738b259afb2f560 | |
parent | 33f42665a810e7aee50b277df30149afbdc5726c (diff) | |
download | redis-d3f4dec440501bdd4823334e6e716667692426e0.tar.gz |
Update PR #6537: use a fresh time outside call().
One problem with the solution proposed so far in #6537 is that key
lookups outside a command execution via call(), still used a cached
time. The cached time needed to be refreshed in multiple places,
especially because of modules callbacks from timers, cluster bus, and
thread safe contexts, that may use RM_Open().
In order to avoid this problem, this commit introduces the ability to
detect if we are inside call(): this way we can use the reference fixed
time only when we are in the context of a command execution or Lua
script, but for the asynchronous lookups, we can still use mstime() to
get a fresh time reference.
-rw-r--r-- | src/db.c | 27 | ||||
-rw-r--r-- | src/server.c | 4 | ||||
-rw-r--r-- | src/server.h | 3 |
3 files changed, 27 insertions, 7 deletions
@@ -1129,6 +1129,7 @@ void propagateExpire(redisDb *db, robj *key, int lazy) { /* Check if the key is expired. */ int keyIsExpired(redisDb *db, robj *key) { mstime_t when = getExpire(db,key); + mstime_t now; if (when < 0) return 0; /* No expire for this key */ @@ -1139,13 +1140,27 @@ int keyIsExpired(redisDb *db, robj *key) { * 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. - * - * Outside the Lua script execution, we use the cached time server.mstime - * that is updated before commands executions in call(). */ - mstime_t now = server.lua_caller ? server.lua_time_start : - server.mstime; + * See issue #1525 on Github for more information. */ + if (server.lua_caller) { + now = server.lua_time_start; + } + /* If we are in the middle of a command execution, we still want to use + * a reference time that does not change: in that case we just use the + * cached time, that we update before each call in the call() function. + * This way we avoid that commands such as RPOPLPUSH or similar, that + * may re-open the same key multiple times, can invalidate an already + * open object in a next call, if the next call will see the key expired, + * while the first did not. */ + else if (server.call_depth > 0) { + now = server.mstime; + } + /* For the other cases, we want to use the most fresh time we have. */ + else { + now = mstime(); + } + /* The key expired if the current (virtual or real) time is greater + * than the expire time of the key. */ return now > when; } diff --git a/src/server.c b/src/server.c index 794055632..caf943f80 100644 --- a/src/server.c +++ b/src/server.c @@ -2038,6 +2038,7 @@ void initServer(void) { server.hz = server.config_hz; server.pid = getpid(); server.current_client = NULL; + server.call_depth = 0; server.clients = listCreate(); server.clients_index = raxNew(); server.clients_to_close = listCreate(); @@ -2430,6 +2431,8 @@ void call(client *c, int flags) { int client_old_flags = c->flags; struct redisCommand *real_cmd = c->cmd; + server.call_depth++; + /* Sent the command to clients in MONITOR mode, only if the commands are * not generated from reading an AOF. */ if (listLength(server.monitors) && @@ -2544,6 +2547,7 @@ void call(client *c, int flags) { redisOpArrayFree(&server.also_propagate); } server.also_propagate = prev_also_propagate; + server.call_depth--; server.stat_numcommands++; } diff --git a/src/server.h b/src/server.h index a9f5313da..42831d93c 100644 --- a/src/server.h +++ b/src/server.h @@ -989,7 +989,8 @@ struct redisServer { list *clients_to_close; /* Clients to close asynchronously */ list *clients_pending_write; /* There is to write or install handler. */ list *slaves, *monitors; /* List of slaves and MONITORs */ - client *current_client; /* Current client, only used on crash report */ + client *current_client; /* Current client executing the command. */ + long call_depth; /* call() re-entering count. */ rax *clients_index; /* Active clients dictionary by client ID. */ int clients_paused; /* True if clients are currently paused */ mstime_t clients_pause_end_time; /* Time when we undo clients_paused */ |