Modules: Add RM_GetDetachedThreadSafeContext(). (#7886)
The main motivation here is to provide a way for modules to create a single, global context that can be used for logging. Currently, it is possible to obtain a thread-safe context that is not attached to any blocked client by using `RM_GetThreadSafeContext`. However, the attached context is not linked to the module identity so log messages produced are not tagged with the module name. Ideally we'd fix this in `RM_GetThreadSafeContext` itself but as it doesn't accept the current context as an argument there's no way to do that in a backwards compatible manner.
This commit is contained in:
parent
7d117d7591
commit
907da0580b
21
src/module.c
21
src/module.c
@ -4901,8 +4901,9 @@ int RM_BlockedClientDisconnected(RedisModuleCtx *ctx) {
|
|||||||
* that a blocked client was used when the context was created, otherwise
|
* that a blocked client was used when the context was created, otherwise
|
||||||
* no RedisModule_Reply* call should be made at all.
|
* no RedisModule_Reply* call should be made at all.
|
||||||
*
|
*
|
||||||
* TODO: thread safe contexts do not inherit the blocked client
|
* NOTE: If you're creating a detached thread safe context (bc is NULL),
|
||||||
* selected database. */
|
* consider using `RM_GetDetachedThreadSafeContext` which will also retain
|
||||||
|
* the module ID and thus be more useful for logging. */
|
||||||
RedisModuleCtx *RM_GetThreadSafeContext(RedisModuleBlockedClient *bc) {
|
RedisModuleCtx *RM_GetThreadSafeContext(RedisModuleBlockedClient *bc) {
|
||||||
RedisModuleCtx *ctx = zmalloc(sizeof(*ctx));
|
RedisModuleCtx *ctx = zmalloc(sizeof(*ctx));
|
||||||
RedisModuleCtx empty = REDISMODULE_CTX_INIT;
|
RedisModuleCtx empty = REDISMODULE_CTX_INIT;
|
||||||
@ -4924,6 +4925,21 @@ RedisModuleCtx *RM_GetThreadSafeContext(RedisModuleBlockedClient *bc) {
|
|||||||
return ctx;
|
return ctx;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Return a detached thread safe context that is not associated with any
|
||||||
|
* specific blocked client, but is associated with the module's context.
|
||||||
|
*
|
||||||
|
* This is useful for modules that wish to hold a global context over
|
||||||
|
* a long term, for purposes such as logging. */
|
||||||
|
RedisModuleCtx *RM_GetDetachedThreadSafeContext(RedisModuleCtx *ctx) {
|
||||||
|
RedisModuleCtx *new_ctx = zmalloc(sizeof(*new_ctx));
|
||||||
|
RedisModuleCtx empty = REDISMODULE_CTX_INIT;
|
||||||
|
memcpy(new_ctx,&empty,sizeof(empty));
|
||||||
|
new_ctx->module = ctx->module;
|
||||||
|
new_ctx->flags |= REDISMODULE_CTX_THREAD_SAFE;
|
||||||
|
new_ctx->client = createClient(NULL);
|
||||||
|
return new_ctx;
|
||||||
|
}
|
||||||
|
|
||||||
/* Release a thread safe context. */
|
/* Release a thread safe context. */
|
||||||
void RM_FreeThreadSafeContext(RedisModuleCtx *ctx) {
|
void RM_FreeThreadSafeContext(RedisModuleCtx *ctx) {
|
||||||
moduleFreeContext(ctx);
|
moduleFreeContext(ctx);
|
||||||
@ -8086,6 +8102,7 @@ void moduleRegisterCoreAPI(void) {
|
|||||||
REGISTER_API(AbortBlock);
|
REGISTER_API(AbortBlock);
|
||||||
REGISTER_API(Milliseconds);
|
REGISTER_API(Milliseconds);
|
||||||
REGISTER_API(GetThreadSafeContext);
|
REGISTER_API(GetThreadSafeContext);
|
||||||
|
REGISTER_API(GetDetachedThreadSafeContext);
|
||||||
REGISTER_API(FreeThreadSafeContext);
|
REGISTER_API(FreeThreadSafeContext);
|
||||||
REGISTER_API(ThreadSafeContextLock);
|
REGISTER_API(ThreadSafeContextLock);
|
||||||
REGISTER_API(ThreadSafeContextTryLock);
|
REGISTER_API(ThreadSafeContextTryLock);
|
||||||
|
@ -684,6 +684,7 @@ REDISMODULE_API void * (*RedisModule_GetBlockedClientPrivateData)(RedisModuleCtx
|
|||||||
REDISMODULE_API RedisModuleBlockedClient * (*RedisModule_GetBlockedClientHandle)(RedisModuleCtx *ctx) REDISMODULE_ATTR;
|
REDISMODULE_API RedisModuleBlockedClient * (*RedisModule_GetBlockedClientHandle)(RedisModuleCtx *ctx) REDISMODULE_ATTR;
|
||||||
REDISMODULE_API int (*RedisModule_AbortBlock)(RedisModuleBlockedClient *bc) REDISMODULE_ATTR;
|
REDISMODULE_API int (*RedisModule_AbortBlock)(RedisModuleBlockedClient *bc) REDISMODULE_ATTR;
|
||||||
REDISMODULE_API RedisModuleCtx * (*RedisModule_GetThreadSafeContext)(RedisModuleBlockedClient *bc) REDISMODULE_ATTR;
|
REDISMODULE_API RedisModuleCtx * (*RedisModule_GetThreadSafeContext)(RedisModuleBlockedClient *bc) REDISMODULE_ATTR;
|
||||||
|
REDISMODULE_API RedisModuleCtx * (*RedisModule_GetDetachedThreadSafeContext)(RedisModuleCtx *ctx) REDISMODULE_ATTR;
|
||||||
REDISMODULE_API void (*RedisModule_FreeThreadSafeContext)(RedisModuleCtx *ctx) REDISMODULE_ATTR;
|
REDISMODULE_API void (*RedisModule_FreeThreadSafeContext)(RedisModuleCtx *ctx) REDISMODULE_ATTR;
|
||||||
REDISMODULE_API void (*RedisModule_ThreadSafeContextLock)(RedisModuleCtx *ctx) REDISMODULE_ATTR;
|
REDISMODULE_API void (*RedisModule_ThreadSafeContextLock)(RedisModuleCtx *ctx) REDISMODULE_ATTR;
|
||||||
REDISMODULE_API int (*RedisModule_ThreadSafeContextTryLock)(RedisModuleCtx *ctx) REDISMODULE_ATTR;
|
REDISMODULE_API int (*RedisModule_ThreadSafeContextTryLock)(RedisModuleCtx *ctx) REDISMODULE_ATTR;
|
||||||
@ -919,6 +920,7 @@ static int RedisModule_Init(RedisModuleCtx *ctx, const char *name, int ver, int
|
|||||||
|
|
||||||
#ifdef REDISMODULE_EXPERIMENTAL_API
|
#ifdef REDISMODULE_EXPERIMENTAL_API
|
||||||
REDISMODULE_GET_API(GetThreadSafeContext);
|
REDISMODULE_GET_API(GetThreadSafeContext);
|
||||||
|
REDISMODULE_GET_API(GetDetachedThreadSafeContext);
|
||||||
REDISMODULE_GET_API(FreeThreadSafeContext);
|
REDISMODULE_GET_API(FreeThreadSafeContext);
|
||||||
REDISMODULE_GET_API(ThreadSafeContextLock);
|
REDISMODULE_GET_API(ThreadSafeContextLock);
|
||||||
REDISMODULE_GET_API(ThreadSafeContextTryLock);
|
REDISMODULE_GET_API(ThreadSafeContextTryLock);
|
||||||
|
@ -231,6 +231,30 @@ int test_clientinfo(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)
|
|||||||
return REDISMODULE_OK;
|
return REDISMODULE_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int test_log_tsctx(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)
|
||||||
|
{
|
||||||
|
RedisModuleCtx *tsctx = RedisModule_GetDetachedThreadSafeContext(ctx);
|
||||||
|
|
||||||
|
if (argc != 3) {
|
||||||
|
RedisModule_WrongArity(ctx);
|
||||||
|
return REDISMODULE_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
char level[50];
|
||||||
|
size_t level_len;
|
||||||
|
const char *level_str = RedisModule_StringPtrLen(argv[1], &level_len);
|
||||||
|
snprintf(level, sizeof(level) - 1, "%.*s", (int) level_len, level_str);
|
||||||
|
|
||||||
|
size_t msg_len;
|
||||||
|
const char *msg_str = RedisModule_StringPtrLen(argv[2], &msg_len);
|
||||||
|
|
||||||
|
RedisModule_Log(tsctx, level, "%.*s", (int) msg_len, msg_str);
|
||||||
|
RedisModule_FreeThreadSafeContext(tsctx);
|
||||||
|
|
||||||
|
RedisModule_ReplyWithSimpleString(ctx, "OK");
|
||||||
|
return REDISMODULE_OK;
|
||||||
|
}
|
||||||
|
|
||||||
int RedisModule_OnLoad(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {
|
int RedisModule_OnLoad(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {
|
||||||
REDISMODULE_NOT_USED(argv);
|
REDISMODULE_NOT_USED(argv);
|
||||||
REDISMODULE_NOT_USED(argc);
|
REDISMODULE_NOT_USED(argc);
|
||||||
@ -259,6 +283,8 @@ int RedisModule_OnLoad(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)
|
|||||||
return REDISMODULE_ERR;
|
return REDISMODULE_ERR;
|
||||||
if (RedisModule_CreateCommand(ctx,"test.clientinfo", test_clientinfo,"",0,0,0) == REDISMODULE_ERR)
|
if (RedisModule_CreateCommand(ctx,"test.clientinfo", test_clientinfo,"",0,0,0) == REDISMODULE_ERR)
|
||||||
return REDISMODULE_ERR;
|
return REDISMODULE_ERR;
|
||||||
|
if (RedisModule_CreateCommand(ctx,"test.log_tsctx", test_log_tsctx,"",0,0,0) == REDISMODULE_ERR)
|
||||||
|
return REDISMODULE_ERR;
|
||||||
|
|
||||||
return REDISMODULE_OK;
|
return REDISMODULE_OK;
|
||||||
}
|
}
|
||||||
|
@ -86,4 +86,9 @@ start_server {tags {"modules"}} {
|
|||||||
set info [r test.clientinfo]
|
set info [r test.clientinfo]
|
||||||
assert { [dict get $info flags] == "${ssl_flag}::tracking::" }
|
assert { [dict get $info flags] == "${ssl_flag}::tracking::" }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
test {test detached thread safe cnotext} {
|
||||||
|
r test.log_tsctx "info" "Test message"
|
||||||
|
verify_log_message 0 "*<misc> Test message*" 0
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user