Merge 3e2f561f60191335df351a94c707ffb09f74462b into 26c6f1af9b29d525831c7fa9840ab3e47ed7b700
This commit is contained in:
commit
79eee406df
42
src/config.c
42
src/config.c
@ -2494,6 +2494,42 @@ static int updateReplBacklogSize(const char **err) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int updateKeyEvictionMemory(const char **err) {
|
||||
UNUSED(err);
|
||||
if (server.maxmemory) {
|
||||
if (!server.key_eviction_memory) {
|
||||
serverLog(LL_WARNING,
|
||||
"WARNING: current maxmemory value is not 0, the new key-eviction-memory value set via CONFIG SET (%llu) is "
|
||||
"0. The new key-eviction-memory value is set to equal to current maxmemory (%llu) ",
|
||||
server.key_eviction_memory, server.maxmemory);
|
||||
server.key_eviction_memory = server.maxmemory;
|
||||
} else if (server.key_eviction_memory > server.maxmemory) {
|
||||
serverLog(LL_WARNING,
|
||||
"WARNING: the new key-eviction-memory value set via CONFIG SET (%llu) is greater than current maxmemory, "
|
||||
"The new key-eviction-memory value is set to equal to current maxmemory (%llu) ",
|
||||
server.key_eviction_memory, server.maxmemory);
|
||||
server.key_eviction_memory = server.maxmemory;
|
||||
}
|
||||
size_t used = zmalloc_used_memory() - freeMemoryGetNotCountedMemory();
|
||||
if (server.key_eviction_memory < used) {
|
||||
serverLog(LL_WARNING,
|
||||
"WARNING: the new key-eviction-memorym value set via CONFIG SET (%llu) is smaller than the current memory "
|
||||
"usage (%zu). This will result in key eviction and/or the inability to accept new write commands "
|
||||
"depending on the maxmemory-policy.",
|
||||
server.key_eviction_memory, used);
|
||||
}
|
||||
} else {
|
||||
if (server.key_eviction_memory) {
|
||||
serverLog(LL_WARNING,
|
||||
"WARNING: current maxmemory value is 0, the new key-eviction-memory value set via CONFIG SET (%llu) is "
|
||||
"greater than 0. The new key-eviction-memory value is invalid, and its value is set to 0 ",
|
||||
server.key_eviction_memory);
|
||||
}
|
||||
server.key_eviction_memory = 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int updateMaxmemory(const char **err) {
|
||||
UNUSED(err);
|
||||
if (server.maxmemory) {
|
||||
@ -2505,7 +2541,12 @@ static int updateMaxmemory(const char **err) {
|
||||
"depending on the maxmemory-policy.",
|
||||
server.maxmemory, used);
|
||||
}
|
||||
if (!server.key_eviction_memory || server.key_eviction_memory > server.maxmemory) {
|
||||
server.key_eviction_memory = server.maxmemory;
|
||||
}
|
||||
startEvictionTimeProc();
|
||||
} else {
|
||||
server.key_eviction_memory = 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
@ -3327,6 +3368,7 @@ standardConfig static_configs[] = {
|
||||
|
||||
/* Unsigned Long Long configs */
|
||||
createULongLongConfig("maxmemory", NULL, MODIFIABLE_CONFIG, 0, ULLONG_MAX, server.maxmemory, 0, MEMORY_CONFIG, NULL, updateMaxmemory),
|
||||
createULongLongConfig("key-eviction-memory", NULL, MODIFIABLE_CONFIG, 0, ULLONG_MAX, server.key_eviction_memory, 0, MEMORY_CONFIG, NULL, updateKeyEvictionMemory),
|
||||
createULongLongConfig("cluster-link-sendbuf-limit", NULL, MODIFIABLE_CONFIG, 0, ULLONG_MAX, server.cluster_link_msg_queue_limit_bytes, 0, MEMORY_CONFIG, NULL, NULL),
|
||||
|
||||
/* Size_t configs */
|
||||
|
44
src/evict.c
44
src/evict.c
@ -377,7 +377,7 @@ size_t freeMemoryGetNotCountedMemory(void) {
|
||||
* limit.
|
||||
* (Populated both for C_ERR and C_OK)
|
||||
*/
|
||||
int getMaxmemoryState(size_t *total, size_t *logical, size_t *tofree, float *level) {
|
||||
int getMaxmemoryState(size_t *total, size_t *logical, size_t *tofree, float *level, unsigned long long maxmemory) {
|
||||
size_t mem_reported, mem_used, mem_tofree;
|
||||
|
||||
/* Check if we are over the memory usage limit. If we are not, no need
|
||||
@ -386,11 +386,12 @@ int getMaxmemoryState(size_t *total, size_t *logical, size_t *tofree, float *lev
|
||||
if (total) *total = mem_reported;
|
||||
|
||||
/* We may return ASAP if there is no need to compute the level. */
|
||||
if (!server.maxmemory) {
|
||||
if (!maxmemory) {
|
||||
if (level) *level = 0;
|
||||
return C_OK;
|
||||
}
|
||||
if (mem_reported <= server.maxmemory && !level) return C_OK;
|
||||
|
||||
if (mem_reported <= maxmemory && !level) return C_OK;
|
||||
|
||||
/* Remove the size of replicas output buffers and AOF buffer from the
|
||||
* count of used memory. */
|
||||
@ -399,15 +400,19 @@ int getMaxmemoryState(size_t *total, size_t *logical, size_t *tofree, float *lev
|
||||
mem_used = (mem_used > overhead) ? mem_used - overhead : 0;
|
||||
|
||||
/* Compute the ratio of memory usage. */
|
||||
if (level) *level = (float)mem_used / (float)server.maxmemory;
|
||||
if (level) {
|
||||
*level = (float)mem_used / (float)server.maxmemory;
|
||||
}
|
||||
|
||||
if (mem_reported <= server.maxmemory) return C_OK;
|
||||
if (mem_reported <= maxmemory) return C_OK;
|
||||
|
||||
/* Check if we are still over the memory limit. */
|
||||
if (mem_used <= server.maxmemory) return C_OK;
|
||||
/* if function parameter 'maxmemory' is equal to maxmemory and mem_used > maxmemory then OOM /
|
||||
/ if function parameter 'maxmemory' is equal to maxmemory_soft and mem_used > function parameter 'maxmemory' then there is no OOM but eviction happens */
|
||||
if (mem_used <= maxmemory) return C_OK;
|
||||
|
||||
/* Compute how much memory we need to free. */
|
||||
mem_tofree = mem_used - server.maxmemory;
|
||||
mem_tofree = mem_used - server.key_eviction_memory;
|
||||
|
||||
if (logical) *logical = mem_used;
|
||||
if (tofree) *tofree = mem_tofree;
|
||||
@ -522,20 +527,24 @@ int performEvictions(void) {
|
||||
if (!isSafeToPerformEvictions()) return EVICT_OK;
|
||||
|
||||
int keys_freed = 0;
|
||||
size_t mem_reported, mem_tofree;
|
||||
size_t mem_reported, mem_tofree, mem_used;
|
||||
long long mem_freed = 0; /* Maybe become negative */
|
||||
mstime_t latency, eviction_latency;
|
||||
long long delta;
|
||||
int replicas = listLength(server.replicas);
|
||||
int result = EVICT_FAIL;
|
||||
|
||||
if (getMaxmemoryState(&mem_reported, NULL, &mem_tofree, NULL) == C_OK) {
|
||||
if (getMaxmemoryState(&mem_reported, &mem_used, &mem_tofree, NULL, server.key_eviction_memory) == C_OK) {
|
||||
result = EVICT_OK;
|
||||
goto update_metrics;
|
||||
}
|
||||
|
||||
if (server.maxmemory_policy == MAXMEMORY_NO_EVICTION || (iAmPrimary() && server.import_mode)) {
|
||||
result = EVICT_FAIL; /* We need to free memory, but policy forbids or we are in import mode. */
|
||||
if (mem_used >= server.maxmemory) {
|
||||
result = EVICT_FAIL; /* We need to free memory, but policy forbids or we are in import mode. */
|
||||
} else {
|
||||
result = EVICT_OK; /* used_memory greater than key_eviction_memory, but not reach OOM */
|
||||
}
|
||||
goto update_metrics;
|
||||
}
|
||||
|
||||
@ -697,7 +706,7 @@ int performEvictions(void) {
|
||||
* across the dbAsyncDelete() call, while the thread can
|
||||
* release the memory all the time. */
|
||||
if (server.lazyfree_lazy_eviction) {
|
||||
if (getMaxmemoryState(NULL, NULL, NULL, NULL) == C_OK) {
|
||||
if (getMaxmemoryState(NULL, NULL, NULL, NULL, server.key_eviction_memory) == C_OK) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -712,11 +721,16 @@ int performEvictions(void) {
|
||||
}
|
||||
}
|
||||
} else {
|
||||
goto cant_free; /* nothing to free... */
|
||||
break;
|
||||
}
|
||||
}
|
||||
/* at this point, the memory is OK, or we have reached the time limit */
|
||||
result = (isEvictionProcRunning) ? EVICT_RUNNING : EVICT_OK;
|
||||
|
||||
if (mem_freed >= (long long)(mem_used - server.key_eviction_memory)) {
|
||||
/* at this point, the memory is OK, or we have reached the time limit */
|
||||
result = (isEvictionProcRunning) ? EVICT_RUNNING : EVICT_OK;
|
||||
} else {
|
||||
goto cant_free;
|
||||
}
|
||||
|
||||
cant_free:
|
||||
if (result == EVICT_FAIL) {
|
||||
@ -726,7 +740,7 @@ cant_free:
|
||||
mstime_t lazyfree_latency;
|
||||
latencyStartMonitor(lazyfree_latency);
|
||||
while (bioPendingJobsOfType(BIO_LAZY_FREE) && elapsedUs(evictionTimer) < eviction_time_limit_us) {
|
||||
if (getMaxmemoryState(NULL, NULL, NULL, NULL) == C_OK) {
|
||||
if (getMaxmemoryState(NULL, NULL, NULL, NULL, server.key_eviction_memory) == C_OK) {
|
||||
result = EVICT_OK;
|
||||
break;
|
||||
}
|
||||
|
@ -4006,7 +4006,7 @@ int VM_GetContextFlags(ValkeyModuleCtx *ctx) {
|
||||
|
||||
/* OOM flag. */
|
||||
float level;
|
||||
int retval = getMaxmemoryState(NULL, NULL, NULL, &level);
|
||||
int retval = getMaxmemoryState(NULL, NULL, NULL, &level, server.maxmemory);
|
||||
if (retval == C_ERR) flags |= VALKEYMODULE_CTX_FLAGS_OOM;
|
||||
if (level > 0.75) flags |= VALKEYMODULE_CTX_FLAGS_OOM_WARNING;
|
||||
|
||||
@ -6404,7 +6404,7 @@ ValkeyModuleCallReply *VM_Call(ValkeyModuleCtx *ctx, const char *cmdname, const
|
||||
/* On background thread we can not count on server.pre_command_oom_state.
|
||||
* Because it is only set on the main thread, in such case we will check
|
||||
* the actual memory usage. */
|
||||
oom_state = (getMaxmemoryState(NULL, NULL, NULL, NULL) == C_ERR);
|
||||
oom_state = (getMaxmemoryState(NULL, NULL, NULL, NULL, server.maxmemory) == C_ERR);
|
||||
} else {
|
||||
oom_state = server.pre_command_oom_state;
|
||||
}
|
||||
@ -10952,7 +10952,7 @@ size_t VM_MallocSizeDict(ValkeyModuleDict *dict) {
|
||||
*/
|
||||
float VM_GetUsedMemoryRatio(void) {
|
||||
float level;
|
||||
getMaxmemoryState(NULL, NULL, NULL, &level);
|
||||
getMaxmemoryState(NULL, NULL, NULL, &level, server.maxmemory);
|
||||
return level;
|
||||
}
|
||||
|
||||
|
12
src/server.c
12
src/server.c
@ -2774,6 +2774,14 @@ void initServer(void) {
|
||||
server.client_mem_usage_buckets = NULL;
|
||||
resetReplicationBuffer();
|
||||
|
||||
if (server.maxmemory) {
|
||||
if (!server.key_eviction_memory || server.key_eviction_memory > server.maxmemory) {
|
||||
server.key_eviction_memory = server.maxmemory;
|
||||
}
|
||||
} else {
|
||||
server.key_eviction_memory = 0;
|
||||
}
|
||||
|
||||
/* Make sure the locale is set on startup based on the config file. */
|
||||
if (setlocale(LC_COLLATE, server.locale_collate) == NULL) {
|
||||
serverLog(LL_WARNING, "Failed to configure LOCALE for invalid locale name.");
|
||||
@ -5705,6 +5713,7 @@ sds genValkeyInfoString(dict *section_dict, int all_sections, int everything) {
|
||||
char used_memory_scripts_hmem[64];
|
||||
char used_memory_rss_hmem[64];
|
||||
char maxmemory_hmem[64];
|
||||
char key_eviction_memory_hmem[64];
|
||||
size_t zmalloc_used = zmalloc_used_memory();
|
||||
size_t total_system_mem = server.system_memory_size;
|
||||
const char *evict_policy = evictPolicyToString();
|
||||
@ -5726,6 +5735,7 @@ sds genValkeyInfoString(dict *section_dict, int all_sections, int everything) {
|
||||
bytesToHuman(used_memory_scripts_hmem, sizeof(used_memory_scripts_hmem), mh->lua_caches + mh->functions_caches);
|
||||
bytesToHuman(used_memory_rss_hmem, sizeof(used_memory_rss_hmem), server.cron_malloc_stats.process_rss);
|
||||
bytesToHuman(maxmemory_hmem, sizeof(maxmemory_hmem), server.maxmemory);
|
||||
bytesToHuman(key_eviction_memory_hmem, sizeof(key_eviction_memory_hmem), server.key_eviction_memory);
|
||||
|
||||
if (sections++) info = sdscat(info, "\r\n");
|
||||
info = sdscatprintf(
|
||||
@ -5764,6 +5774,8 @@ sds genValkeyInfoString(dict *section_dict, int all_sections, int everything) {
|
||||
"maxmemory:%lld\r\n", server.maxmemory,
|
||||
"maxmemory_human:%s\r\n", maxmemory_hmem,
|
||||
"maxmemory_policy:%s\r\n", evict_policy,
|
||||
"key_eviction_memory:%lld\r\n", server.key_eviction_memory,
|
||||
"key_eviction_memory_human:%s\r\n", key_eviction_memory_hmem,
|
||||
"allocator_frag_ratio:%.2f\r\n", mh->allocator_frag,
|
||||
"allocator_frag_bytes:%zu\r\n", mh->allocator_frag_bytes,
|
||||
"allocator_rss_ratio:%.2f\r\n", mh->allocator_rss,
|
||||
|
@ -1991,6 +1991,7 @@ struct valkeyServer {
|
||||
/* Limits */
|
||||
unsigned int maxclients; /* Max number of simultaneous clients */
|
||||
unsigned long long maxmemory; /* Max number of memory bytes to use */
|
||||
unsigned long long key_eviction_memory; /* Memory bytes to begin the key eviction process */
|
||||
ssize_t maxmemory_clients; /* Memory limit for total client buffers */
|
||||
int maxmemory_policy; /* Policy for key eviction */
|
||||
int maxmemory_samples; /* Precision of random sampling */
|
||||
@ -3138,7 +3139,7 @@ int zslLexValueGteMin(sds value, zlexrangespec *spec);
|
||||
int zslLexValueLteMax(sds value, zlexrangespec *spec);
|
||||
|
||||
/* Core functions */
|
||||
int getMaxmemoryState(size_t *total, size_t *logical, size_t *tofree, float *level);
|
||||
int getMaxmemoryState(size_t *total, size_t *logical, size_t *tofree, float *level, unsigned long long maxmemory);
|
||||
size_t freeMemoryGetNotCountedMemory(void);
|
||||
int overMaxmemoryAfterAlloc(size_t moremem);
|
||||
uint64_t getCommandFlags(client *c);
|
||||
|
@ -174,6 +174,7 @@ start_server {tags {"maxmemory external:skip"}} {
|
||||
r setex [randomKey] 10000 x
|
||||
}
|
||||
assert {[s used_memory] < ($limit+4096)}
|
||||
r config set maxmemory 0
|
||||
}
|
||||
}
|
||||
|
||||
@ -216,6 +217,7 @@ start_server {tags {"maxmemory external:skip"}} {
|
||||
} else {
|
||||
assert {$err == 1}
|
||||
}
|
||||
r config set maxmemory 0
|
||||
}
|
||||
}
|
||||
|
||||
@ -261,10 +263,91 @@ start_server {tags {"maxmemory external:skip"}} {
|
||||
for {set j 0} {$j < $numkeys} {incr j 2} {
|
||||
assert {[r exists "key:$j"]}
|
||||
}
|
||||
r config set maxmemory 0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
test "key-eviction-memory could change with maxmemory update" {
|
||||
# make sure to start with a blank instance
|
||||
r flushall
|
||||
# we set maxmemory as 0, and we expect key-eviction-memory as 0 too.
|
||||
r config set maxmemory 0
|
||||
assert_equal 0 [lindex [r config get maxmemory] 1]
|
||||
assert_equal 0 [lindex [r config get key-eviction-memory] 1]
|
||||
# we increase maxmemory, and we expect key-eviction-memory value is increased too.
|
||||
r config set maxmemory 10mb
|
||||
set key_eviction_memory [lindex [r config get key-eviction-memory] 1]
|
||||
assert_equal [lindex [r config get maxmemory] 1] $key_eviction_memory
|
||||
# we increase maxmemory a little bit, but we expect key-eviction-memory value no change.
|
||||
r config set maxmemory 11mb
|
||||
assert_equal $key_eviction_memory [lindex [r config get key-eviction-memory] 1]
|
||||
assert_morethan [lindex [r config get maxmemory] 1] [lindex [r config get key-eviction-memory] 1]
|
||||
# we decrease maxmemory lower than key-eviction-memory, thus we expect key-eviction-memory value decrease to maxmemory value.
|
||||
r config set maxmemory 6mb
|
||||
assert_equal [lindex [r config get maxmemory] 1] [lindex [r config get key-eviction-memory] 1]
|
||||
# we decrease maxmemory to 0, and we expect key-eviction-memory value decrease to 0 too.
|
||||
r config set maxmemory 0
|
||||
assert_equal 0 [lindex [r config get maxmemory] 1]
|
||||
assert_equal 0 [lindex [r config get key-eviction-memory] 1]
|
||||
}
|
||||
|
||||
test "key-eviction-memory update test" {
|
||||
# make sure to start with a blank instance
|
||||
r flushall
|
||||
# If maxmemory is not 0, and key-eviction-memory is set to 0, we expect the key-eviction-memory equal to maxmemory
|
||||
r config set maxmemory 10mb
|
||||
r config set key-eviction-memory 0
|
||||
assert_equal [lindex [r config get maxmemory] 1] [lindex [r config get key-eviction-memory] 1]
|
||||
# we increase key-eviction-memory, but its value is still less than maxmemory.
|
||||
r config set key-eviction-memory 5mb
|
||||
assert_morethan [lindex [r config get maxmemory] 1] [lindex [r config get key-eviction-memory] 1]
|
||||
# we increase key-eviction-memory more than maxmemory, but we expect its value is equal to maxmemory.
|
||||
r config set key-eviction-memory 10mb
|
||||
assert_equal [lindex [r config get maxmemory] 1] [lindex [r config get key-eviction-memory] 1]
|
||||
r config set maxmemory 0
|
||||
}
|
||||
|
||||
|
||||
foreach policy {
|
||||
allkeys-random allkeys-lru allkeys-lfu volatile-lru volatile-lfu volatile-random volatile-ttl
|
||||
} {
|
||||
test "key-eviction-memory - test eviction key number with policy ($policy) and different key-eviction-memory value" {
|
||||
r flushall
|
||||
# make sure to start with a blank instance
|
||||
set num_eviction_key_init [s evicted_keys]
|
||||
set used 1134728
|
||||
set limit_maxmemory [expr {$used+100*1024}]
|
||||
set limit_key_eviction_memory_threshold1 [expr {$used+70*1024}]
|
||||
set limit_key_eviction_memory_threshold2 [expr {$used+40*1024}]
|
||||
r config set maxmemory $limit_maxmemory
|
||||
r config set maxmemory-policy $policy
|
||||
set numkeys 5000
|
||||
for {set j 0} {$j < $numkeys} {incr j} {
|
||||
catch {r set $j $j EX 10000}
|
||||
}
|
||||
set num_eviction_key_maxmemory [s evicted_keys]
|
||||
set diff_num_key_eviction_one [expr {$num_eviction_key_maxmemory - $num_eviction_key_init}]
|
||||
r flushall
|
||||
r config set key-eviction-memory $limit_key_eviction_memory_threshold1
|
||||
for {set j 0} {$j < $numkeys} {incr j} {
|
||||
catch {r set $j $j EX 10000}
|
||||
}
|
||||
set num_eviction_key_threshold1 [s evicted_keys]
|
||||
set diff_num_key_eviction_two [expr {$num_eviction_key_threshold1 - $num_eviction_key_maxmemory}]
|
||||
r flushall
|
||||
r config set key-eviction-memory $limit_key_eviction_memory_threshold2
|
||||
for {set j 0} {$j < $numkeys} {incr j} {
|
||||
catch {r set $j $j EX 10000}
|
||||
}
|
||||
set num_eviction_key_threshold2 [s evicted_keys]
|
||||
set diff_num_key_eviction_three [expr {$num_eviction_key_threshold2 - $num_eviction_key_threshold1}]
|
||||
assert_morethan $diff_num_key_eviction_three $diff_num_key_eviction_two
|
||||
assert_morethan $diff_num_key_eviction_two $diff_num_key_eviction_one
|
||||
r flushall
|
||||
r config set maxmemory 0
|
||||
}
|
||||
}
|
||||
}
|
||||
# Calculate query buffer memory of slave
|
||||
proc slave_query_buffer {srv} {
|
||||
set clients [split [$srv client list] "\r\n"]
|
||||
|
@ -66,6 +66,7 @@ run_solo {defrag} {
|
||||
r config set latency-monitor-threshold 5
|
||||
r latency reset
|
||||
r config set maxmemory 110mb ;# prevent further eviction (not to fail the digest test)
|
||||
r config set key-eviction-memory 110mb
|
||||
set digest [debug_digest]
|
||||
catch {r config set activedefrag yes} e
|
||||
if {[r config get activedefrag] eq "activedefrag yes"} {
|
||||
|
@ -1279,6 +1279,12 @@ acllog-max-len 128
|
||||
#
|
||||
# maxmemory-policy noeviction
|
||||
|
||||
# `key-eviction-memory` defines a "soft" maxmemory threshold as the `maxmemory` limit.
|
||||
# When memory usage exceeds this key-eviction-memory, Valkey begins proactive key eviction. However, exceeding this
|
||||
# threshold does not immediately reject new write commands; only the hard `maxmemory` limit will do so.
|
||||
#
|
||||
# key-eviction-memory <bytes>
|
||||
|
||||
# LRU, LFU and minimal TTL algorithms are not precise algorithms but approximated
|
||||
# algorithms (in order to save memory), so you can tune it for speed or
|
||||
# accuracy. By default the server will check five keys and pick the one that was
|
||||
|
Loading…
x
Reference in New Issue
Block a user