X-Git-Url: https://git.saurik.com/redis.git/blobdiff_plain/ca734d17ad9b7ea97a87bd048ad1c1e253ef67a6..feecb608ed0ec8f9932960d5fa8f02e551ffbe59:/src/redis.c diff --git a/src/redis.c b/src/redis.c index 99ff1898..30fe807d 100644 --- a/src/redis.c +++ b/src/redis.c @@ -252,6 +252,15 @@ int dictSdsKeyCompare(void *privdata, const void *key1, return memcmp(key1, key2, l1) == 0; } +/* A case insensitive version used for the command lookup table. */ +int dictSdsKeyCaseCompare(void *privdata, const void *key1, + const void *key2) +{ + DICT_NOTUSED(privdata); + + return strcasecmp(key1, key2) == 0; +} + void dictRedisObjectDestructor(void *privdata, void *val) { DICT_NOTUSED(privdata); @@ -283,6 +292,10 @@ unsigned int dictSdsHash(const void *key) { return dictGenHashFunction((unsigned char*)key, sdslen((char*)key)); } +unsigned int dictSdsCaseHash(const void *key) { + return dictGenCaseHashFunction((unsigned char*)key, sdslen((char*)key)); +} + int dictEncObjKeyCompare(void *privdata, const void *key1, const void *key2) { @@ -364,6 +377,16 @@ dictType keyptrDictType = { NULL /* val destructor */ }; +/* Command table. sds string -> command struct pointer. */ +dictType commandTableDictType = { + dictSdsCaseHash, /* hash function */ + NULL, /* key dup */ + NULL, /* val dup */ + dictSdsKeyCaseCompare, /* key compare */ + dictSdsDestructor, /* key destructor */ + NULL /* val destructor */ +}; + /* Hash type hash table (note that small hashes are represented with zimpaps) */ dictType hashDictType = { dictEncObjHash, /* hash function */ @@ -621,9 +644,6 @@ int serverCron(struct aeEventLoop *eventLoop, long long id, void *clientData) { if (server.appendonly) rewriteAppendOnlyFileBackground(); } } - - /* Update fragmentation info, used for fast RSS estimation */ - if (!(loops % 10)) server.fragmentation = zmalloc_get_fragmentation_ratio(); return 100; } @@ -717,13 +737,16 @@ void createSharedObjects(void) { } void initServerConfig() { - server.dbnum = REDIS_DEFAULT_DBNUM; server.port = REDIS_SERVERPORT; + server.bindaddr = NULL; + server.unixsocket = NULL; + server.ipfd = -1; + server.sofd = -1; + server.dbnum = REDIS_DEFAULT_DBNUM; server.verbosity = REDIS_VERBOSE; server.maxidletime = REDIS_MAXIDLETIME; server.saveparams = NULL; server.logfile = NULL; /* NULL = log on standard output */ - server.bindaddr = NULL; server.glueoutputbuf = 1; server.daemonize = 0; server.appendonly = 0; @@ -738,7 +761,6 @@ void initServerConfig() { server.requirepass = NULL; server.rdbcompression = 1; server.activerehashing = 1; - server.fragmentation = 1; server.maxclients = 0; server.blpop_blocked_clients = 0; server.maxmemory = 0; @@ -777,6 +799,14 @@ void initServerConfig() { R_PosInf = 1.0/R_Zero; R_NegInf = -1.0/R_Zero; R_Nan = R_Zero/R_Zero; + + /* Command table -- we intiialize it here as it is part of the + * initial configuration, since command names may be changed via + * redis.conf using the rename-command directive. */ + server.commands = dictCreate(&commandTableDictType,NULL); + populateCommandTable(); + server.delCommand = lookupCommandByCString("del"); + server.multiCommand = lookupCommandByCString("multi"); } void initServer() { @@ -799,9 +829,21 @@ void initServer() { createSharedObjects(); server.el = aeCreateEventLoop(); server.db = zmalloc(sizeof(redisDb)*server.dbnum); - server.fd = anetTcpServer(server.neterr, server.port, server.bindaddr); - if (server.fd == -1) { - redisLog(REDIS_WARNING, "Opening TCP port: %s", server.neterr); + server.ipfd = anetTcpServer(server.neterr,server.port,server.bindaddr); + if (server.ipfd == ANET_ERR) { + redisLog(REDIS_WARNING, "Opening port: %s", server.neterr); + exit(1); + } + if (server.unixsocket != NULL) { + unlink(server.unixsocket); /* don't care if this fails */ + server.sofd = anetUnixServer(server.neterr,server.unixsocket); + if (server.sofd == ANET_ERR) { + redisLog(REDIS_WARNING, "Opening socket: %s", server.neterr); + exit(1); + } + } + if (server.ipfd < 0 && server.sofd < 0) { + redisLog(REDIS_WARNING, "Configured to not listen anywhere, exiting."); exit(1); } for (j = 0; j < server.dbnum; j++) { @@ -832,8 +874,10 @@ void initServer() { server.stat_keyspace_hits = 0; server.unixtime = time(NULL); aeCreateTimeEvent(server.el, 1, serverCron, NULL, NULL); - if (aeCreateFileEvent(server.el, server.fd, AE_READABLE, - acceptHandler, NULL) == AE_ERR) oom("creating file event"); + if (server.ipfd > 0 && aeCreateFileEvent(server.el,server.ipfd,AE_READABLE, + acceptTcpHandler,NULL) == AE_ERR) oom("creating file event"); + if (server.sofd > 0 && aeCreateFileEvent(server.el,server.sofd,AE_READABLE, + acceptUnixHandler,NULL) == AE_ERR) oom("creating file event"); if (server.appendonly) { server.appendfd = open(server.appendfilename,O_WRONLY|O_APPEND|O_CREAT,0644); @@ -847,31 +891,34 @@ void initServer() { if (server.vm_enabled) vmInit(); } -int qsortRedisCommands(const void *r1, const void *r2) { - return strcasecmp( - ((struct redisCommand*)r1)->name, - ((struct redisCommand*)r2)->name); -} +/* Populates the Redis Command Table starting from the hard coded list + * we have on top of redis.c file. */ +void populateCommandTable(void) { + int j; + int numcommands = sizeof(readonlyCommandTable)/sizeof(struct redisCommand); + + for (j = 0; j < numcommands; j++) { + struct redisCommand *c = readonlyCommandTable+j; + int retval; -void sortCommandTable() { - /* Copy and sort the read-only version of the command table */ - commandTable = (struct redisCommand*)zmalloc(sizeof(readonlyCommandTable)); - memcpy(commandTable,readonlyCommandTable,sizeof(readonlyCommandTable)); - qsort(commandTable, - sizeof(readonlyCommandTable)/sizeof(struct redisCommand), - sizeof(struct redisCommand),qsortRedisCommands); + retval = dictAdd(server.commands, sdsnew(c->name), c); + assert(retval == DICT_OK); + } } /* ====================== Commands lookup and execution ===================== */ -struct redisCommand *lookupCommand(char *name) { - struct redisCommand tmp = {name,NULL,0,0,NULL,0,0,0}; - return bsearch( - &tmp, - commandTable, - sizeof(readonlyCommandTable)/sizeof(struct redisCommand), - sizeof(struct redisCommand), - qsortRedisCommands); +struct redisCommand *lookupCommand(sds name) { + return dictFetchValue(server.commands, name); +} + +struct redisCommand *lookupCommandByCString(char *s) { + struct redisCommand *cmd; + sds name = sdsnew(s); + + cmd = dictFetchValue(server.commands, name); + sdsfree(name); + return cmd; } /* Call() is the core of Redis execution of a command */ @@ -1080,7 +1127,6 @@ sds genRedisInfoString(void) { "used_memory:%zu\r\n" "used_memory_human:%s\r\n" "used_memory_rss:%zu\r\n" - "used_memory_estimated_rss:%zu\r\n" "mem_fragmentation_ratio:%.2f\r\n" "use_tcmalloc:%d\r\n" "changes_since_last_save:%lld\r\n" @@ -1117,7 +1163,6 @@ sds genRedisInfoString(void) { zmalloc_used_memory(), hmem, zmalloc_get_rss(), - redisEstimateRSS(), zmalloc_get_fragmentation_ratio(), #ifdef USE_TCMALLOC 1, @@ -1237,40 +1282,6 @@ int tryFreeOneObjectFromFreelist(void) { } } -/* A fast RSS sampling function. - * - * The function is reasonably accurate while fast, since it uses the trick of - * using the server.fragmentation ratio that is computed every second and - * is the ratio between the RSS and our zmalloc() count of allocated bytes. - * - * So in order to compute the current RSS used we just need to multiply - * the zmalloc() memory reporting, that is as fast as reading a counter, - * for the latest estimation of fragmentation. - * - * The behavior of this function is also very desirable because it is - * very responsive to memory changes: while the real RSS is actually measured - * in pages, the RSS estimation will actually change even if just a few bytes - * are freed, and this is a good property when the function is used in order - * to evict keys for Virtual Memory of for 'maxmemory' directive. - * - * Note that when the memory reported by zmalloc is smaller than the RSS - * (that is, fragmentation < 1) this means that something is odd (many pages - * swapped since the Redis instance is idle) and we consider the fragmentation - * ratio 1. */ -size_t redisEstimateRSS(void) { - size_t used = zmalloc_used_memory(); - float maxfrag; - - if (server.fragmentation < 1) return used; - maxfrag = (float)SIZE_MAX / used; - - /* If multiplying memory usage reported by zmalloc per fragmentation - * ratio will lead to an overflow we just return SIZE_MAX. */ - if (maxfrag < server.fragmentation) return SIZE_MAX; - - return (size_t)((used * server.fragmentation)); -} - /* This function gets called when 'maxmemory' is set on the config file to limit * the max memory used by the server, and we are out of memory. * This function will try to, in order: @@ -1466,7 +1477,6 @@ int main(int argc, char **argv) { time_t start; initServerConfig(); - sortCommandTable(); if (argc == 2) { if (strcmp(argv[1], "-v") == 0 || strcmp(argv[1], "--version") == 0) version(); @@ -1493,7 +1503,10 @@ int main(int argc, char **argv) { if (rdbLoad(server.dbfilename) == REDIS_OK) redisLog(REDIS_NOTICE,"DB loaded from disk: %ld seconds",time(NULL)-start); } - redisLog(REDIS_NOTICE,"The server is now ready to accept connections on port %d", server.port); + if (server.ipfd > 0) + redisLog(REDIS_NOTICE,"The server is now ready to accept connections on port %d", server.port); + if (server.sofd > 0) + redisLog(REDIS_NOTICE,"The server is now ready to accept connections at %s", server.unixsocket); aeSetBeforeSleepProc(server.el,beforeSleep); aeMain(server.el); aeDeleteEventLoop(server.el);