diff --git a/src/config.c b/src/config.c index 0d168c03..15ea1c5c 100644 --- a/src/config.c +++ b/src/config.c @@ -600,7 +600,7 @@ void configSetCommand(redisClient *c) { } else if (!strcasecmp(c->argv[2]->ptr,"maxclients")) { int orig_value = server.maxclients; - if (getLongLongFromObject(o,&ll) == REDIS_ERR || ll < 0) goto badfmt; + if (getLongLongFromObject(o,&ll) == REDIS_ERR || ll < 1) goto badfmt; /* Try to check if the OS is capable of supporting so many FDs. */ server.maxclients = ll; diff --git a/src/redis.c b/src/redis.c index d2dece76..9f8868bb 100644 --- a/src/redis.c +++ b/src/redis.c @@ -1490,21 +1490,21 @@ void initServerConfig() { } /* This function will try to raise the max number of open files accordingly to - * the configured max number of clients. It will also account for 32 additional - * file descriptors as we need a few more for persistence, listening - * sockets, log files and so forth. + * the configured max number of clients. It also reserves a number of file + * descriptors (REDIS_MIN_RESERVED_FDS) for extra operations of + * persistence, listening sockets, log files and so forth. * * If it will not be possible to set the limit accordingly to the configured * max number of clients, the function will do the reverse setting * server.maxclients to the value that we can actually handle. */ void adjustOpenFilesLimit(void) { - rlim_t maxfiles = server.maxclients+32; + rlim_t maxfiles = server.maxclients+REDIS_MIN_RESERVED_FDS; struct rlimit limit; if (getrlimit(RLIMIT_NOFILE,&limit) == -1) { redisLog(REDIS_WARNING,"Unable to obtain the current NOFILE limit (%s), assuming 1024 and setting the max clients configuration accordingly.", strerror(errno)); - server.maxclients = 1024-32; + server.maxclients = 1024-REDIS_MIN_RESERVED_FDS; } else { rlim_t oldlimit = limit.rlim_cur; @@ -1518,16 +1518,46 @@ void adjustOpenFilesLimit(void) { limit.rlim_cur = f; limit.rlim_max = f; if (setrlimit(RLIMIT_NOFILE,&limit) != -1) break; - f -= 128; + f -= REDIS_MIN_RESERVED_FDS; + if (f > limit.rlim_cur) { + /* Instead of getting smaller, f just got bigger. + * That means it wrapped around its unsigned floor + * and is now closer to 2^64. We can't help anymore. */ + redisLog(REDIS_WARNING,"Failed to set max file limit. " + "You requested maxclients of %d " + "but your 'ulimit -n' is set to %llu. " + "Please increase your 'ulimit -n' to at least %llu.", + server.maxclients, oldlimit, maxfiles); + exit(1); + } } if (f < oldlimit) f = oldlimit; if (f != maxfiles) { - server.maxclients = f-32; - redisLog(REDIS_WARNING,"Unable to set the max number of files limit to %d (%s), setting the max clients configuration to %d.", - (int) maxfiles, strerror(errno), (int) server.maxclients); + int old_maxclients = server.maxclients; + int original_errno = errno; + server.maxclients = f-REDIS_MIN_RESERVED_FDS; + if (server.maxclients < 1) { + redisLog(REDIS_WARNING,"Your current 'ulimit -n' " + "of %llu is not enough for Redis to start. " + "Please increase your open file limit to at least " + "%llu. Exiting.", oldlimit, maxfiles); + exit(1); + } + redisLog(REDIS_WARNING,"You requested maxclients of %d " + "requiring at least %llu max file descriptors.", + old_maxclients, maxfiles); + redisLog(REDIS_WARNING,"Redis can't set maximum open files " + "to %llu because of OS error: %s.", + maxfiles, strerror(original_errno)); + redisLog(REDIS_WARNING,"Current maximum open files is %llu. " + "maxclients has been reduced to %d to compensate for " + "low ulimit. " + "If you need higher maxclients increase 'ulimit -n'.", + oldlimit, server.maxclients); } else { - redisLog(REDIS_NOTICE,"Max number of open files set to %d", - (int) maxfiles); + redisLog(REDIS_NOTICE,"Increased maximum number of open files " + "to %llu (it was originally set to %llu).", + maxfiles, oldlimit); } } } diff --git a/src/redis.h b/src/redis.h index fd5dac19..14a656b3 100644 --- a/src/redis.h +++ b/src/redis.h @@ -123,6 +123,7 @@ #define REDIS_IP_STR_LEN INET6_ADDRSTRLEN #define REDIS_PEER_ID_LEN (REDIS_IP_STR_LEN+32) /* Must be enough for ip:port */ #define REDIS_BINDADDR_MAX 16 +#define REDIS_MIN_RESERVED_FDS 32 #define ACTIVE_EXPIRE_CYCLE_LOOKUPS_PER_LOOP 20 /* Loopkups per loop. */ #define ACTIVE_EXPIRE_CYCLE_FAST_DURATION 1000 /* Microseconds */ @@ -139,9 +140,9 @@ #define REDIS_LONGSTR_SIZE 21 /* Bytes needed for long -> str */ #define REDIS_AOF_AUTOSYNC_BYTES (1024*1024*32) /* fdatasync every 32MB */ /* When configuring the Redis eventloop, we setup it so that the total number - * of file descriptors we can handle are server.maxclients + FDSET_INCR + * of file descriptors we can handle are server.maxclients + RESERVED_FDS + FDSET_INCR * that is our safety margin. */ -#define REDIS_EVENTLOOP_FDSET_INCR 128 +#define REDIS_EVENTLOOP_FDSET_INCR (REDIS_MIN_RESERVED_FDS+96) /* Hash table parameters */ #define REDIS_HT_MINFILL 10 /* Minimal hash table fill 10% */ @@ -786,7 +787,7 @@ struct redisServer { list *clients_waiting_acks; /* Clients waiting in WAIT command. */ int get_ack_from_slaves; /* If true we send REPLCONF GETACK. */ /* Limits */ - unsigned int maxclients; /* Max number of simultaneous clients */ + int maxclients; /* Max number of simultaneous clients */ unsigned long long maxmemory; /* Max number of memory bytes to use */ int maxmemory_policy; /* Policy for key eviction */ int maxmemory_samples; /* Pricision of random sampling */