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);
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)
{
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 */
while (server.vm_enabled && zmalloc_used_memory() >
server.vm_max_memory)
{
- int retval;
-
- if (tryFreeOneObjectFromFreelist() == REDIS_OK) continue;
- retval = (server.vm_max_threads == 0) ?
+ int retval = (server.vm_max_threads == 0) ?
vmSwapOneObjectBlocking() :
vmSwapOneObjectThreaded();
if (retval == REDIS_ERR && !(loops % 300) &&
if (server.appendonly) rewriteAppendOnlyFileBackground();
}
}
-
- /* Update fragmentation info, used for fast RSS estimation */
- if (!(loops % 10)) server.fragmentation = zmalloc_get_fragmentation_ratio();
return 100;
}
}
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;
server.requirepass = NULL;
server.rdbcompression = 1;
server.activerehashing = 1;
- server.fragmentation = 1;
server.maxclients = 0;
server.blpop_blocked_clients = 0;
server.maxmemory = 0;
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() {
server.clients = listCreate();
server.slaves = listCreate();
server.monitors = listCreate();
- server.objfreelist = listCreate();
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++) {
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);
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 */
"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"
zmalloc_used_memory(),
hmem,
zmalloc_get_rss(),
- redisEstimateRSS(),
zmalloc_get_fragmentation_ratio(),
#ifdef USE_TCMALLOC
1,
/* ============================ Maxmemory directive ======================== */
-/* Try to free one object form the pre-allocated objects free list.
- * This is useful under low mem conditions as by default we take 1 million
- * free objects allocated. On success REDIS_OK is returned, otherwise
- * REDIS_ERR. */
-int tryFreeOneObjectFromFreelist(void) {
- robj *o;
-
- if (server.vm_enabled) pthread_mutex_lock(&server.obj_freelist_mutex);
- if (listLength(server.objfreelist)) {
- listNode *head = listFirst(server.objfreelist);
- o = listNodeValue(head);
- listDelNode(server.objfreelist,head);
- if (server.vm_enabled) pthread_mutex_unlock(&server.obj_freelist_mutex);
- zfree(o);
- return REDIS_OK;
- } else {
- if (server.vm_enabled) pthread_mutex_unlock(&server.obj_freelist_mutex);
- return REDIS_ERR;
- }
-}
-
-/* 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:
while (server.maxmemory && zmalloc_used_memory() > server.maxmemory) {
int j, k, freed = 0;
- /* Basic strategy -- remove objects from the free list. */
- if (tryFreeOneObjectFromFreelist() == REDIS_OK) continue;
-
for (j = 0; j < server.dbnum; j++) {
long bestval = 0; /* just to prevent warning */
sds bestkey = NULL;
time_t start;
initServerConfig();
- sortCommandTable();
if (argc == 2) {
if (strcmp(argv[1], "-v") == 0 ||
strcmp(argv[1], "--version") == 0) version();
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);