From: antirez Date: Sat, 6 Jun 2009 21:40:48 +0000 (+0200) Subject: Merge git://github.com/dierbro/redis X-Git-Url: https://git.saurik.com/redis.git/commitdiff_plain/c4cd5f393ced23cb298c52eaa5ed67569d3f8d0d?hp=-c Merge git://github.com/dierbro/redis --- c4cd5f393ced23cb298c52eaa5ed67569d3f8d0d diff --combined Makefile index f7c54647,9fc0caf0..fda703e1 --- a/Makefile +++ b/Makefile @@@ -2,8 -2,8 +2,8 @@@ # Copyright (C) 2009 Salvatore Sanfilippo # This file is released under the BSD license, see the COPYING file - DEBUG?= -g - CFLAGS?= -std=c99 -pedantic -O2 -Wall -W -DSDS_ABORT_ON_OOM + DEBUG?= -g -rdynamic -ggdb + CFLAGS?= -std=c99 -pedantic -O0 -Wall -W -DSDS_ABORT_ON_OOM CCOPT= $(CFLAGS) OBJ = adlist.o ae.o anet.o dict.o redis.o sds.o zmalloc.o lzf_c.o lzf_d.o pqsort.o @@@ -17,18 -17,16 +17,18 @@@ CLIPRGNAME = redis-cl all: redis-server redis-benchmark redis-cli # Deps (use make dep to generate this) -adlist.o: adlist.c adlist.h -ae.o: ae.c ae.h -anet.o: anet.c anet.h -benchmark.o: benchmark.c ae.h anet.h sds.h adlist.h -dict.o: dict.c dict.h -redis-cli.o: redis-cli.c anet.h sds.h adlist.h -redis.o: redis.c ae.h sds.h anet.h dict.h adlist.h zmalloc.c zmalloc.h -sds.o: sds.c sds.h -sha1.o: sha1.c sha1.h -zmalloc.o: zmalloc.c +adlist.o: adlist.c adlist.h zmalloc.h +ae.o: ae.c ae.h zmalloc.h +anet.o: anet.c fmacros.h anet.h +benchmark.o: benchmark.c fmacros.h ae.h anet.h sds.h adlist.h zmalloc.h +dict.o: dict.c fmacros.h dict.h zmalloc.h +lzf_c.o: lzf_c.c lzfP.h +lzf_d.o: lzf_d.c lzfP.h +pqsort.o: pqsort.c +redis-cli.o: redis-cli.c fmacros.h anet.h sds.h adlist.h zmalloc.h +redis.o: redis.c fmacros.h ae.h sds.h anet.h dict.h adlist.h zmalloc.h lzf.h pqsort.h config.h +sds.o: sds.c sds.h zmalloc.h +zmalloc.o: zmalloc.c config.h redis-server: $(OBJ) $(CC) -o $(PRGNAME) $(CCOPT) $(DEBUG) $(OBJ) diff --combined redis.c index f9fb3682,57c7ed1f..56ddee9e --- a/redis.c +++ b/redis.c @@@ -36,7 -36,10 +36,10 @@@ #include #include #include + #define __USE_POSIX199309 #include + #include + #include #include #include #include @@@ -51,6 -54,7 +54,7 @@@ #include #include + #include "redis.h" #include "ae.h" /* Event driven programming library */ #include "sds.h" /* Dynamic safe strings */ #include "anet.h" /* Networking the easy way */ @@@ -60,8 -64,6 +64,8 @@@ #include "lzf.h" /* LZF compression library */ #include "pqsort.h" /* Partial qsort for SORT+LIMIT */ +#include "config.h" + /* Error codes */ #define REDIS_OK 0 #define REDIS_ERR -1 @@@ -77,7 -79,6 +81,7 @@@ #define REDIS_OBJFREELIST_MAX 1000000 /* Max number of objects to cache */ #define REDIS_MAX_SYNC_TIME 60 /* Slave can't take more to sync */ #define REDIS_EXPIRELOOKUPS_PER_CRON 100 /* try to expire 100 keys/second */ +#define REDIS_MAX_WRITE_PER_EVENT (1024*64) /* Hash table parameters */ #define REDIS_HT_MINFILL 10 /* Minimal hash table fill 10% */ @@@ -171,6 -172,7 +175,7 @@@ /* Anti-warning macro... */ #define REDIS_NOTUSED(V) ((void) V) + /*================================= Data types ============================== */ /* A redis object, that is a type able to hold a string / list / set */ @@@ -241,7 -243,6 +246,7 @@@ struct redisServer int daemonize; char *pidfile; int bgsaveinprogress; + pid_t bgsavechildpid; struct saveparam *saveparams; int saveparamslen; char *logfile; @@@ -272,6 -273,11 +277,11 @@@ struct redisCommand int flags; }; + struct redisFunctionSym { + char *name; + long pointer; + }; + typedef struct _redisSortObject { robj *obj; union { @@@ -319,6 -325,7 +329,7 @@@ static time_t getExpire(redisDb *db, ro static int setExpire(redisDb *db, robj *key, time_t when); static void updateSalvesWaitingBgsave(int bgsaveerr); static void freeMemoryIfNeeded(void); + static int processCommand(redisClient *c); static void authCommand(redisClient *c); static void pingCommand(redisClient *c); @@@ -377,7 -384,7 +388,7 @@@ static void getSetCommand(redisClient * static void ttlCommand(redisClient *c); static void slaveofCommand(redisClient *c); static void debugCommand(redisClient *c); - + static void setupSigSegvAction(); /*================================= Globals ================================= */ /* Global vars */ @@@ -443,7 -450,91 +454,91 @@@ static struct redisCommand cmdTable[] {"debug",debugCommand,-2,REDIS_CMD_INLINE}, {NULL,NULL,0,0} }; - + static struct redisFunctionSym symsTable[] = { + {"freeStringObject", (long)freeStringObject}, + {"freeListObject", (long)freeListObject}, + {"freeSetObject", (long)freeSetObject}, + {"decrRefCount", (long)decrRefCount}, + {"createObject", (long)createObject}, + {"freeClient", (long)freeClient}, + {"rdbLoad", (long)rdbLoad}, + {"addReply", (long)addReply}, + {"addReplySds", (long)addReplySds}, + {"incrRefCount", (long)incrRefCount}, + {"rdbSaveBackground", (long)rdbSaveBackground}, + {"createStringObject", (long)createStringObject}, + {"replicationFeedSlaves", (long)replicationFeedSlaves}, + {"syncWithMaster", (long)syncWithMaster}, + {"tryObjectSharing", (long)tryObjectSharing}, + {"removeExpire", (long)removeExpire}, + {"expireIfNeeded", (long)expireIfNeeded}, + {"deleteIfVolatile", (long)deleteIfVolatile}, + {"deleteKey", (long)deleteKey}, + {"getExpire", (long)getExpire}, + {"setExpire", (long)setExpire}, + {"updateSalvesWaitingBgsave", (long)updateSalvesWaitingBgsave}, + {"freeMemoryIfNeeded", (long)freeMemoryIfNeeded}, + {"authCommand", (long)authCommand}, + {"pingCommand", (long)pingCommand}, + {"echoCommand", (long)echoCommand}, + {"setCommand", (long)setCommand}, + {"setnxCommand", (long)setnxCommand}, + {"getCommand", (long)getCommand}, + {"delCommand", (long)delCommand}, + {"existsCommand", (long)existsCommand}, + {"incrCommand", (long)incrCommand}, + {"decrCommand", (long)decrCommand}, + {"incrbyCommand", (long)incrbyCommand}, + {"decrbyCommand", (long)decrbyCommand}, + {"selectCommand", (long)selectCommand}, + {"randomkeyCommand", (long)randomkeyCommand}, + {"keysCommand", (long)keysCommand}, + {"dbsizeCommand", (long)dbsizeCommand}, + {"lastsaveCommand", (long)lastsaveCommand}, + {"saveCommand", (long)saveCommand}, + {"bgsaveCommand", (long)bgsaveCommand}, + {"shutdownCommand", (long)shutdownCommand}, + {"moveCommand", (long)moveCommand}, + {"renameCommand", (long)renameCommand}, + {"renamenxCommand", (long)renamenxCommand}, + {"lpushCommand", (long)lpushCommand}, + {"rpushCommand", (long)rpushCommand}, + {"lpopCommand", (long)lpopCommand}, + {"rpopCommand", (long)rpopCommand}, + {"llenCommand", (long)llenCommand}, + {"lindexCommand", (long)lindexCommand}, + {"lrangeCommand", (long)lrangeCommand}, + {"ltrimCommand", (long)ltrimCommand}, + {"typeCommand", (long)typeCommand}, + {"lsetCommand", (long)lsetCommand}, + {"saddCommand", (long)saddCommand}, + {"sremCommand", (long)sremCommand}, + {"smoveCommand", (long)smoveCommand}, + {"sismemberCommand", (long)sismemberCommand}, + {"scardCommand", (long)scardCommand}, + {"sinterCommand", (long)sinterCommand}, + {"sinterstoreCommand", (long)sinterstoreCommand}, + {"sunionCommand", (long)sunionCommand}, + {"sunionstoreCommand", (long)sunionstoreCommand}, + {"sdiffCommand", (long)sdiffCommand}, + {"sdiffstoreCommand", (long)sdiffstoreCommand}, + {"syncCommand", (long)syncCommand}, + {"flushdbCommand", (long)flushdbCommand}, + {"flushallCommand", (long)flushallCommand}, + {"sortCommand", (long)sortCommand}, + {"lremCommand", (long)lremCommand}, + {"infoCommand", (long)infoCommand}, + {"mgetCommand", (long)mgetCommand}, + {"monitorCommand", (long)monitorCommand}, + {"expireCommand", (long)expireCommand}, + {"getSetCommand", (long)getSetCommand}, + {"ttlCommand", (long)ttlCommand}, + {"slaveofCommand", (long)slaveofCommand}, + {"debugCommand", (long)debugCommand}, + {"processCommand", (long)processCommand}, + {"setupSigSegvAction", (long)setupSigSegvAction}, + {NULL,0} + }; /*============================ Utility functions ============================ */ /* Glob-style pattern matching. */ @@@ -750,21 -841,16 +845,21 @@@ int serverCron(struct aeEventLoop *even /* XXX: TODO handle the case of the saving child killed */ if (wait4(-1,&statloc,WNOHANG,NULL)) { int exitcode = WEXITSTATUS(statloc); - if (exitcode == 0) { + int bysignal = WIFSIGNALED(statloc); + + if (!bysignal && exitcode == 0) { redisLog(REDIS_NOTICE, "Background saving terminated with success"); server.dirty = 0; server.lastsave = time(NULL); + } else if (!bysignal && exitcode != 0) { + redisLog(REDIS_WARNING, "Background saving error"); } else { redisLog(REDIS_WARNING, - "Background saving error"); + "Background saving terminated by signal"); } server.bgsaveinprogress = 0; + server.bgsavechildpid = -1; updateSalvesWaitingBgsave(exitcode == 0 ? REDIS_OK : REDIS_ERR); } } else { @@@ -902,6 -988,7 +997,7 @@@ static void initServer() signal(SIGHUP, SIG_IGN); signal(SIGPIPE, SIG_IGN); + setupSigSegvAction(); server.clients = listCreate(); server.slaves = listCreate(); @@@ -926,7 -1013,6 +1022,7 @@@ } server.cronloops = 0; server.bgsaveinprogress = 0; + server.bgsavechildpid = -1; server.lastsave = time(NULL); server.dirty = 0; server.usedmemory = 0; @@@ -1178,7 -1264,6 +1274,7 @@@ static void sendReplyToClient(aeEventLo } if (c->flags & REDIS_MASTER) { + /* Don't reply to a master */ nwritten = objlen - c->sentlen; } else { nwritten = write(fd, ((char*)o->ptr)+c->sentlen, objlen - c->sentlen); @@@ -1191,12 -1276,6 +1287,12 @@@ listDelNode(c->reply,listFirst(c->reply)); c->sentlen = 0; } + /* Note that we avoid to send more thank REDIS_MAX_WRITE_PER_EVENT + * bytes, in a single threaded server it's a good idea to server + * other clients as well, even if a very large request comes from + * super fast link that is always able to accept data (in real world + * terms think to 'KEYS *' against the loopback interfae) */ + if (totwritten > REDIS_MAX_WRITE_PER_EVENT) break; } if (nwritten == -1) { if (errno == EAGAIN) { @@@ -1998,7 -2077,6 +2094,7 @@@ static int rdbSaveBackground(char *file } redisLog(REDIS_NOTICE,"Background saving started by pid %d",childpid); server.bgsaveinprogress = 1; + server.bgsavechildpid = childpid; return REDIS_OK; } return REDIS_OK; /* unreached */ @@@ -2521,19 -2599,15 +2617,19 @@@ static void bgsaveCommand(redisClient * static void shutdownCommand(redisClient *c) { redisLog(REDIS_WARNING,"User requested shutdown, saving DB..."); - /* XXX: TODO kill the child if there is a bgsave in progress */ + if (server.bgsaveinprogress) { + redisLog(REDIS_WARNING,"There is a live saving child. Killing it!"); + signal(SIGCHLD, SIG_IGN); + kill(server.bgsavechildpid,SIGKILL); + } if (rdbSave(server.dbfilename) == REDIS_OK) { - if (server.daemonize) { + if (server.daemonize) unlink(server.pidfile); - } redisLog(REDIS_WARNING,"%zu bytes used at exit",zmalloc_used_memory()); redisLog(REDIS_WARNING,"Server exit now, bye bye..."); exit(1); } else { + signal(SIGCHLD, SIG_DFL); redisLog(REDIS_WARNING,"Error trying to save the DB, can't exit"); addReplySds(c,sdsnew("-ERR can't quit, problems saving the DB\r\n")); } @@@ -2880,7 -2954,7 +2976,7 @@@ static void lremCommand(redisClient *c o = lookupKeyWrite(c->db,c->argv[1]); if (o == NULL) { - addReply(c,shared.nokeyerr); + addReply(c,shared.czero); } else { if (o->type != REDIS_LIST) { addReply(c,shared.wrongtypeerr); @@@ -3888,7 -3962,7 +3984,7 @@@ static void updateSalvesWaitingBgsave(i startbgsave = 1; slave->replstate = REDIS_REPL_WAIT_BGSAVE_END; } else if (slave->replstate == REDIS_REPL_WAIT_BGSAVE_END) { - struct stat buf; + struct redis_stat buf; if (bgsaveerr != REDIS_OK) { freeClient(slave); @@@ -3896,7 -3970,7 +3992,7 @@@ continue; } if ((slave->repldbfd = open(server.dbfilename,O_RDONLY)) == -1 || - fstat(slave->repldbfd,&buf) == -1) { + redis_fstat(slave->repldbfd,&buf) == -1) { freeClient(slave); redisLog(REDIS_WARNING,"SYNC failed. Can't open/stat DB after BGSAVE: %s", strerror(errno)); continue; @@@ -4096,7 -4170,94 +4192,94 @@@ static void debugCommand(redisClient *c "-ERR Syntax error, try DEBUG [SEGFAULT|OBJECT ]\r\n")); } } - + char *findFuncName(void *pointer, long *offset){ + int i, ret=-1; + long val, off; + for(i=0; symsTable[i].pointer!=0; i++){ + val=(long)pointer-symsTable[i].pointer; + if(val>=0 && (off<0 || val <= off)){ + off=val; + ret=i; + } + } + if(ret<0) + *offset=0; + else + *offset=off; + return ret>=0?symsTable[ret].name:"unknown"; + } + + static void segvHandler (int sig, siginfo_t *info, void *secret) { + + void *trace[100]; + char **messages = (char **)NULL; + char *tmp; + int i, trace_size = 0; + long offset=0; + ucontext_t *uc = (ucontext_t *)secret; + time_t uptime = time(NULL)-server.stat_starttime; + + redisLog(REDIS_WARNING, "application: redis, signal: segmentation fault -%d-",REDIS_VERSION, sig); + redisLog(REDIS_WARNING, "%s", sdscatprintf(sdsempty(), + "redis_version:%s; " + "uptime_in_days:%d; " + "connected_clients:%d; " + "connected_slaves:%d; " + "used_memory:%zu; " + "changes_since_last_save:%lld; " + "bgsave_in_progress:%d; " + "last_save_time:%d; " + "total_connections_received:%lld; " + "total_commands_processed:%lld; " + "role:%s;" + ,REDIS_VERSION, + uptime, + listLength(server.clients)-listLength(server.slaves), + listLength(server.slaves), + server.usedmemory, + server.dirty, + server.bgsaveinprogress, + server.lastsave, + server.stat_numconnections, + server.stat_numcommands, + server.masterhost == NULL ? "master" : "slave" + )); + + redisLog(REDIS_WARNING,"EIP %p", (void *)uc->uc_mcontext.gregs[REG_EIP]); + redisLog(REDIS_WARNING,"EAX %p, EBX %p, ECX %p, EDX %p", (void *)uc->uc_mcontext.gregs[REG_EAX], (void *)uc->uc_mcontext.gregs[REG_EBX], (void *)uc->uc_mcontext.gregs[REG_ECX], (void *)uc->uc_mcontext.gregs[REG_EDX]); + + + trace_size = backtrace(trace, 100); + char pointer[trace_size][11]; + /* overwrite sigaction with caller's address */ + trace[1] = (void *) uc->uc_mcontext.gregs[REG_EIP]; + for (i=1; i