diff options
author | guybe7 <guy.benoish@redislabs.com> | 2020-11-02 17:18:42 +0100 |
---|---|---|
committer | Oran Agra <oran@redislabs.com> | 2021-01-12 16:25:37 +0200 |
commit | acb4cd213553f6edddd9d16b8e2906684e5433f7 (patch) | |
tree | 183bc8a1137fb2b924d61a6d7968d86872419455 | |
parent | fb86ecb802c45fac54aa7087e37592a9cbc0ab14 (diff) | |
download | redis-acb4cd213553f6edddd9d16b8e2906684e5433f7.tar.gz |
Modules: Improve timer accuracy (#7987)
The bug occurs when 'callback' re-registers itself to a point
in the future and the execution time in non-negligible:
'now' refers to time BEFORE callback was executed and is used
to calculate 'next_period'.
We must get the actual current time when calculating 'next_period'
(cherry picked from commit 1a91a2700b24211e90c695d3fdbbfe7e8d75dbe4)
-rw-r--r-- | src/module.c | 16 |
1 files changed, 14 insertions, 2 deletions
diff --git a/src/module.c b/src/module.c index 3c96c601b..ae7fa22b2 100644 --- a/src/module.c +++ b/src/module.c @@ -5441,7 +5441,10 @@ int moduleTimerHandler(struct aeEventLoop *eventLoop, long long id, void *client raxRemove(Timers,(unsigned char*)ri.key,ri.key_len,NULL); zfree(timer); } else { - next_period = (expiretime-now)/1000; /* Scale to milliseconds. */ + /* We call ustime() again instead of using the cached 'now' so that + * 'next_period' isn't affected by the time it took to execute + * previous calls to 'callback. */ + next_period = (expiretime-ustime())/1000; /* Scale to milliseconds. */ break; } } @@ -5454,7 +5457,16 @@ int moduleTimerHandler(struct aeEventLoop *eventLoop, long long id, void *client /* Create a new timer that will fire after `period` milliseconds, and will call * the specified function using `data` as argument. The returned timer ID can be - * used to get information from the timer or to stop it before it fires. */ + * used to get information from the timer or to stop it before it fires. + * Note that for the common use case of a repeating timer (Re-registration + * of the timer inside the RedisModuleTimerProc callback) it matters when + * this API is called: + * If it is called at the beginning of 'callback' it means + * the event will triggered every 'period'. + * If it is called at the end of 'callback' it means + * there will 'period' milliseconds gaps between events. + * (If the time it takes to execute 'callback' is negligible the two + * statements above mean the same) */ RedisModuleTimerID RM_CreateTimer(RedisModuleCtx *ctx, mstime_t period, RedisModuleTimerProc callback, void *data) { RedisModuleTimer *timer = zmalloc(sizeof(*timer)); timer->module = ctx->module; |