diff options
author | uriyage <78144248+uriyage@users.noreply.github.com> | 2023-02-28 19:38:58 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2023-02-28 19:38:58 +0200 |
commit | 9d336ac398d3a28f6e5e76f1b3b110bab949bcc5 (patch) | |
tree | 0925b307d0459a816ae002a0e569a7c80110675f /tests | |
parent | b1939b052adc058bd814045a745ec02d3f791d7b (diff) | |
download | redis-9d336ac398d3a28f6e5e76f1b3b110bab949bcc5.tar.gz |
Try to trim strings only when applicable (#11817)
As `sdsRemoveFreeSpace` have an impact on performance even if it is a no-op (see details at #11508).
Only call the function when there is a possibility that the string contains free space.
* For strings coming from the network, it's only if they're bigger than PROTO_MBULK_BIG_ARG
* For strings coming from scripts, it's only if they're smaller than LUA_CMD_OBJCACHE_MAX_LEN
* For strings coming from modules, it could be anything.
Co-authored-by: Oran Agra <oran@redislabs.com>
Co-authored-by: sundb <sundbcn@gmail.com>
Diffstat (limited to 'tests')
-rw-r--r-- | tests/modules/basics.c | 27 | ||||
-rw-r--r-- | tests/unit/moduleapi/misc.tcl | 9 | ||||
-rw-r--r-- | tests/unit/scripting.tcl | 27 |
3 files changed, 62 insertions, 1 deletions
diff --git a/tests/modules/basics.c b/tests/modules/basics.c index b6f9766c6..579d8f629 100644 --- a/tests/modules/basics.c +++ b/tests/modules/basics.c @@ -407,6 +407,26 @@ int TestStringAppendAM(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) return REDISMODULE_OK; } +/* TEST.STRING.TRIM -- Test we trim a string with free space. */ +int TestTrimString(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { + REDISMODULE_NOT_USED(argv); + REDISMODULE_NOT_USED(argc); + RedisModuleString *s = RedisModule_CreateString(ctx,"foo",3); + char *tmp = RedisModule_Alloc(1024); + RedisModule_StringAppendBuffer(ctx,s,tmp,1024); + size_t string_len = RedisModule_MallocSizeString(s); + RedisModule_TrimStringAllocation(s); + size_t len_after_trim = RedisModule_MallocSizeString(s); + if (len_after_trim < string_len) { + RedisModule_ReplyWithSimpleString(ctx, "OK"); + } else { + RedisModule_ReplyWithError(ctx, "String was not trimmed as expected."); + } + RedisModule_Free(tmp); + RedisModule_FreeString(ctx,s); + return REDISMODULE_OK; +} + /* TEST.STRING.PRINTF -- Test string formatting. */ int TestStringPrintf(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { RedisModule_AutoMemory(ctx); @@ -852,6 +872,9 @@ int TestBasics(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { T("test.string.append.am",""); if (!TestAssertStringReply(ctx,reply,"foobar",6)) goto fail; + + T("test.string.trim",""); + if (!TestAssertStringReply(ctx,reply,"OK",2)) goto fail; T("test.string.printf", "cc", "foo", "bar"); if (!TestAssertStringReply(ctx,reply,"Got 3 args. argv[1]: foo, argv[2]: bar",38)) goto fail; @@ -963,6 +986,10 @@ int RedisModule_OnLoad(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) TestStringAppend,"write deny-oom",1,1,1) == REDISMODULE_ERR) return REDISMODULE_ERR; + if (RedisModule_CreateCommand(ctx,"test.string.trim", + TestTrimString,"write deny-oom",1,1,1) == REDISMODULE_ERR) + return REDISMODULE_ERR; + if (RedisModule_CreateCommand(ctx,"test.string.append.am", TestStringAppendAM,"write deny-oom",1,1,1) == REDISMODULE_ERR) return REDISMODULE_ERR; diff --git a/tests/unit/moduleapi/misc.tcl b/tests/unit/moduleapi/misc.tcl index 8d63ce5b6..4bf0d58ea 100644 --- a/tests/unit/moduleapi/misc.tcl +++ b/tests/unit/moduleapi/misc.tcl @@ -525,6 +525,15 @@ start_server {tags {"modules"}} { assert_equal {0} [r test.get_n_events] } + test {test RM_Call with large arg for SET command} { + # set a big value to trigger increasing the query buf + r set foo [string repeat A 100000] + # set a smaller value but > PROTO_MBULK_BIG_ARG (32*1024) Redis will try to save the query buf itself on the DB. + r test.call_generic set bar [string repeat A 33000] + # asset the value was trimmed + assert {[r memory usage bar] < 42000}; # 42K to count for Jemalloc's additional memory overhead. + } + test "Unload the module - misc" { assert_equal {OK} [r module unload misc] } diff --git a/tests/unit/scripting.tcl b/tests/unit/scripting.tcl index 440b9e2e1..e3b1a620b 100644 --- a/tests/unit/scripting.tcl +++ b/tests/unit/scripting.tcl @@ -1995,5 +1995,30 @@ start_server {tags {"scripting"}} { } 0 ] {asdf} } -} + test "LUA test trim string as expected" { + # this test may fail if we use different memory allocator than jemalloc, as libc for example may keep the old size on realloc. + if {[string match {*jemalloc*} [s mem_allocator]]} { + # test that when using LUA cache mechanism, if there is free space in the argv array, the string is trimmed. + r set foo [string repeat "a" 45] + set expected_memory [r memory usage foo] + + # Jemalloc will allocate for the requested 63 bytes, 80 bytes. + # We can't test for larger sizes because LUA_CMD_OBJCACHE_MAX_LEN is 64. + # This value will be recycled to be used in the next argument. + # We use SETNX to avoid saving the string which will prevent us to reuse it in the next command. + r eval { + return redis.call("SETNX", "foo", string.rep("a", 63)) + } 0 + + # Jemalloc will allocate for the request 45 bytes, 56 bytes. + # we can't test for smaller sizes because OBJ_ENCODING_EMBSTR_SIZE_LIMIT is 44 where no trim is done. + r eval { + return redis.call("SET", "foo", string.rep("a", 45)) + } 0 + + # Assert the string has been trimmed and the 80 bytes from the previous alloc were not kept. + assert { [r memory usage foo] <= $expected_memory}; + } + } +} |