diff --git a/src/config.c b/src/config.c index 2d4e703d95..6d967954a2 100644 --- a/src/config.c +++ b/src/config.c @@ -2471,6 +2471,19 @@ static int updateReplBacklogSize(const char **err) { return 1; } +static int updateMaxmemoryReserved(const char **err) { + UNUSED(err); + if (server.maxmemory_reserved_scale) { + if (server.maxmemory_reserved_scale < 10) { + server.maxmemory_reserved_scale = 10; + } else if (server.maxmemory_reserved_scale > 60) { + server.maxmemory_reserved_scale = 60; + } + } + server.maxmemory_available = (unsigned long long)server.maxmemory / 100.0 * (100 - server.maxmemory_reserved_scale); + return 1; +} + static int updateMaxmemory(const char **err) { UNUSED(err); if (server.maxmemory) { @@ -2482,6 +2495,8 @@ static int updateMaxmemory(const char **err) { "depending on the maxmemory-policy.", server.maxmemory, used); } + server.maxmemory_available = + (unsigned long long)server.maxmemory / 100.0 * (100 - server.maxmemory_reserved_scale); startEvictionTimeProc(); } return 1; @@ -3181,6 +3196,7 @@ standardConfig static_configs[] = { createIntConfig("lfu-decay-time", NULL, MODIFIABLE_CONFIG, 0, INT_MAX, server.lfu_decay_time, 1, INTEGER_CONFIG, NULL, NULL), createIntConfig("replica-priority", "slave-priority", MODIFIABLE_CONFIG, 0, INT_MAX, server.replica_priority, 100, INTEGER_CONFIG, NULL, NULL), createIntConfig("repl-diskless-sync-delay", NULL, MODIFIABLE_CONFIG, 0, INT_MAX, server.repl_diskless_sync_delay, 5, INTEGER_CONFIG, NULL, NULL), + createIntConfig("maxmemory-reserved-scale", NULL, MODIFIABLE_CONFIG, 0, 100, server.maxmemory_reserved_scale, 0, INTEGER_CONFIG, NULL, updateMaxmemoryReserved), createIntConfig("maxmemory-samples", NULL, MODIFIABLE_CONFIG, 1, 64, server.maxmemory_samples, 5, INTEGER_CONFIG, NULL, NULL), createIntConfig("maxmemory-eviction-tenacity", NULL, MODIFIABLE_CONFIG, 0, 100, server.maxmemory_eviction_tenacity, 10, INTEGER_CONFIG, NULL, NULL), createIntConfig("timeout", NULL, MODIFIABLE_CONFIG, 0, INT_MAX, server.maxidletime, 0, INTEGER_CONFIG, NULL, NULL), /* Default client timeout: infinite */ diff --git a/src/evict.c b/src/evict.c index 4b9f70eaa5..32fbf1a947 100644 --- a/src/evict.c +++ b/src/evict.c @@ -402,7 +402,11 @@ int getMaxmemoryState(size_t *total, size_t *logical, size_t *tofree, float *lev if (level) *level = 0; return C_OK; } - if (mem_reported <= server.maxmemory && !level) return C_OK; + + if (server.maxmemory_reserved_scale) { + if (mem_reported <= server.maxmemory_available && !level) return C_OK; + } else if (mem_reported <= server.maxmemory && !level) + return C_OK; /* Remove the size of replicas output buffers and AOF buffer from the * count of used memory. */ @@ -411,15 +415,29 @@ 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) { + if (server.maxmemory_reserved_scale) + *level = (float)mem_used / (float)server.maxmemory_available; + else + *level = (float)mem_used / (float)server.maxmemory; + } - if (mem_reported <= server.maxmemory) return C_OK; + if (server.maxmemory_reserved_scale) { + if (mem_reported <= server.maxmemory_available) return C_OK; + } else if (mem_reported <= server.maxmemory) + return C_OK; /* Check if we are still over the memory limit. */ - if (mem_used <= server.maxmemory) return C_OK; + if (server.maxmemory_reserved_scale) { + if (mem_used <= server.maxmemory_available) return C_OK; + } else if (mem_used <= server.maxmemory) + return C_OK; /* Compute how much memory we need to free. */ - mem_tofree = mem_used - server.maxmemory; + if (server.maxmemory_reserved_scale) { + mem_tofree = mem_used - server.maxmemory_available; + } else + mem_tofree = mem_used - server.maxmemory; if (logical) *logical = mem_used; if (tofree) *tofree = mem_tofree; diff --git a/src/server.c b/src/server.c index 6a2d46d974..566f34e953 100644 --- a/src/server.c +++ b/src/server.c @@ -2621,6 +2621,11 @@ void initServer(void) { server.client_mem_usage_buckets = NULL; resetReplicationBuffer(); + if (server.maxmemory) { + server.maxmemory_available = + (unsigned long long)server.maxmemory / 100.0 * (100 - server.maxmemory_reserved_scale); + } + /* 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."); @@ -5512,6 +5517,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 maxmemory_available_hmem[64]; size_t zmalloc_used = zmalloc_used_memory(); size_t total_system_mem = server.system_memory_size; const char *evict_policy = evictPolicyToString(); @@ -5533,6 +5539,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(maxmemory_available_hmem, sizeof(maxmemory_available_hmem), server.maxmemory_available); if (sections++) info = sdscat(info, "\r\n"); /* clang-format off */ @@ -5570,6 +5577,9 @@ 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, + "maxmemory_reserved_scale:%d\r\n",server.maxmemory_reserved_scale, + "maxmemory_available:%lld\r\n",server.maxmemory_available, + "maxmemory_available_human:%s\r\n",maxmemory_available_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, diff --git a/src/server.h b/src/server.h index a5cee03055..ef7c927a14 100644 --- a/src/server.h +++ b/src/server.h @@ -2098,6 +2098,8 @@ struct valkeyServer { ssize_t maxmemory_clients; /* Memory limit for total client buffers */ int maxmemory_policy; /* Policy for key eviction */ int maxmemory_samples; /* Precision of random sampling */ + int maxmemory_reserved_scale; + unsigned long long maxmemory_available; int maxmemory_eviction_tenacity; /* Aggressiveness of eviction processing */ int lfu_log_factor; /* LFU logarithmic counter factor. */ int lfu_decay_time; /* LFU counter decay factor. */ diff --git a/valkey.conf b/valkey.conf index f9d102a95d..8af492d205 100644 --- a/valkey.conf +++ b/valkey.conf @@ -1245,6 +1245,13 @@ acllog-max-len 128 # in the system. It's a tradeoff between memory, CPU and latency. # # active-expire-effort 1 +# +# It allows the valkey to evict keys earlier. The value of this parameter represents +# percent of the maxmemory value. It means how much memory the valkey instance want to hold +# not to store the data. +# Default is 0, and the value could be set between 10 to 60. +# +# maxmemory-reserved-scale 0 ############################# LAZY FREEING ####################################