diff --git a/src/module.c b/src/module.c index 507c3e6e..08389c20 100644 --- a/src/module.c +++ b/src/module.c @@ -649,6 +649,37 @@ int RM_DeleteKey(RedisModuleKey *key) { return REDISMODULE_OK; } +/* Return the key expire value, as milliseconds of remaining TTL. + * If no TTL is associated with the key or if the key is empty, + * REDISMODULE_NO_EXPIRE is returned. */ +mstime_t RM_GetExpire(RedisModuleKey *key) { + mstime_t expire = getExpire(key->db,key->key); + if (expire == -1 || key->value == NULL) return -1; + expire -= mstime(); + return expire >= 0 ? expire : 0; +} + +/* Set a new expire for the key. If the special expire + * REDISMODULE_NO_EXPIRE is set, the expire is cancelled if there was + * one (the same as the PERSIST command). + * + * Note that the expire must be provided as a positive integer representing + * the number of milliseconds of TTL the key should have. + * + * The function returns REDISMODULE_OK on success or REDISMODULE_ERR if + * the key was not open for writing or is an empty key. */ +int RM_SetExpire(RedisModuleKey *key, mstime_t expire) { + if (!(key->mode & REDISMODULE_WRITE) || key->value == NULL) + return REDISMODULE_ERR; + if (expire != REDISMODULE_NO_EXPIRE) { + expire += mstime(); + setExpire(key->db,key->key,expire); + } else { + removeExpire(key->db,key->key); + } + return REDISMODULE_OK; +} + /* -------------------------------------------------------------------------- * Key API for String type * -------------------------------------------------------------------------- */ @@ -1241,6 +1272,8 @@ void moduleRegisterCoreAPI(void) { REGISTER_API(StringSet); REGISTER_API(StringDMA); REGISTER_API(StringTruncate); + REGISTER_API(SetExpire); + REGISTER_API(GetExpire); } /* Global initialization at Redis startup. */ diff --git a/src/modules/API.md b/src/modules/API.md index 0dcd3ffa..93b68c84 100644 --- a/src/modules/API.md +++ b/src/modules/API.md @@ -520,6 +520,38 @@ by new key commands. For example `RedisModule_KeyType()` will return it is an empty key, and writing to it will create a new key, possibly of another type (depending on the API used). +## Managing key expires (TTLs) + +To control key expires two functions are provided, that are able to set, +modify, get, and unset the time to live associated with a key. + +One function is used in order to query the current expire of an open key: + + mstime_t RedisModule_GetExpire(RedisModuleKey *key); + +The function returns the time to live of the key in milliseconds, or +`REDISMODULE_NO_EXPIRE` as a special value to signal the key has no associated +expire or does not exist at all (you can differentiate the two cases checking +if the key type is `REDISMODULE_KEYTYPE_EMPTY`). + +In order to change the expire of a key the following function is used instead: + + int RedisModule_SetExpire(RedisModuleKey *key, mstime_t expire); + +When called on a non existing key, `REDISMODULE_ERR` is returned, because +the function can only associate expires to existing open keys (non existing +open keys are only useful in order to create new values with data type +specific write operations). + +Again the `expire` time is specified in milliseconds. If the key has currently +no expire, a new expire is set. If the key already have an expire, it is +replaced with the new value. + +If the key has an expire, and the special value `REDISMODULE_NO_EXPIRE` is +used as a new expire, the expire is removed, similarly to the Redis +`PERSIST` command. In case the key was already persistent, no operation is +performed. + ## Obtaining the length of values There is a single function in order to retrieve the length of the value diff --git a/src/modules/helloworld.c b/src/modules/helloworld.c index 477a1272..7480415e 100644 --- a/src/modules/helloworld.c +++ b/src/modules/helloworld.c @@ -303,6 +303,29 @@ int HelloToggleCase_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, return REDISMODULE_OK; } +/* HELLO.MORE.EXPIRE key milliseconds. + * + * If they key has already an associated TTL, extends it by "milliseconds" + * milliseconds. Otherwise no operation is performed. */ +int HelloMoreExpire_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { + RedisModule_AutoMemory(ctx); /* Use automatic memory management. */ + if (argc != 3) return RedisModule_WrongArity(ctx); + + mstime_t addms, expire; + + if (RedisModule_StringToLongLong(argv[2],&addms) != REDISMODULE_OK) + return RedisModule_ReplyWithError(ctx,"ERR invalid expire time"); + + RedisModuleKey *key = RedisModule_OpenKey(ctx,argv[1], + REDISMODULE_READ|REDISMODULE_WRITE); + expire = RedisModule_GetExpire(key); + if (expire != REDISMODULE_NO_EXPIRE) { + expire += addms; + RedisModule_SetExpire(key,expire); + } + return RedisModule_ReplyWithSimpleString(ctx,"OK"); +} + /* This function must be present on each Redis module. It is used in order to * register the commands into the Redis server. */ int RedisModule_OnLoad(RedisModuleCtx *ctx) { @@ -353,5 +376,9 @@ int RedisModule_OnLoad(RedisModuleCtx *ctx) { HelloToggleCase_RedisCommand) == REDISMODULE_ERR) return REDISMODULE_ERR; + if (RedisModule_CreateCommand(ctx,"hello.more.expire", + HelloMoreExpire_RedisCommand) == REDISMODULE_ERR) + return REDISMODULE_ERR; + return REDISMODULE_OK; } diff --git a/src/redismodule.h b/src/redismodule.h index a4d42739..0af014cf 100644 --- a/src/redismodule.h +++ b/src/redismodule.h @@ -36,6 +36,9 @@ #define REDISMODULE_REPLY_ARRAY 3 #define REDISMODULE_REPLY_NULL 4 +/* Expire */ +#define REDISMODULE_NO_EXPIRE -1 + /* Error messages. */ #define REDISMODULE_ERRORMSG_WRONGTYPE "WRONGTYPE Operation against a key holding the wrong kind of value" @@ -43,6 +46,8 @@ #ifndef REDISMODULE_CORE +typedef long long mstime_t; + /* Incomplete structures for compiler checks but opaque access. */ typedef struct RedisModuleCtx RedisModuleCtx; typedef struct RedisModuleKey RedisModuleKey; @@ -97,6 +102,8 @@ int REDISMODULE_API_FUNC(RedisModule_DeleteKey)(RedisModuleKey *key); int REDISMODULE_API_FUNC(RedisModule_StringSet)(RedisModuleKey *key, RedisModuleString *str); char *REDISMODULE_API_FUNC(RedisModule_StringDMA)(RedisModuleKey *key, size_t *len, int mode); int REDISMODULE_API_FUNC(RedisModule_StringTruncate)(RedisModuleKey *key, size_t newlen); +mstime_t REDISMODULE_API_FUNC(RedisModule_GetExpire)(RedisModuleKey *key); +int REDISMODULE_API_FUNC(RedisModule_SetExpire)(RedisModuleKey *key, mstime_t expire); /* This is included inline inside each Redis module. */ static int RedisModule_Init(RedisModuleCtx *ctx, const char *name, int ver, int apiver) { @@ -142,6 +149,8 @@ static int RedisModule_Init(RedisModuleCtx *ctx, const char *name, int ver, int REDISMODULE_GET_API(StringSet); REDISMODULE_GET_API(StringDMA); REDISMODULE_GET_API(StringTruncate); + REDISMODULE_GET_API(GetExpire); + REDISMODULE_GET_API(SetExpire); RedisModule_SetModuleAttribs(ctx,name,ver,apiver); return REDISMODULE_OK;