Skip to content

Commit

Permalink
Added new defrag API to allocate and free raw memory.
Browse files Browse the repository at this point in the history
All the defrag allocations API expects to get a value and replace it,
leaving the old value untouchable. In some cases a value might be shared
between multiple keys, in such cases we can not simply replace it when
the defrag callback is called.

To allow support such use cases, the PR adds two new API's to the
defrag API:

1. RM_DefragAllocRaw - allocate memory base on a given size.
2. RM_DefragFreeRaw - Free the given pointer.

Those API's avoid using tcache so they operate just like RM_DefragAlloc
but allows the user to split the allocation and the memory free operations
into two stages and control when those happen.
  • Loading branch information
MeirShpilraien committed Sep 2, 2024
1 parent 3fcddfb commit 21ad53f
Show file tree
Hide file tree
Showing 6 changed files with 42 additions and 0 deletions.
10 changes: 10 additions & 0 deletions src/defrag.c
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,16 @@ void* activeDefragAlloc(void *ptr) {
return newptr;
}

/* Raw memory allocation for defrag, avoid using tcache. */
void *activeDefragAllocRaw(size_t size) {
return zmalloc_no_tcache(size);
}

/* Raw memory free for defrag, avoid using tcache. */
void activeDefragFreeRaw(void *ptr) {
zfree_no_tcache(ptr);
}

/*Defrag helper for sds strings
*
* returns NULL in case the allocation wasn't moved.
Expand Down
12 changes: 12 additions & 0 deletions src/module.c
Original file line number Diff line number Diff line change
Expand Up @@ -13529,6 +13529,16 @@ void *RM_DefragAlloc(RedisModuleDefragCtx *ctx, void *ptr) {
return activeDefragAlloc(ptr);
}

void *RM_DefragAllocRaw(RedisModuleDefragCtx *ctx, size_t size) {
UNUSED(ctx);
return activeDefragAllocRaw(size);
}

void RM_DefragFreeRaw(RedisModuleDefragCtx *ctx, void *ptr) {
UNUSED(ctx);
activeDefragFreeRaw(ptr);
}

/* Defrag a RedisModuleString previously allocated by RM_Alloc, RM_Calloc, etc.
* See RM_DefragAlloc() for more information on how the defragmentation process
* works.
Expand Down Expand Up @@ -13981,6 +13991,8 @@ void moduleRegisterCoreAPI(void) {
REGISTER_API(GetTypeMethodVersion);
REGISTER_API(RegisterDefragFunc);
REGISTER_API(DefragAlloc);
REGISTER_API(DefragAllocRaw);
REGISTER_API(DefragFreeRaw);
REGISTER_API(DefragRedisModuleString);
REGISTER_API(DefragShouldStop);
REGISTER_API(DefragCursorSet);
Expand Down
4 changes: 4 additions & 0 deletions src/redismodule.h
Original file line number Diff line number Diff line change
Expand Up @@ -1297,6 +1297,8 @@ REDISMODULE_API int *(*RedisModule_GetCommandKeysWithFlags)(RedisModuleCtx *ctx,
REDISMODULE_API const char *(*RedisModule_GetCurrentCommandName)(RedisModuleCtx *ctx) REDISMODULE_ATTR;
REDISMODULE_API int (*RedisModule_RegisterDefragFunc)(RedisModuleCtx *ctx, RedisModuleDefragFunc func) REDISMODULE_ATTR;
REDISMODULE_API void *(*RedisModule_DefragAlloc)(RedisModuleDefragCtx *ctx, void *ptr) REDISMODULE_ATTR;
REDISMODULE_API void *(*RedisModule_DefragAllocRaw)(RedisModuleDefragCtx *ctx, size_t size) REDISMODULE_ATTR;
REDISMODULE_API void (*RedisModule_DefragFreeRaw)(RedisModuleDefragCtx *ctx, void *ptr) REDISMODULE_ATTR;
REDISMODULE_API RedisModuleString *(*RedisModule_DefragRedisModuleString)(RedisModuleDefragCtx *ctx, RedisModuleString *str) REDISMODULE_ATTR;
REDISMODULE_API int (*RedisModule_DefragShouldStop)(RedisModuleDefragCtx *ctx) REDISMODULE_ATTR;
REDISMODULE_API int (*RedisModule_DefragCursorSet)(RedisModuleDefragCtx *ctx, unsigned long cursor) REDISMODULE_ATTR;
Expand Down Expand Up @@ -1663,6 +1665,8 @@ static int RedisModule_Init(RedisModuleCtx *ctx, const char *name, int ver, int
REDISMODULE_GET_API(GetCurrentCommandName);
REDISMODULE_GET_API(RegisterDefragFunc);
REDISMODULE_GET_API(DefragAlloc);
REDISMODULE_GET_API(DefragAllocRaw);
REDISMODULE_GET_API(DefragFreeRaw);
REDISMODULE_GET_API(DefragRedisModuleString);
REDISMODULE_GET_API(DefragShouldStop);
REDISMODULE_GET_API(DefragCursorSet);
Expand Down
2 changes: 2 additions & 0 deletions src/server.h
Original file line number Diff line number Diff line change
Expand Up @@ -3127,6 +3127,8 @@ void checkChildrenDone(void);
int setOOMScoreAdj(int process_class);
void rejectCommandFormat(client *c, const char *fmt, ...);
void *activeDefragAlloc(void *ptr);
void *activeDefragAllocRaw(size_t size);
void activeDefragFreeRaw(void *ptr);
robj *activeDefragStringOb(robj* ob);
void dismissSds(sds s);
void dismissMemory(void* ptr, size_t size_hint);
Expand Down
12 changes: 12 additions & 0 deletions tests/modules/defragtest.c
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

#include "redismodule.h"
#include <stdlib.h>
#include <string.h>

static RedisModuleType *FragType;

Expand All @@ -17,6 +18,7 @@ unsigned long int last_set_cursor = 0;

unsigned long int datatype_attempts = 0;
unsigned long int datatype_defragged = 0;
unsigned long int datatype_raw_defragged = 0;
unsigned long int datatype_resumes = 0;
unsigned long int datatype_wrong_cursor = 0;
unsigned long int global_attempts = 0;
Expand Down Expand Up @@ -53,6 +55,7 @@ static void FragInfo(RedisModuleInfoCtx *ctx, int for_crash_report) {
RedisModule_InfoAddSection(ctx, "stats");
RedisModule_InfoAddFieldLongLong(ctx, "datatype_attempts", datatype_attempts);
RedisModule_InfoAddFieldLongLong(ctx, "datatype_defragged", datatype_defragged);
RedisModule_InfoAddFieldLongLong(ctx, "datatype_raw_defragged", datatype_raw_defragged);
RedisModule_InfoAddFieldLongLong(ctx, "datatype_resumes", datatype_resumes);
RedisModule_InfoAddFieldLongLong(ctx, "datatype_wrong_cursor", datatype_wrong_cursor);
RedisModule_InfoAddFieldLongLong(ctx, "global_attempts", global_attempts);
Expand All @@ -79,6 +82,7 @@ static int fragResetStatsCommand(RedisModuleCtx *ctx, RedisModuleString **argv,

datatype_attempts = 0;
datatype_defragged = 0;
datatype_raw_defragged = 0;
datatype_resumes = 0;
datatype_wrong_cursor = 0;
global_attempts = 0;
Expand Down Expand Up @@ -188,6 +192,14 @@ int FragDefrag(RedisModuleDefragCtx *ctx, RedisModuleString *key, void **value)
}
}

/* Defrag the values array itself using RedisModule_DefragAllocRaw
* and RedisModule_DefragFreeRaw for testing purposes. */
void *new_values = RedisModule_DefragAllocRaw(ctx, o->len * sizeof(void*));
memcpy(new_values, o->values, o->len * sizeof(void*));
RedisModule_DefragFreeRaw(ctx, o->values);
o->values = new_values;
datatype_raw_defragged++;

last_set_cursor = 0;
return 0;
}
Expand Down
2 changes: 2 additions & 0 deletions tests/unit/moduleapi/defrag.tcl
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ start_server {tags {"modules"} overrides {{save ""}}} {
set info [r info defragtest_stats]
assert {[getInfoProperty $info defragtest_datatype_attempts] > 0}
assert_equal 0 [getInfoProperty $info defragtest_datatype_resumes]
assert_morethan [getInfoProperty $info defragtest_datatype_raw_defragged] 0
}

test {Module defrag: late defrag with cursor works} {
Expand All @@ -32,6 +33,7 @@ start_server {tags {"modules"} overrides {{save ""}}} {
set info [r info defragtest_stats]
assert {[getInfoProperty $info defragtest_datatype_resumes] > 10}
assert_equal 0 [getInfoProperty $info defragtest_datatype_wrong_cursor]
assert_morethan [getInfoProperty $info defragtest_datatype_raw_defragged] 0
}

test {Module defrag: global defrag works} {
Expand Down

0 comments on commit 21ad53f

Please sign in to comment.