From 0edd38e5dad3d99864f9da9b945f030aa2a6ba90 Mon Sep 17 00:00:00 2001 From: Yossi Gottlieb Date: Tue, 25 Dec 2012 09:36:08 +0200 Subject: [PATCH] Add minmemory-os configuration parameter. --- redis.conf | 6 ++++ src/config.c | 4 ++- src/redis.c | 91 ++++++++++++++++++++++++++++++++++++++++++++++--- src/redis.h | 3 +- src/scripting.c | 2 +- 5 files changed, 99 insertions(+), 7 deletions(-) mode change 100644 => 100755 src/scripting.c diff --git a/redis.conf b/redis.conf index 9e659eff5..e3531927c 100755 --- a/redis.conf +++ b/redis.conf @@ -322,6 +322,12 @@ slave-priority 100 # # maxmemory-samples 3 +# Define the minimum OS memory threshold allowed before eviction begins. This +# is checked perioedically and eviction will be attempted for 1/2 of the delta +# between the free memory and required spare. If 0 is specified, this is +# disabled. +# minmemory-os 0 + ############################## APPEND ONLY MODE ############################### # By default Redis asynchronously dumps the dataset on disk. This mode is diff --git a/src/config.c b/src/config.c index d25c89b2e..43c042698 100755 --- a/src/config.c +++ b/src/config.c @@ -197,6 +197,8 @@ void loadServerConfigFromString(char *config) { } } else if (!strcasecmp(argv[0],"maxmemory") && argc == 2) { server.maxmemory = memtoll(argv[1],NULL); + } else if (!strcasecmp(argv[0],"minmemory-os") && argc == 2) { + server.minmemory_os = memtoll(argv[1],NULL); } else if (!strcasecmp(argv[0],"maxmemory-policy") && argc == 2) { if (!strcasecmp(argv[1],"volatile-lru")) { server.maxmemory_policy = REDIS_MAXMEMORY_VOLATILE_LRU; @@ -493,7 +495,7 @@ void configSetCommand(redisClient *c) { if (server.maxmemory < zmalloc_used_memory()) { redisLog(REDIS_WARNING,"WARNING: the new maxmemory value set via CONFIG SET is smaller than the current memory usage. This will result in keys eviction and/or inability to accept new write commands depending on the maxmemory-policy."); } - freeMemoryIfNeeded(); + freeMemoryIfNeeded(server.maxmemory); } } else if (!strcasecmp(c->argv[2]->ptr,"maxmemory-policy")) { if (!strcasecmp(o->ptr,"volatile-lru")) { diff --git a/src/redis.c b/src/redis.c index a7f1b071c..e6731ba8b 100755 --- a/src/redis.c +++ b/src/redis.c @@ -794,6 +794,84 @@ void clientsCron(void) { } } +#ifdef __linux__ +long long int getFreeOSMemory(void) { + FILE *meminfo_file; + char buf[128]; + long long int memfree_value = -1; + long long int buffers_value = -1; + long long int cached_value = -1; + long long int memfree = -1; + + meminfo_file = fopen("/proc/meminfo", "r"); + if (!meminfo_file) + return -1; + while (fgets(buf, sizeof(buf)-1, meminfo_file) != NULL) { + char *p = NULL; + char *k; + char *arg; + if (!(k = strtok_r(buf, " ", &p))) + break; /* parse error */ + if (!(arg = strtok_r(NULL, " ", &p))) + break; /* parse error */ + if (strcmp(k, "MemFree:") == 0) { + memfree_value = strtoull(arg, &p, 10); + if (!p || *p != '\0') + memfree_value = -1; /* parse error */ + } else if (strcmp(k, "Buffers:") == 0) { + buffers_value = strtoull(arg, &p, 10); + if (!p || *p != '\0') + buffers_value = -1; /* parse error */ + } else if (strcmp(k, "Cached:") == 0) { + cached_value = strtoull(arg, &p, 10); + if (!p || *p != '\0') + cached_value = -1; /* parse error */ + } + if (memfree_value != -1 && + buffers_value != -1 && + cached_value != -1) { + memfree = memfree_value + buffers_value + cached_value; + break; + } + } + fclose(meminfo_file); + if (memfree > 0) + memfree *= 1024; + + return memfree; +} +#else +#error "Implement getFreeOSMemory for this platform first." +#endif + +void checkOSMemory(void) { + /* Called periodically if minmemory_os is defined, and verifies that + * enough free OS memory is reported. If not, it attempts to free 1/2 + * of the minmemory_os value. + */ + + long long int os_memfree; + + if (!server.minmemory_os) + return; + + os_memfree = getFreeOSMemory(); + if (os_memfree < 0) + return; + if ((unsigned long long) os_memfree < server.minmemory_os) { + long long int delta = server.minmemory_os - os_memfree; + + if ((long long int) zmalloc_used_memory() > (delta / 2)) { + redisLog(REDIS_WARNING, "OS Memory is low, trying to free %llu bytes.", delta / 2); + + freeMemoryIfNeeded(zmalloc_used_memory() - (delta / 2)); + } else { + redisLog(REDIS_WARNING, "OS Memory is low, but this redis is too small to attempt eviction."); + } + } +} + + /* This is our timer interrupt, called REDIS_HZ times per second. * Here is where we do a number of things that need to be done asynchronously. * For instance: @@ -856,6 +934,11 @@ int serverCron(struct aeEventLoop *eventLoop, long long id, void *clientData) { redisLog(REDIS_WARNING,"SIGTERM received but errors trying to shut down the server, check the logs for more information"); } + /* Try to evict if OS memory is low */ + run_with_period(10000) { + checkOSMemory(); + } + /* Cancel draining mode if not polled for a long time */ if (server.draining && server.unixtime - server.last_drain_time >= 10) server.draining = 0; @@ -1636,7 +1719,7 @@ int processCommand(redisClient *c) { * keys in the dataset). If there are not the only thing we can do * is returning an error. */ if (server.maxmemory) { - int retval = freeMemoryIfNeeded(); + int retval = freeMemoryIfNeeded(server.maxmemory); if ((c->cmd->flags & REDIS_CMD_DENYOOM) && retval == REDIS_ERR) { flagTransaction(c); addReply(c, shared.oomerr); @@ -2321,7 +2404,7 @@ void hideconnectionCommand(redisClient *c) { * should block the execution of commands that will result in more memory * used by the server. */ -int freeMemoryIfNeeded(void) { +int freeMemoryIfNeeded(unsigned long long maxmemory) { size_t mem_used, mem_tofree, mem_freed; int slaves = listLength(server.slaves); @@ -2348,13 +2431,13 @@ int freeMemoryIfNeeded(void) { } /* Check if we are over the memory limit. */ - if (mem_used <= server.maxmemory) return REDIS_OK; + if (mem_used <= maxmemory) return REDIS_OK; if (server.maxmemory_policy == REDIS_MAXMEMORY_NO_EVICTION) return REDIS_ERR; /* We need to free memory, but policy forbids. */ /* Compute how much memory we need to free. */ - mem_tofree = mem_used - server.maxmemory; + mem_tofree = mem_used - maxmemory; mem_freed = 0; while (mem_freed < mem_tofree) { int j, k, keys_freed = 0; diff --git a/src/redis.h b/src/redis.h index c3c62dfeb..059830c1a 100755 --- a/src/redis.h +++ b/src/redis.h @@ -650,6 +650,7 @@ struct redisServer { /* Limits */ unsigned int maxclients; /* Max number of simultaneous clients */ unsigned long long maxmemory; /* Max number of memory bytes to use */ + unsigned long long minmemory_os; /* OS Free memory threshold that */ int maxmemory_policy; /* Policy for key evition */ int maxmemory_samples; /* Pricision of random sampling */ /* Blocked clients */ @@ -969,7 +970,7 @@ unsigned int zsetLength(robj *zobj); void zsetConvert(robj *zobj, int encoding); /* Core functions */ -int freeMemoryIfNeeded(void); +int freeMemoryIfNeeded(unsigned long long maxmemory); int processCommand(redisClient *c); void setupSignalHandlers(void); struct redisCommand *lookupCommand(sds name); diff --git a/src/scripting.c b/src/scripting.c old mode 100644 new mode 100755 index d614f42a1..d3a33388a --- a/src/scripting.c +++ b/src/scripting.c @@ -278,7 +278,7 @@ int luaRedisGenericCommand(lua_State *lua, int raise_error) { if (server.maxmemory && server.lua_write_dirty == 0 && (cmd->flags & REDIS_CMD_DENYOOM)) { - if (freeMemoryIfNeeded() == REDIS_ERR) { + if (freeMemoryIfNeeded(server.maxmemory) == REDIS_ERR) { luaPushError(lua, shared.oomerr->ptr); goto cleanup; } -- GitLab