From 45c99d7092926c0774608520b9a399dbd40f9714 Mon Sep 17 00:00:00 2001 From: guybe7 Date: Wed, 27 Jul 2022 13:40:05 +0200 Subject: [PATCH] Adds RM_Microseconds and RM_CachedMicroseconds (#11016) RM_Microseconds Return the wall-clock Unix time, in microseconds RM_CachedMicroseconds Returns a cached copy of the Unix time, in microseconds. It is updated in the server cron job and before executing a command. It is useful for complex call stacks, such as a command causing a key space notification, causing a module to execute a RedisModule_Call, causing another notification, etc. It makes sense that all these callbacks would use the same clock. --- src/module.c | 19 ++++++++++++++++++- src/redismodule.h | 7 ++++++- tests/modules/keyspace_events.c | 13 +++++++++++++ 3 files changed, 37 insertions(+), 2 deletions(-) diff --git a/src/module.c b/src/module.c index c9d8064d2..78e47b1eb 100644 --- a/src/module.c +++ b/src/module.c @@ -2027,7 +2027,7 @@ int RM_IsModuleNameBusy(const char *name) { } /* Return the current UNIX time in milliseconds. */ -long long RM_Milliseconds(void) { +mstime_t RM_Milliseconds(void) { return mstime(); } @@ -2036,6 +2036,21 @@ uint64_t RM_MonotonicMicroseconds(void) { return getMonotonicUs(); } +/* Return the current UNIX time in microseconds */ +ustime_t RM_Microseconds() { + return ustime(); +} + +/* Return the cached UNIX time in microseconds. + * It is updated in the server cron job and before executing a command. + * It is useful for complex call stacks, such as a command causing a + * key space notification, causing a module to execute a RedisModule_Call, + * causing another notification, etc. + * It makes sense that all this callbacks would use the same clock. */ +ustime_t RM_CachedMicroseconds() { + return server.ustime; +} + /* Mark a point in time that will be used as the start time to calculate * the elapsed execution time when RM_BlockedClientMeasureTimeEnd() is called. * Within the same command, you can call multiple times @@ -12578,6 +12593,8 @@ void moduleRegisterCoreAPI(void) { REGISTER_API(AbortBlock); REGISTER_API(Milliseconds); REGISTER_API(MonotonicMicroseconds); + REGISTER_API(Microseconds); + REGISTER_API(CachedMicroseconds); REGISTER_API(BlockedClientMeasureTimeStart); REGISTER_API(BlockedClientMeasureTimeEnd); REGISTER_API(GetThreadSafeContext); diff --git a/src/redismodule.h b/src/redismodule.h index 205a0568d..51d22dd52 100644 --- a/src/redismodule.h +++ b/src/redismodule.h @@ -752,6 +752,7 @@ typedef enum { #ifndef REDISMODULE_CORE typedef long long mstime_t; +typedef long long ustime_t; /* Macro definitions specific to individual compilers */ #ifndef REDISMODULE_ATTR_UNUSED @@ -1055,8 +1056,10 @@ REDISMODULE_API int (*RedisModule_GetDbIdFromOptCtx)(RedisModuleKeyOptCtx *ctx) REDISMODULE_API int (*RedisModule_GetToDbIdFromOptCtx)(RedisModuleKeyOptCtx *ctx) REDISMODULE_ATTR; REDISMODULE_API const RedisModuleString * (*RedisModule_GetKeyNameFromOptCtx)(RedisModuleKeyOptCtx *ctx) REDISMODULE_ATTR; REDISMODULE_API const RedisModuleString * (*RedisModule_GetToKeyNameFromOptCtx)(RedisModuleKeyOptCtx *ctx) REDISMODULE_ATTR; -REDISMODULE_API long long (*RedisModule_Milliseconds)(void) REDISMODULE_ATTR; +REDISMODULE_API mstime_t (*RedisModule_Milliseconds)(void) REDISMODULE_ATTR; REDISMODULE_API uint64_t (*RedisModule_MonotonicMicroseconds)(void) REDISMODULE_ATTR; +REDISMODULE_API ustime_t (*RedisModule_Microseconds)(void) REDISMODULE_ATTR; +REDISMODULE_API ustime_t (*RedisModule_CachedMicroseconds)(void) REDISMODULE_ATTR; REDISMODULE_API void (*RedisModule_DigestAddStringBuffer)(RedisModuleDigest *md, const char *ele, size_t len) REDISMODULE_ATTR; REDISMODULE_API void (*RedisModule_DigestAddLongLong)(RedisModuleDigest *md, long long ele) REDISMODULE_ATTR; REDISMODULE_API void (*RedisModule_DigestEndSequence)(RedisModuleDigest *md) REDISMODULE_ATTR; @@ -1390,6 +1393,8 @@ static int RedisModule_Init(RedisModuleCtx *ctx, const char *name, int ver, int REDISMODULE_GET_API(GetToDbIdFromOptCtx); REDISMODULE_GET_API(Milliseconds); REDISMODULE_GET_API(MonotonicMicroseconds); + REDISMODULE_GET_API(Microseconds); + REDISMODULE_GET_API(CachedMicroseconds); REDISMODULE_GET_API(DigestAddStringBuffer); REDISMODULE_GET_API(DigestAddLongLong); REDISMODULE_GET_API(DigestEndSequence); diff --git a/tests/modules/keyspace_events.c b/tests/modules/keyspace_events.c index 58670164d..c0f79bf37 100644 --- a/tests/modules/keyspace_events.c +++ b/tests/modules/keyspace_events.c @@ -30,10 +30,14 @@ * POSSIBILITY OF SUCH DAMAGE. */ +#define _DEFAULT_SOURCE /* For usleep */ #include "redismodule.h" #include #include +#include + +ustime_t cached_time = 0; /** stores all the keys on which we got 'loaded' keyspace notification **/ RedisModuleDict *loaded_event_log = NULL; @@ -59,6 +63,12 @@ static int KeySpace_NotificationLoaded(RedisModuleCtx *ctx, int type, const char static int KeySpace_NotificationGeneric(RedisModuleCtx *ctx, int type, const char *event, RedisModuleString *key) { REDISMODULE_NOT_USED(type); + if (cached_time) { + RedisModule_Assert(cached_time == RedisModule_CachedMicroseconds()); + usleep(1); + RedisModule_Assert(cached_time != RedisModule_Microseconds()); + } + if (strcmp(event, "del") == 0) { RedisModuleString *copykey = RedisModule_CreateStringPrintf(ctx, "%s_copy", RedisModule_StringPtrLen(key, NULL)); RedisModuleCallReply* rep = RedisModule_Call(ctx, "DEL", "s!", copykey); @@ -158,6 +168,8 @@ static int cmdDelKeyCopy(RedisModuleCtx *ctx, RedisModuleString **argv, int argc if (argc != 2) return RedisModule_WrongArity(ctx); + cached_time = RedisModule_CachedMicroseconds(); + RedisModuleCallReply* rep = RedisModule_Call(ctx, "DEL", "s!", argv[1]); if (!rep) { RedisModule_ReplyWithError(ctx, "NULL reply returned"); @@ -165,6 +177,7 @@ static int cmdDelKeyCopy(RedisModuleCtx *ctx, RedisModuleString **argv, int argc RedisModule_ReplyWithCallReply(ctx, rep); RedisModule_FreeCallReply(rep); } + cached_time = 0; return REDISMODULE_OK; }