X-Git-Url: https://git.saurik.com/redis.git/blobdiff_plain/12ebe2ac17a26ecf263f90b2acdf03db29b00223..d9cb288c9ededc83c59e1f46f0ae738a0fd3edd2:/src/redis.c diff --git a/src/redis.c b/src/redis.c index 971dfeca..a7760e7b 100644 --- a/src/redis.c +++ b/src/redis.c @@ -69,130 +69,137 @@ double R_Zero, R_PosInf, R_NegInf, R_Nan; struct redisServer server; /* server global state */ struct redisCommand *commandTable; struct redisCommand readonlyCommandTable[] = { - {"get",getCommand,2,0,NULL,1,1,1}, - {"set",setCommand,3,REDIS_CMD_DENYOOM,NULL,0,0,0}, - {"setnx",setnxCommand,3,REDIS_CMD_DENYOOM,NULL,0,0,0}, - {"setex",setexCommand,4,REDIS_CMD_DENYOOM,NULL,0,0,0}, - {"append",appendCommand,3,REDIS_CMD_DENYOOM,NULL,1,1,1}, - {"substr",substrCommand,4,0,NULL,1,1,1}, - {"strlen",strlenCommand,2,0,NULL,1,1,1}, - {"del",delCommand,-2,0,NULL,0,0,0}, - {"exists",existsCommand,2,0,NULL,1,1,1}, - {"incr",incrCommand,2,REDIS_CMD_DENYOOM,NULL,1,1,1}, - {"decr",decrCommand,2,REDIS_CMD_DENYOOM,NULL,1,1,1}, - {"mget",mgetCommand,-2,0,NULL,1,-1,1}, - {"rpush",rpushCommand,3,REDIS_CMD_DENYOOM,NULL,1,1,1}, - {"lpush",lpushCommand,3,REDIS_CMD_DENYOOM,NULL,1,1,1}, - {"rpushx",rpushxCommand,3,REDIS_CMD_DENYOOM,NULL,1,1,1}, - {"lpushx",lpushxCommand,3,REDIS_CMD_DENYOOM,NULL,1,1,1}, - {"linsert",linsertCommand,5,REDIS_CMD_DENYOOM,NULL,1,1,1}, - {"rpop",rpopCommand,2,0,NULL,1,1,1}, - {"lpop",lpopCommand,2,0,NULL,1,1,1}, - {"brpop",brpopCommand,-3,0,NULL,1,1,1}, - {"blpop",blpopCommand,-3,0,NULL,1,1,1}, - {"llen",llenCommand,2,0,NULL,1,1,1}, - {"lindex",lindexCommand,3,0,NULL,1,1,1}, - {"lset",lsetCommand,4,REDIS_CMD_DENYOOM,NULL,1,1,1}, - {"lrange",lrangeCommand,4,0,NULL,1,1,1}, - {"ltrim",ltrimCommand,4,0,NULL,1,1,1}, - {"lrem",lremCommand,4,0,NULL,1,1,1}, - {"rpoplpush",rpoplpushcommand,3,REDIS_CMD_DENYOOM,NULL,1,2,1}, - {"sadd",saddCommand,3,REDIS_CMD_DENYOOM,NULL,1,1,1}, - {"srem",sremCommand,3,0,NULL,1,1,1}, - {"smove",smoveCommand,4,0,NULL,1,2,1}, - {"sismember",sismemberCommand,3,0,NULL,1,1,1}, - {"scard",scardCommand,2,0,NULL,1,1,1}, - {"spop",spopCommand,2,0,NULL,1,1,1}, - {"srandmember",srandmemberCommand,2,0,NULL,1,1,1}, - {"sinter",sinterCommand,-2,REDIS_CMD_DENYOOM,NULL,1,-1,1}, - {"sinterstore",sinterstoreCommand,-3,REDIS_CMD_DENYOOM,NULL,2,-1,1}, - {"sunion",sunionCommand,-2,REDIS_CMD_DENYOOM,NULL,1,-1,1}, - {"sunionstore",sunionstoreCommand,-3,REDIS_CMD_DENYOOM,NULL,2,-1,1}, - {"sdiff",sdiffCommand,-2,REDIS_CMD_DENYOOM,NULL,1,-1,1}, - {"sdiffstore",sdiffstoreCommand,-3,REDIS_CMD_DENYOOM,NULL,2,-1,1}, - {"smembers",sinterCommand,2,0,NULL,1,1,1}, - {"zadd",zaddCommand,4,REDIS_CMD_DENYOOM,NULL,1,1,1}, - {"zincrby",zincrbyCommand,4,REDIS_CMD_DENYOOM,NULL,1,1,1}, - {"zrem",zremCommand,3,0,NULL,1,1,1}, - {"zremrangebyscore",zremrangebyscoreCommand,4,0,NULL,1,1,1}, - {"zremrangebyrank",zremrangebyrankCommand,4,0,NULL,1,1,1}, - {"zunionstore",zunionstoreCommand,-4,REDIS_CMD_DENYOOM,zunionInterBlockClientOnSwappedKeys,0,0,0}, - {"zinterstore",zinterstoreCommand,-4,REDIS_CMD_DENYOOM,zunionInterBlockClientOnSwappedKeys,0,0,0}, - {"zrange",zrangeCommand,-4,0,NULL,1,1,1}, - {"zrangebyscore",zrangebyscoreCommand,-4,0,NULL,1,1,1}, - {"zrevrangebyscore",zrevrangebyscoreCommand,-4,0,NULL,1,1,1}, - {"zcount",zcountCommand,4,0,NULL,1,1,1}, - {"zrevrange",zrevrangeCommand,-4,0,NULL,1,1,1}, - {"zcard",zcardCommand,2,0,NULL,1,1,1}, - {"zscore",zscoreCommand,3,REDIS_CMD_DENYOOM,NULL,1,1,1}, - {"zrank",zrankCommand,3,0,NULL,1,1,1}, - {"zrevrank",zrevrankCommand,3,0,NULL,1,1,1}, - {"hset",hsetCommand,4,REDIS_CMD_DENYOOM,NULL,1,1,1}, - {"hsetnx",hsetnxCommand,4,REDIS_CMD_DENYOOM,NULL,1,1,1}, - {"hget",hgetCommand,3,0,NULL,1,1,1}, - {"hmset",hmsetCommand,-4,REDIS_CMD_DENYOOM,NULL,1,1,1}, - {"hmget",hmgetCommand,-3,0,NULL,1,1,1}, - {"hincrby",hincrbyCommand,4,REDIS_CMD_DENYOOM,NULL,1,1,1}, - {"hdel",hdelCommand,3,0,NULL,1,1,1}, - {"hlen",hlenCommand,2,0,NULL,1,1,1}, - {"hkeys",hkeysCommand,2,0,NULL,1,1,1}, - {"hvals",hvalsCommand,2,0,NULL,1,1,1}, - {"hgetall",hgetallCommand,2,0,NULL,1,1,1}, - {"hexists",hexistsCommand,3,0,NULL,1,1,1}, - {"incrby",incrbyCommand,3,REDIS_CMD_DENYOOM,NULL,1,1,1}, - {"decrby",decrbyCommand,3,REDIS_CMD_DENYOOM,NULL,1,1,1}, - {"getset",getsetCommand,3,REDIS_CMD_DENYOOM,NULL,1,1,1}, - {"mset",msetCommand,-3,REDIS_CMD_DENYOOM,NULL,1,-1,2}, - {"msetnx",msetnxCommand,-3,REDIS_CMD_DENYOOM,NULL,1,-1,2}, - {"randomkey",randomkeyCommand,1,0,NULL,0,0,0}, - {"select",selectCommand,2,0,NULL,0,0,0}, - {"move",moveCommand,3,0,NULL,1,1,1}, - {"rename",renameCommand,3,0,NULL,1,1,1}, - {"renamenx",renamenxCommand,3,0,NULL,1,1,1}, - {"expire",expireCommand,3,0,NULL,0,0,0}, - {"expireat",expireatCommand,3,0,NULL,0,0,0}, - {"keys",keysCommand,2,0,NULL,0,0,0}, - {"dbsize",dbsizeCommand,1,0,NULL,0,0,0}, - {"auth",authCommand,2,0,NULL,0,0,0}, - {"ping",pingCommand,1,0,NULL,0,0,0}, - {"echo",echoCommand,2,0,NULL,0,0,0}, - {"save",saveCommand,1,0,NULL,0,0,0}, - {"bgsave",bgsaveCommand,1,0,NULL,0,0,0}, - {"bgrewriteaof",bgrewriteaofCommand,1,0,NULL,0,0,0}, - {"shutdown",shutdownCommand,1,0,NULL,0,0,0}, - {"lastsave",lastsaveCommand,1,0,NULL,0,0,0}, - {"type",typeCommand,2,0,NULL,1,1,1}, - {"multi",multiCommand,1,0,NULL,0,0,0}, - {"exec",execCommand,1,REDIS_CMD_DENYOOM,execBlockClientOnSwappedKeys,0,0,0}, - {"discard",discardCommand,1,0,NULL,0,0,0}, - {"sync",syncCommand,1,0,NULL,0,0,0}, - {"flushdb",flushdbCommand,1,0,NULL,0,0,0}, - {"flushall",flushallCommand,1,0,NULL,0,0,0}, - {"sort",sortCommand,-2,REDIS_CMD_DENYOOM,NULL,1,1,1}, - {"info",infoCommand,1,0,NULL,0,0,0}, - {"monitor",monitorCommand,1,0,NULL,0,0,0}, - {"ttl",ttlCommand,2,0,NULL,1,1,1}, - {"persist",persistCommand,2,0,NULL,1,1,1}, - {"slaveof",slaveofCommand,3,0,NULL,0,0,0}, - {"debug",debugCommand,-2,0,NULL,0,0,0}, - {"config",configCommand,-2,0,NULL,0,0,0}, - {"subscribe",subscribeCommand,-2,0,NULL,0,0,0}, - {"unsubscribe",unsubscribeCommand,-1,0,NULL,0,0,0}, - {"psubscribe",psubscribeCommand,-2,0,NULL,0,0,0}, - {"punsubscribe",punsubscribeCommand,-1,0,NULL,0,0,0}, - {"publish",publishCommand,3,REDIS_CMD_FORCE_REPLICATION,NULL,0,0,0}, - {"watch",watchCommand,-2,0,NULL,0,0,0}, - {"unwatch",unwatchCommand,1,0,NULL,0,0,0} + {"get",getCommand,2,0,NULL,1,1,1,0}, + {"set",setCommand,3,REDIS_CMD_DENYOOM,NULL,0,0,0,0}, + {"setnx",setnxCommand,3,REDIS_CMD_DENYOOM,NULL,0,0,0,0}, + {"setex",setexCommand,4,REDIS_CMD_DENYOOM,NULL,0,0,0,0}, + {"append",appendCommand,3,REDIS_CMD_DENYOOM,NULL,1,1,1,0}, + {"strlen",strlenCommand,2,0,NULL,1,1,1,0}, + {"del",delCommand,-2,0,NULL,0,0,0,0}, + {"exists",existsCommand,2,0,NULL,1,1,1,0}, + {"setbit",setbitCommand,4,REDIS_CMD_DENYOOM,NULL,1,1,1,0}, + {"getbit",getbitCommand,3,0,NULL,1,1,1,0}, + {"setrange",setrangeCommand,4,REDIS_CMD_DENYOOM,NULL,1,1,1,0}, + {"getrange",getrangeCommand,4,0,NULL,1,1,1,0}, + {"substr",getrangeCommand,4,0,NULL,1,1,1,0}, + {"incr",incrCommand,2,REDIS_CMD_DENYOOM,NULL,1,1,1,0}, + {"decr",decrCommand,2,REDIS_CMD_DENYOOM,NULL,1,1,1,0}, + {"mget",mgetCommand,-2,0,NULL,1,-1,1,0}, + {"rpush",rpushCommand,3,REDIS_CMD_DENYOOM,NULL,1,1,1,0}, + {"lpush",lpushCommand,3,REDIS_CMD_DENYOOM,NULL,1,1,1,0}, + {"rpushx",rpushxCommand,3,REDIS_CMD_DENYOOM,NULL,1,1,1,0}, + {"lpushx",lpushxCommand,3,REDIS_CMD_DENYOOM,NULL,1,1,1,0}, + {"linsert",linsertCommand,5,REDIS_CMD_DENYOOM,NULL,1,1,1,0}, + {"rpop",rpopCommand,2,0,NULL,1,1,1,0}, + {"lpop",lpopCommand,2,0,NULL,1,1,1,0}, + {"brpop",brpopCommand,-3,0,NULL,1,1,1,0}, + {"brpoplpush",brpoplpushCommand,4,REDIS_CMD_DENYOOM,NULL,1,2,1,0}, + {"blpop",blpopCommand,-3,0,NULL,1,1,1,0}, + {"llen",llenCommand,2,0,NULL,1,1,1,0}, + {"lindex",lindexCommand,3,0,NULL,1,1,1,0}, + {"lset",lsetCommand,4,REDIS_CMD_DENYOOM,NULL,1,1,1,0}, + {"lrange",lrangeCommand,4,0,NULL,1,1,1,0}, + {"ltrim",ltrimCommand,4,0,NULL,1,1,1,0}, + {"lrem",lremCommand,4,0,NULL,1,1,1,0}, + {"rpoplpush",rpoplpushCommand,3,REDIS_CMD_DENYOOM,NULL,1,2,1,0}, + {"sadd",saddCommand,3,REDIS_CMD_DENYOOM,NULL,1,1,1,0}, + {"srem",sremCommand,3,0,NULL,1,1,1,0}, + {"smove",smoveCommand,4,0,NULL,1,2,1,0}, + {"sismember",sismemberCommand,3,0,NULL,1,1,1,0}, + {"scard",scardCommand,2,0,NULL,1,1,1,0}, + {"spop",spopCommand,2,0,NULL,1,1,1,0}, + {"srandmember",srandmemberCommand,2,0,NULL,1,1,1,0}, + {"sinter",sinterCommand,-2,REDIS_CMD_DENYOOM,NULL,1,-1,1,0}, + {"sinterstore",sinterstoreCommand,-3,REDIS_CMD_DENYOOM,NULL,2,-1,1,0}, + {"sunion",sunionCommand,-2,REDIS_CMD_DENYOOM,NULL,1,-1,1,0}, + {"sunionstore",sunionstoreCommand,-3,REDIS_CMD_DENYOOM,NULL,2,-1,1,0}, + {"sdiff",sdiffCommand,-2,REDIS_CMD_DENYOOM,NULL,1,-1,1,0}, + {"sdiffstore",sdiffstoreCommand,-3,REDIS_CMD_DENYOOM,NULL,2,-1,1,0}, + {"smembers",sinterCommand,2,0,NULL,1,1,1,0}, + {"zadd",zaddCommand,4,REDIS_CMD_DENYOOM,NULL,1,1,1,0}, + {"zincrby",zincrbyCommand,4,REDIS_CMD_DENYOOM,NULL,1,1,1,0}, + {"zrem",zremCommand,3,0,NULL,1,1,1,0}, + {"zremrangebyscore",zremrangebyscoreCommand,4,0,NULL,1,1,1,0}, + {"zremrangebyrank",zremrangebyrankCommand,4,0,NULL,1,1,1,0}, + {"zunionstore",zunionstoreCommand,-4,REDIS_CMD_DENYOOM,zunionInterBlockClientOnSwappedKeys,0,0,0,0}, + {"zinterstore",zinterstoreCommand,-4,REDIS_CMD_DENYOOM,zunionInterBlockClientOnSwappedKeys,0,0,0,0}, + {"zrange",zrangeCommand,-4,0,NULL,1,1,1,0}, + {"zrangebyscore",zrangebyscoreCommand,-4,0,NULL,1,1,1,0}, + {"zrevrangebyscore",zrevrangebyscoreCommand,-4,0,NULL,1,1,1,0}, + {"zcount",zcountCommand,4,0,NULL,1,1,1,0}, + {"zrevrange",zrevrangeCommand,-4,0,NULL,1,1,1,0}, + {"zcard",zcardCommand,2,0,NULL,1,1,1,0}, + {"zscore",zscoreCommand,3,0,NULL,1,1,1,0}, + {"zrank",zrankCommand,3,0,NULL,1,1,1,0}, + {"zrevrank",zrevrankCommand,3,0,NULL,1,1,1,0}, + {"hset",hsetCommand,4,REDIS_CMD_DENYOOM,NULL,1,1,1,0}, + {"hsetnx",hsetnxCommand,4,REDIS_CMD_DENYOOM,NULL,1,1,1,0}, + {"hget",hgetCommand,3,0,NULL,1,1,1,0}, + {"hmset",hmsetCommand,-4,REDIS_CMD_DENYOOM,NULL,1,1,1,0}, + {"hmget",hmgetCommand,-3,0,NULL,1,1,1,0}, + {"hincrby",hincrbyCommand,4,REDIS_CMD_DENYOOM,NULL,1,1,1,0}, + {"hdel",hdelCommand,3,0,NULL,1,1,1,0}, + {"hlen",hlenCommand,2,0,NULL,1,1,1,0}, + {"hkeys",hkeysCommand,2,0,NULL,1,1,1,0}, + {"hvals",hvalsCommand,2,0,NULL,1,1,1,0}, + {"hgetall",hgetallCommand,2,0,NULL,1,1,1,0}, + {"hexists",hexistsCommand,3,0,NULL,1,1,1,0}, + {"incrby",incrbyCommand,3,REDIS_CMD_DENYOOM,NULL,1,1,1,0}, + {"decrby",decrbyCommand,3,REDIS_CMD_DENYOOM,NULL,1,1,1,0}, + {"getset",getsetCommand,3,REDIS_CMD_DENYOOM,NULL,1,1,1,0}, + {"mset",msetCommand,-3,REDIS_CMD_DENYOOM,NULL,1,-1,2,0}, + {"msetnx",msetnxCommand,-3,REDIS_CMD_DENYOOM,NULL,1,-1,2,0}, + {"randomkey",randomkeyCommand,1,0,NULL,0,0,0,0}, + {"select",selectCommand,2,0,NULL,0,0,0,0}, + {"move",moveCommand,3,0,NULL,1,1,1,0}, + {"rename",renameCommand,3,0,NULL,1,1,1,0}, + {"renamenx",renamenxCommand,3,0,NULL,1,1,1,0}, + {"expire",expireCommand,3,0,NULL,0,0,0,0}, + {"expireat",expireatCommand,3,0,NULL,0,0,0,0}, + {"keys",keysCommand,2,0,NULL,0,0,0,0}, + {"dbsize",dbsizeCommand,1,0,NULL,0,0,0,0}, + {"auth",authCommand,2,0,NULL,0,0,0,0}, + {"ping",pingCommand,1,0,NULL,0,0,0,0}, + {"echo",echoCommand,2,0,NULL,0,0,0,0}, + {"save",saveCommand,1,0,NULL,0,0,0,0}, + {"bgsave",bgsaveCommand,1,0,NULL,0,0,0,0}, + {"bgrewriteaof",bgrewriteaofCommand,1,0,NULL,0,0,0,0}, + {"shutdown",shutdownCommand,1,0,NULL,0,0,0,0}, + {"lastsave",lastsaveCommand,1,0,NULL,0,0,0,0}, + {"type",typeCommand,2,0,NULL,1,1,1,0}, + {"multi",multiCommand,1,0,NULL,0,0,0,0}, + {"exec",execCommand,1,REDIS_CMD_DENYOOM,execBlockClientOnSwappedKeys,0,0,0,0}, + {"discard",discardCommand,1,0,NULL,0,0,0,0}, + {"sync",syncCommand,1,0,NULL,0,0,0,0}, + {"flushdb",flushdbCommand,1,0,NULL,0,0,0,0}, + {"flushall",flushallCommand,1,0,NULL,0,0,0,0}, + {"sort",sortCommand,-2,REDIS_CMD_DENYOOM,NULL,1,1,1,0}, + {"info",infoCommand,1,0,NULL,0,0,0,0}, + {"monitor",monitorCommand,1,0,NULL,0,0,0,0}, + {"ttl",ttlCommand,2,0,NULL,1,1,1,0}, + {"persist",persistCommand,2,0,NULL,1,1,1,0}, + {"slaveof",slaveofCommand,3,0,NULL,0,0,0,0}, + {"debug",debugCommand,-2,0,NULL,0,0,0,0}, + {"config",configCommand,-2,0,NULL,0,0,0,0}, + {"subscribe",subscribeCommand,-2,0,NULL,0,0,0,0}, + {"unsubscribe",unsubscribeCommand,-1,0,NULL,0,0,0,0}, + {"psubscribe",psubscribeCommand,-2,0,NULL,0,0,0,0}, + {"punsubscribe",punsubscribeCommand,-1,0,NULL,0,0,0,0}, + {"publish",publishCommand,3,REDIS_CMD_FORCE_REPLICATION,NULL,0,0,0,0}, + {"watch",watchCommand,-2,0,NULL,0,0,0,0}, + {"unwatch",unwatchCommand,1,0,NULL,0,0,0,0} }; /*============================ Utility functions ============================ */ void redisLog(int level, const char *fmt, ...) { + const int syslogLevelMap[] = { LOG_DEBUG, LOG_INFO, LOG_NOTICE, LOG_WARNING }; + const char *c = ".-*#"; + time_t now = time(NULL); va_list ap; FILE *fp; - char *c = ".-*#"; char buf[64]; - time_t now; + char msg[REDIS_MAX_LOGMSG_LEN]; if (level < server.verbosity) return; @@ -200,15 +207,16 @@ void redisLog(int level, const char *fmt, ...) { if (!fp) return; va_start(ap, fmt); - now = time(NULL); - strftime(buf,64,"%d %b %H:%M:%S",localtime(&now)); - fprintf(fp,"[%d] %s %c ",(int)getpid(),buf,c[level]); - vfprintf(fp, fmt, ap); - fprintf(fp,"\n"); - fflush(fp); + vsnprintf(msg, sizeof(msg), fmt, ap); va_end(ap); + strftime(buf,sizeof(buf),"%d %b %H:%M:%S",localtime(&now)); + fprintf(fp,"[%d] %s %c %s\n",(int)getpid(),buf,c[level],msg); + fflush(fp); + if (server.logfile) fclose(fp); + + if (server.syslog_enabled) syslog(syslogLevelMap[level], "%s", msg); } /* Redis generally does not try to recover from out of memory conditions @@ -222,6 +230,17 @@ void oom(const char *msg) { abort(); } +/* Return the UNIX time in microseconds */ +long long ustime(void) { + struct timeval tv; + long long ust; + + gettimeofday(&tv, NULL); + ust = ((long long)tv.tv_sec)*1000000; + ust += tv.tv_usec; + return ust; +} + /*====================== Hash table type implementation ==================== */ /* This is an hash table type that uses the SDS dynamic strings libary as @@ -337,7 +356,7 @@ unsigned int dictEncObjHash(const void *key) { } } -/* Sets type */ +/* Sets type and diskstore negative caching hash table */ dictType setDictType = { dictEncObjHash, /* hash function */ NULL, /* key dup */ @@ -508,7 +527,7 @@ void updateLRUClock(void) { } int serverCron(struct aeEventLoop *eventLoop, long long id, void *clientData) { - int j, loops = server.cronloops++; + int j, loops = server.cronloops; REDIS_NOTUSED(eventLoop); REDIS_NOTUSED(id); REDIS_NOTUSED(clientData); @@ -572,23 +591,43 @@ int serverCron(struct aeEventLoop *eventLoop, long long id, void *clientData) { } /* Close connections of timedout clients */ - if ((server.maxidletime && !(loops % 100)) || server.blpop_blocked_clients) + if ((server.maxidletime && !(loops % 100)) || server.bpop_blocked_clients) closeTimedoutClients(); - /* Check if a background saving or AOF rewrite in progress terminated */ + /* Check if a background saving or AOF rewrite in progress terminated. */ if (server.bgsavechildpid != -1 || server.bgrewritechildpid != -1) { int statloc; pid_t pid; if ((pid = wait3(&statloc,WNOHANG,NULL)) != 0) { + int exitcode = WEXITSTATUS(statloc); + int bysignal = 0; + + if (WIFSIGNALED(statloc)) bysignal = WTERMSIG(statloc); + if (pid == server.bgsavechildpid) { - backgroundSaveDoneHandler(statloc); + backgroundSaveDoneHandler(exitcode,bysignal); } else { - backgroundRewriteDoneHandler(statloc); + backgroundRewriteDoneHandler(exitcode,bysignal); } updateDictResizePolicy(); } - } else { + } else if (server.bgsavethread != (pthread_t) -1) { + if (server.bgsavethread != (pthread_t) -1) { + int state; + + pthread_mutex_lock(&server.bgsavethread_mutex); + state = server.bgsavethread_state; + pthread_mutex_unlock(&server.bgsavethread_mutex); + + if (state == REDIS_BGSAVE_THREAD_DONE_OK || + state == REDIS_BGSAVE_THREAD_DONE_ERR) + { + backgroundSaveDoneHandler( + (state == REDIS_BGSAVE_THREAD_DONE_OK) ? 0 : 1, 0); + } + } + } else if (!server.ds_enabled) { /* If there is not a background saving in progress check if * we have to save now */ time_t now = time(NULL); @@ -610,33 +649,15 @@ int serverCron(struct aeEventLoop *eventLoop, long long id, void *clientData) { * in order to guarantee a strict consistency. */ if (server.masterhost == NULL) activeExpireCycle(); - /* Swap a few keys on disk if we are over the memory limit and VM - * is enbled. Try to free objects from the free list first. */ - if (vmCanSwapOut()) { - while (server.vm_enabled && zmalloc_used_memory() > - server.vm_max_memory) - { - int retval = (server.vm_max_threads == 0) ? - vmSwapOneObjectBlocking() : - vmSwapOneObjectThreaded(); - if (retval == REDIS_ERR && !(loops % 300) && - zmalloc_used_memory() > - (server.vm_max_memory+server.vm_max_memory/10)) - { - redisLog(REDIS_WARNING,"WARNING: vm-max-memory limit exceeded by more than 10%% but unable to swap more objects out!"); - } - /* Note that when using threade I/O we free just one object, - * because anyway when the I/O thread in charge to swap this - * object out will finish, the handler of completed jobs - * will try to swap more objects if we are still out of memory. */ - if (retval == REDIS_ERR || server.vm_max_threads > 0) break; - } - } + /* Remove a few cached objects from memory if we are over the + * configured memory limit */ + if (server.ds_enabled) cacheCron(); /* Replication cron function -- used to reconnect to master and * to detect transfer failures. */ if (!(loops % 10)) replicationCron(); + server.cronloops++; return 100; } @@ -645,21 +666,22 @@ int serverCron(struct aeEventLoop *eventLoop, long long id, void *clientData) { * for ready file descriptors. */ void beforeSleep(struct aeEventLoop *eventLoop) { REDIS_NOTUSED(eventLoop); + listNode *ln; + redisClient *c; - /* Awake clients that got all the swapped keys they requested */ - if (server.vm_enabled && listLength(server.io_ready_clients)) { + /* Awake clients that got all the on disk keys they requested */ + if (server.ds_enabled && listLength(server.io_ready_clients)) { listIter li; - listNode *ln; listRewind(server.io_ready_clients,&li); while((ln = listNext(&li))) { - redisClient *c = ln->value; + c = ln->value; struct redisCommand *cmd; /* Resume the client. */ listDelNode(server.io_ready_clients,ln); c->flags &= (~REDIS_IO_WAIT); - server.vm_blocked_clients--; + server.cache_blocked_clients--; aeCreateFileEvent(server.el, c->fd, AE_READABLE, readQueryFromClient, c); cmd = lookupCommand(c->argv[0]->ptr); @@ -671,6 +693,20 @@ void beforeSleep(struct aeEventLoop *eventLoop) { processInputBuffer(c); } } + + /* Try to process pending commands for clients that were just unblocked. */ + while (listLength(server.unblocked_clients)) { + ln = listFirst(server.unblocked_clients); + redisAssert(ln != NULL); + c = ln->value; + listDelNode(server.unblocked_clients,ln); + c->flags &= ~REDIS_UNBLOCKED; + + /* Process remaining data in the input buffer. */ + if (c->querybuf && sdslen(c->querybuf) > 0) + processInputBuffer(c); + } + /* Write the AOF buffer on disk */ flushAppendOnlyFile(); } @@ -702,6 +738,8 @@ void createSharedObjects(void) { "-ERR source and destination objects are the same\r\n")); shared.outofrangeerr = createObject(REDIS_STRING,sdsnew( "-ERR index out of range\r\n")); + shared.loadingerr = createObject(REDIS_STRING,sdsnew( + "-LOADING Redis is loading the dataset in memory\r\n")); shared.space = createObject(REDIS_STRING,sdsnew(" ")); shared.colon = createObject(REDIS_STRING,sdsnew(":")); shared.plus = createObject(REDIS_STRING,sdsnew("+")); @@ -739,8 +777,11 @@ void initServerConfig() { server.verbosity = REDIS_VERBOSE; server.maxidletime = REDIS_MAXIDLETIME; server.saveparams = NULL; + server.loading = 0; server.logfile = NULL; /* NULL = log on standard output */ - server.glueoutputbuf = 1; + server.syslog_enabled = 0; + server.syslog_ident = zstrdup("redis"); + server.syslog_facility = LOG_LOCAL0; server.daemonize = 0; server.appendonly = 0; server.appendfsync = APPENDFSYNC_EVERYSEC; @@ -755,23 +796,21 @@ void initServerConfig() { server.rdbcompression = 1; server.activerehashing = 1; server.maxclients = 0; - server.blpop_blocked_clients = 0; + server.bpop_blocked_clients = 0; server.maxmemory = 0; server.maxmemory_policy = REDIS_MAXMEMORY_VOLATILE_LRU; server.maxmemory_samples = 3; - server.vm_enabled = 0; - server.vm_swap_file = zstrdup("/tmp/redis-%p.vm"); - server.vm_page_size = 256; /* 256 bytes per page */ - server.vm_pages = 1024*1024*100; /* 104 millions of pages */ - server.vm_max_memory = 1024LL*1024*1024*1; /* 1 GB of RAM */ - server.vm_max_threads = 4; - server.vm_blocked_clients = 0; + server.ds_enabled = 0; + server.ds_path = sdsnew("/tmp/redis.ds"); + server.cache_max_memory = 64LL*1024*1024; /* 64 MB of RAM */ + server.cache_blocked_clients = 0; server.hash_max_zipmap_entries = REDIS_HASH_MAX_ZIPMAP_ENTRIES; server.hash_max_zipmap_value = REDIS_HASH_MAX_ZIPMAP_VALUE; server.list_max_ziplist_entries = REDIS_LIST_MAX_ZIPLIST_ENTRIES; server.list_max_ziplist_value = REDIS_LIST_MAX_ZIPLIST_VALUE; server.set_max_intset_entries = REDIS_SET_MAX_INTSET_ENTRIES; server.shutdown_asap = 0; + server.cache_flush_delay = 0; updateLRUClock(); resetServerSaveParams(); @@ -786,6 +825,7 @@ void initServerConfig() { server.masterport = 6379; server.master = NULL; server.replstate = REDIS_REPL_NONE; + server.repl_serve_stale_data = 1; /* Double constants initialization */ R_Zero = 0.0; @@ -809,15 +849,18 @@ void initServer() { signal(SIGPIPE, SIG_IGN); setupSigSegvAction(); - server.mainthread = pthread_self(); - server.devnull = fopen("/dev/null","w"); - if (server.devnull == NULL) { - redisLog(REDIS_WARNING, "Can't open /dev/null: %s", server.neterr); - exit(1); + if (server.syslog_enabled) { + openlog(server.syslog_ident, LOG_PID | LOG_NDELAY | LOG_NOWAIT, + server.syslog_facility); } + + server.mainthread = pthread_self(); server.clients = listCreate(); server.slaves = listCreate(); server.monitors = listCreate(); + server.unblocked_clients = listCreate(); + server.cache_io_queue = listCreate(); + createSharedObjects(); server.el = aeCreateEventLoop(); server.db = zmalloc(sizeof(redisDb)*server.dbnum); @@ -843,8 +886,11 @@ void initServer() { server.db[j].expires = dictCreate(&keyptrDictType,NULL); server.db[j].blocking_keys = dictCreate(&keylistDictType,NULL); server.db[j].watched_keys = dictCreate(&keylistDictType,NULL); - if (server.vm_enabled) + if (server.ds_enabled) { server.db[j].io_keys = dictCreate(&keylistDictType,NULL); + server.db[j].io_negcache = dictCreate(&setDictType,NULL); + server.db[j].io_queued = dictCreate(&setDictType,NULL); + } server.db[j].id = j; } server.pubsub_channels = dictCreate(&keylistDictType,NULL); @@ -854,6 +900,8 @@ void initServer() { server.cronloops = 0; server.bgsavechildpid = -1; server.bgrewritechildpid = -1; + server.bgsavethread_state = REDIS_BGSAVE_THREAD_UNACTIVE; + server.bgsavethread = (pthread_t) -1; server.bgrewritebuf = sdsempty(); server.aofbuf = sdsempty(); server.lastsave = time(NULL); @@ -861,6 +909,7 @@ void initServer() { server.stat_numcommands = 0; server.stat_numconnections = 0; server.stat_expiredkeys = 0; + server.stat_evictedkeys = 0; server.stat_starttime = time(NULL); server.stat_keyspace_misses = 0; server.stat_keyspace_hits = 0; @@ -880,7 +929,7 @@ void initServer() { } } - if (server.vm_enabled) vmInit(); + if (server.ds_enabled) dsInit(); } /* Populates the Redis Command Table starting from the hard coded list @@ -915,11 +964,12 @@ struct redisCommand *lookupCommandByCString(char *s) { /* Call() is the core of Redis execution of a command */ void call(redisClient *c, struct redisCommand *cmd) { - long long dirty; + long long dirty, start = ustime(); dirty = server.dirty; cmd->proc(c); dirty = server.dirty-dirty; + cmd->microseconds += ustime()-start; if (server.appendonly && dirty) feedAppendOnlyFile(cmd,c->db->id,c->argv,c->argc); @@ -994,6 +1044,23 @@ int processCommand(redisClient *c) { return REDIS_OK; } + /* Only allow INFO and SLAVEOF when slave-serve-stale-data is no and + * we are a slave with a broken link with master. */ + if (server.masterhost && server.replstate != REDIS_REPL_CONNECTED && + server.repl_serve_stale_data == 0 && + cmd->proc != infoCommand && cmd->proc != slaveofCommand) + { + addReplyError(c, + "link with MASTER is down and slave-serve-stale-data is set to no"); + return REDIS_OK; + } + + /* Loading DB? Return an error if the command is not INFO */ + if (server.loading && cmd->proc != infoCommand) { + addReply(c, shared.loadingerr); + return REDIS_OK; + } + /* Exec the command */ if (c->flags & REDIS_MULTI && cmd->proc != execCommand && cmd->proc != discardCommand && @@ -1002,8 +1069,8 @@ int processCommand(redisClient *c) { queueMultiCommand(c,cmd); addReply(c,shared.queued); } else { - if (server.vm_enabled && server.vm_max_threads > 0 && - blockClientOnSwappedKeys(c,cmd)) return REDIS_ERR; + if (server.ds_enabled && blockClientOnSwappedKeys(c,cmd)) + return REDIS_ERR; call(c,cmd); } return REDIS_OK; @@ -1021,10 +1088,11 @@ int prepareForShutdown() { kill(server.bgsavechildpid,SIGKILL); rdbRemoveTempFile(server.bgsavechildpid); } - if (server.appendonly) { + if (server.ds_enabled) { + /* FIXME: flush all objects on disk */ + } else if (server.appendonly) { /* Append only file: fsync() the AOF and exit */ aof_fsync(server.appendfd); - if (server.vm_enabled) unlink(server.vm_swap_file); } else if (server.saveparamslen > 0) { /* Snapshotting. Perform a SYNC SAVE and exit */ if (rdbSave(server.dbfilename) != REDIS_OK) { @@ -1091,12 +1159,14 @@ void bytesToHuman(char *s, unsigned long long n) { sds genRedisInfoString(void) { sds info; time_t uptime = time(NULL)-server.stat_starttime; - int j; + int j, numcommands; char hmem[64]; struct rusage self_ru, c_ru; + unsigned long lol, bib; getrusage(RUSAGE_SELF, &self_ru); getrusage(RUSAGE_CHILDREN, &c_ru); + getClientsMaxBuffers(&lol,&bib); bytesToHuman(hmem,zmalloc_used_memory()); info = sdscatprintf(sdsempty(), @@ -1115,12 +1185,16 @@ sds genRedisInfoString(void) { "used_cpu_user_childrens:%.2f\r\n" "connected_clients:%d\r\n" "connected_slaves:%d\r\n" + "client_longest_output_list:%lu\r\n" + "client_biggest_input_buf:%lu\r\n" "blocked_clients:%d\r\n" "used_memory:%zu\r\n" "used_memory_human:%s\r\n" "used_memory_rss:%zu\r\n" "mem_fragmentation_ratio:%.2f\r\n" "use_tcmalloc:%d\r\n" + "loading:%d\r\n" + "aof_enabled:%d\r\n" "changes_since_last_save:%lld\r\n" "bgsave_in_progress:%d\r\n" "last_save_time:%ld\r\n" @@ -1128,13 +1202,14 @@ sds genRedisInfoString(void) { "total_connections_received:%lld\r\n" "total_commands_processed:%lld\r\n" "expired_keys:%lld\r\n" + "evicted_keys:%lld\r\n" "keyspace_hits:%lld\r\n" "keyspace_misses:%lld\r\n" "hash_max_zipmap_entries:%zu\r\n" "hash_max_zipmap_value:%zu\r\n" "pubsub_channels:%ld\r\n" "pubsub_patterns:%u\r\n" - "vm_enabled:%d\r\n" + "ds_enabled:%d\r\n" "role:%s\r\n" ,REDIS_VERSION, redisGitSHA1(), @@ -1151,7 +1226,8 @@ sds genRedisInfoString(void) { (float)c_ru.ru_stime.tv_sec+(float)c_ru.ru_stime.tv_usec/1000000, listLength(server.clients)-listLength(server.slaves), listLength(server.slaves), - server.blpop_blocked_clients, + lol, bib, + server.bpop_blocked_clients, zmalloc_used_memory(), hmem, zmalloc_get_rss(), @@ -1161,20 +1237,23 @@ sds genRedisInfoString(void) { #else 0, #endif + server.loading, + server.appendonly, server.dirty, - server.bgsavechildpid != -1, + server.bgsavechildpid != -1 || server.bgsavethread != (pthread_t) -1, server.lastsave, server.bgrewritechildpid != -1, server.stat_numconnections, server.stat_numcommands, server.stat_expiredkeys, + server.stat_evictedkeys, server.stat_keyspace_hits, server.stat_keyspace_misses, server.hash_max_zipmap_entries, server.hash_max_zipmap_value, dictSize(server.pubsub_channels), listLength(server.pubsub_patterns), - server.vm_enabled != 0, + server.ds_enabled != 0, server.masterhost == NULL ? "master" : "slave" ); if (server.masterhost) { @@ -1201,36 +1280,65 @@ sds genRedisInfoString(void) { ); } } - if (server.vm_enabled) { + if (server.ds_enabled) { lockThreadedIO(); info = sdscatprintf(info, - "vm_conf_max_memory:%llu\r\n" - "vm_conf_page_size:%llu\r\n" - "vm_conf_pages:%llu\r\n" - "vm_stats_used_pages:%llu\r\n" - "vm_stats_swapped_objects:%llu\r\n" - "vm_stats_swappin_count:%llu\r\n" - "vm_stats_swappout_count:%llu\r\n" - "vm_stats_io_newjobs_len:%lu\r\n" - "vm_stats_io_processing_len:%lu\r\n" - "vm_stats_io_processed_len:%lu\r\n" - "vm_stats_io_active_threads:%lu\r\n" - "vm_stats_blocked_clients:%lu\r\n" - ,(unsigned long long) server.vm_max_memory, - (unsigned long long) server.vm_page_size, - (unsigned long long) server.vm_pages, - (unsigned long long) server.vm_stats_used_pages, - (unsigned long long) server.vm_stats_swapped_objects, - (unsigned long long) server.vm_stats_swapins, - (unsigned long long) server.vm_stats_swapouts, - (unsigned long) listLength(server.io_newjobs), - (unsigned long) listLength(server.io_processing), - (unsigned long) listLength(server.io_processed), - (unsigned long) server.io_active_threads, - (unsigned long) server.vm_blocked_clients + "cache_max_memory:%llu\r\n" + "cache_blocked_clients:%lu\r\n" + ,(unsigned long long) server.cache_max_memory, + (unsigned long) server.cache_blocked_clients ); unlockThreadedIO(); } + if (server.loading) { + double perc; + time_t eta, elapsed; + off_t remaining_bytes = server.loading_total_bytes- + server.loading_loaded_bytes; + + perc = ((double)server.loading_loaded_bytes / + server.loading_total_bytes) * 100; + + elapsed = time(NULL)-server.loading_start_time; + if (elapsed == 0) { + eta = 1; /* A fake 1 second figure if we don't have enough info */ + } else { + eta = (elapsed*remaining_bytes)/server.loading_loaded_bytes; + } + + info = sdscatprintf(info, + "loading_start_time:%ld\r\n" + "loading_total_bytes:%llu\r\n" + "loading_loaded_bytes:%llu\r\n" + "loading_loaded_perc:%.2f\r\n" + "loading_eta_seconds:%ld\r\n" + ,(unsigned long) server.loading_start_time, + (unsigned long long) server.loading_total_bytes, + (unsigned long long) server.loading_loaded_bytes, + perc, + eta + ); + } + + info = sdscat(info,"allocation_stats:"); + for (j = 0; j <= ZMALLOC_MAX_ALLOC_STAT; j++) { + size_t count = zmalloc_allocations_for_size(j); + if (count) { + if (info[sdslen(info)-1] != ':') info = sdscatlen(info,",",1); + info = sdscatprintf(info,"%s%d=%zu", + (j == ZMALLOC_MAX_ALLOC_STAT) ? ">=" : "", + j,count); + } + } + info = sdscat(info,"\r\n"); + + numcommands = sizeof(readonlyCommandTable)/sizeof(struct redisCommand); + for (j = 0; j < numcommands; j++) { + struct redisCommand *c = readonlyCommandTable+j; + info = sdscatprintf(info,"command_%s:microseconds:%lld\r\n", + c->name, c->microseconds); + } + for (j = 0; j < server.dbnum; j++) { long long keys, vkeys; @@ -1278,6 +1386,8 @@ void monitorCommand(redisClient *c) { void freeMemoryIfNeeded(void) { /* Remove keys accordingly to the active policy as long as we are * over the memory limit. */ + if (server.maxmemory_policy == REDIS_MAXMEMORY_NO_EVICTION) return; + while (server.maxmemory && zmalloc_used_memory() > server.maxmemory) { int j, k, freed = 0; @@ -1316,6 +1426,10 @@ void freeMemoryIfNeeded(void) { de = dictGetRandomKey(dict); thiskey = dictGetEntryKey(de); + /* When policy is volatile-lru we need an additonal lookup + * to locate the real key, as dict is set to db->expires. */ + if (server.maxmemory_policy == REDIS_MAXMEMORY_VOLATILE_LRU) + de = dictFind(db->dict, thiskey); o = dictGetEntryVal(de); thisval = estimateObjectIdleTime(o); @@ -1350,44 +1464,13 @@ void freeMemoryIfNeeded(void) { if (bestkey) { robj *keyobj = createStringObject(bestkey,sdslen(bestkey)); dbDelete(db,keyobj); - server.stat_expiredkeys++; + server.stat_evictedkeys++; decrRefCount(keyobj); freed++; } } if (!freed) return; /* nothing to free... */ } - - while(0) { - int j, k, freed = 0; - for (j = 0; j < server.dbnum; j++) { - int minttl = -1; - sds minkey = NULL; - robj *keyobj = NULL; - struct dictEntry *de; - - if (dictSize(server.db[j].expires)) { - freed = 1; - /* From a sample of three keys drop the one nearest to - * the natural expire */ - for (k = 0; k < 3; k++) { - time_t t; - - de = dictGetRandomKey(server.db[j].expires); - t = (time_t) dictGetEntryVal(de); - if (minttl == -1 || t < minttl) { - minkey = dictGetEntryKey(de); - minttl = t; - } - } - keyobj = createStringObject(minkey,sdslen(minkey)); - dbDelete(server.db+j,keyobj); - server.stat_expiredkeys++; - decrRefCount(keyobj); - } - } - if (!freed) return; /* nothing to free... */ - } } /* =================================== Main! ================================ */ @@ -1418,7 +1501,7 @@ void createPidFile(void) { /* Try to write the pid file in a best-effort way. */ FILE *fp = fopen(server.pidfile,"w"); if (fp) { - fprintf(fp,"%d\n",getpid()); + fprintf(fp,"%d\n",(int)getpid()); fclose(fp); } } @@ -1475,7 +1558,9 @@ int main(int argc, char **argv) { linuxOvercommitMemoryWarning(); #endif start = time(NULL); - if (server.appendonly) { + if (server.ds_enabled) { + redisLog(REDIS_NOTICE,"DB not loaded (running with disk back end)"); + } else if (server.appendonly) { if (loadAppendOnlyFile(server.appendfilename) == REDIS_OK) redisLog(REDIS_NOTICE,"DB loaded from append only file: %ld seconds",time(NULL)-start); } else {