Compare commits

...
4 changed files with 107 additions and 28 deletions
+8
View File
@@ -123,6 +123,10 @@ void loadServerConfig(char *filename) {
server.maxclients = atoi(argv[1]);
} else if (!strcasecmp(argv[0],"maxmemory") && argc == 2) {
server.maxmemory = memtoll(argv[1],NULL);
} else if (!strcasecmp(argv[0],"maxmemory_margin") && argc == 2) {
server.maxmemory_margin = memtoll(argv[1],NULL);
} else if (!strcasecmp(argv[0],"memory_pressure_selection") && argc == 2) {
server.memory_pressure_selection = atoi(argv[1]);
} else if (!strcasecmp(argv[0],"slaveof") && argc == 3) {
server.masterhost = sdsnew(argv[1]);
server.masterport = atoi(argv[2]);
@@ -141,6 +145,10 @@ void loadServerConfig(char *filename) {
if ((server.activerehashing = yesnotoi(argv[1])) == -1) {
err = "argument must be 'yes' or 'no'"; goto loaderr;
}
} else if (!strcasecmp(argv[0],"dumpcore") && argc == 2) {
if ((server.dumpcore = yesnotoi(argv[1])) == -1) {
err = "argument must be 'yes' or 'no'"; goto loaderr;
}
} else if (!strcasecmp(argv[0],"daemonize") && argc == 2) {
if ((server.daemonize = yesnotoi(argv[1])) == -1) {
err = "argument must be 'yes' or 'no'"; goto loaderr;
+47 -8
View File
@@ -217,17 +217,23 @@ void randomkeyCommand(redisClient *c) {
decrRefCount(key);
}
void keysCommand(redisClient *c) {
static void addToReply(void *context, robj *keyobj) {
redisClient *c = (redisClient *)context;
addReplyBulk(c,keyobj);
}
static void addToList(void *context, robj *keyobj) {
robj *lobj = (robj *)context;
listTypePush(lobj,keyobj,REDIS_TAIL);
}
static unsigned long scanKeys(redisDb *db, sds pattern, void (*callback)(void *, robj *), void *context) {
dictIterator *di;
dictEntry *de;
sds pattern = c->argv[1]->ptr;
int plen = sdslen(pattern);
unsigned long numkeys = 0;
robj *lenobj = createObject(REDIS_STRING,NULL);
di = dictGetIterator(c->db->dict);
addReply(c,lenobj);
decrRefCount(lenobj);
di = dictGetIterator(db->dict);
while((de = dictNext(di)) != NULL) {
sds key = dictGetEntryKey(de);
robj *keyobj;
@@ -235,17 +241,50 @@ void keysCommand(redisClient *c) {
if ((pattern[0] == '*' && pattern[1] == '\0') ||
stringmatchlen(pattern,plen,key,sdslen(key),0)) {
keyobj = createStringObject(key,sdslen(key));
if (expireIfNeeded(c->db,keyobj) == 0) {
addReplyBulk(c,keyobj);
if (expireIfNeeded(db,keyobj) == 0) {
callback(context,keyobj);
numkeys++;
}
decrRefCount(keyobj);
}
}
dictReleaseIterator(di);
return numkeys;
}
void keysCommand(redisClient *c) {
sds pattern = c->argv[1]->ptr;
robj *lenobj = createObject(REDIS_STRING,NULL);
unsigned long numkeys;
addReply(c,lenobj);
decrRefCount(lenobj);
numkeys = scanKeys(c->db,pattern,addToReply,c);
lenobj->ptr = sdscatprintf(sdsempty(),"*%lu\r\n",numkeys);
}
void keystolistCommand(redisClient *c) {
sds pattern = c->argv[1]->ptr;
robj *key = c->argv[2];
robj *lobj = lookupKeyWrite(c->db,key);
unsigned long numkeys;
if (lobj != NULL) {
if (lobj->type != REDIS_LIST) {
addReply(c,shared.wrongtypeerr);
return;
}
dbDelete(c->db,key);
}
lobj = createZiplistObject();
dbAdd(c->db,key,lobj);
numkeys = scanKeys(c->db,pattern,addToList,lobj);
addReplyLongLong(c,numkeys);
touchWatchedKey(c->db,key);
server.dirty++;
}
void dbsizeCommand(redisClient *c) {
addReplySds(c,
sdscatprintf(sdsempty(),":%lu\r\n",dictSize(c->db->dict)));
+45 -20
View File
@@ -150,6 +150,7 @@ struct redisCommand readonlyCommandTable[] = {
{"expire",expireCommand,3,REDIS_CMD_INLINE,NULL,0,0,0},
{"expireat",expireatCommand,3,REDIS_CMD_INLINE,NULL,0,0,0},
{"keys",keysCommand,2,REDIS_CMD_INLINE,NULL,0,0,0},
{"keystolist",keystolistCommand,3,REDIS_CMD_INLINE,NULL,0,0,0},
{"dbsize",dbsizeCommand,1,REDIS_CMD_INLINE,NULL,0,0,0},
{"auth",authCommand,2,REDIS_CMD_INLINE,NULL,0,0,0},
{"ping",pingCommand,1,REDIS_CMD_INLINE,NULL,0,0,0},
@@ -470,6 +471,7 @@ void activeExpireCycle(void) {
dbDelete(db,keyobj);
decrRefCount(keyobj);
expired++;
db->expired_count++;
server.stat_expiredkeys++;
}
}
@@ -501,7 +503,7 @@ int serverCron(struct aeEventLoop *eventLoop, long long id, void *clientData) {
* This is not precise but we don't need at all precision, but just
* something statistically reasonable.
*/
server.lruclock = (time(NULL)/60)&((1<<21)-1);
server.lruclock = (server.unixtime/60)&((1<<21)-1);
/* We received a SIGTERM, shutting down here in a safe way, as it is
* not ok doing so inside the signal handler. */
@@ -729,9 +731,12 @@ void initServerConfig() {
server.requirepass = NULL;
server.rdbcompression = 1;
server.activerehashing = 1;
server.dumpcore = 0;
server.maxclients = 0;
server.blpop_blocked_clients = 0;
server.maxmemory = 0;
server.maxmemory_margin = 0;
server.memory_pressure_selection = 3;
server.vm_enabled = 0;
server.vm_swap_file = zstrdup("/tmp/redis-%p.vm");
server.vm_page_size = 256; /* 256 bytes per page */
@@ -770,7 +775,10 @@ void initServer() {
signal(SIGHUP, SIG_IGN);
signal(SIGPIPE, SIG_IGN);
setupSigSegvAction();
if (!server.dumpcore) {
setupSigSegvAction();
}
setupSigTermAction();
server.mainthread = pthread_self();
server.devnull = fopen("/dev/null","w");
@@ -798,6 +806,8 @@ void initServer() {
if (server.vm_enabled)
server.db[j].io_keys = dictCreate(&keylistDictType,NULL);
server.db[j].id = j;
server.db[j].expired_count = 0;
server.db[j].prematurely_expired_count = 0;
}
server.pubsub_channels = dictCreate(&keylistDictType,NULL);
server.pubsub_patterns = listCreate();
@@ -1076,18 +1086,22 @@ int prepareForShutdown() {
if (server.vm_enabled) unlink(server.vm_swap_file);
} else {
/* Snapshotting. Perform a SYNC SAVE and exit */
if (rdbSave(server.dbfilename) == REDIS_OK) {
if (server.daemonize)
unlink(server.pidfile);
redisLog(REDIS_WARNING,"%zu bytes used at exit",zmalloc_used_memory());
if (server.saveparamslen == 0) {
redisLog(REDIS_WARNING,"Not saving DB.");
} else {
/* Ooops.. error saving! The best we can do is to continue
* operating. Note that if there was a background saving process,
* in the next cron() Redis will be notified that the background
* saving aborted, handling special stuff like slaves pending for
* synchronization... */
redisLog(REDIS_WARNING,"Error trying to save the DB, can't exit");
return REDIS_ERR;
if (rdbSave(server.dbfilename) == REDIS_OK) {
if (server.daemonize)
unlink(server.pidfile);
redisLog(REDIS_WARNING,"%zu bytes used at exit",zmalloc_used_memory());
} else {
/* Ooops.. error saving! The best we can do is to continue
* operating. Note that if there was a background saving process,
* in the next cron() Redis will be notified that the background
* saving aborted, handling special stuff like slaves pending for
* synchronization... */
redisLog(REDIS_WARNING,"Error trying to save the DB, can't exit");
return REDIS_ERR;
}
}
}
redisLog(REDIS_WARNING,"Server exit now, bye bye...");
@@ -1248,8 +1262,8 @@ sds genRedisInfoString(void) {
keys = dictSize(server.db[j].dict);
vkeys = dictSize(server.db[j].expires);
if (keys || vkeys) {
info = sdscatprintf(info, "db%d:keys=%lld,expires=%lld\r\n",
j, keys, vkeys);
info = sdscatprintf(info, "db%d:keys=%lld,expires=%lld,expired=%lu,premature=%lu\r\n",
j, keys, vkeys, server.db[j].expired_count, server.db[j].prematurely_expired_count);
}
}
return info;
@@ -1308,20 +1322,20 @@ int tryFreeOneObjectFromFreelist(void) {
* memory usage.
*/
void freeMemoryIfNeeded(void) {
while (server.maxmemory && zmalloc_used_memory() > server.maxmemory) {
while (server.maxmemory && zmalloc_used_memory() + server.maxmemory_margin > server.maxmemory) {
int j, k, freed = 0;
if (tryFreeOneObjectFromFreelist() == REDIS_OK) continue;
for (j = 0; j < server.dbnum; j++) {
int minttl = -1;
robj *minkey = NULL;
sds minkey = 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++) {
for (k = 0; k < server.memory_pressure_selection; k++) {
time_t t;
de = dictGetRandomKey(server.db[j].expires);
@@ -1331,7 +1345,12 @@ void freeMemoryIfNeeded(void) {
minttl = t;
}
}
dbDelete(server.db+j,minkey);
if (dictDelete(server.db[j].expires,minkey) == DICT_OK &&
dictDelete(server.db[j].dict,minkey) == DICT_OK) {
server.db[j].expired_count++;
server.db[j].prematurely_expired_count++;
}
}
}
if (!freed) return; /* nothing to free... */
@@ -1515,16 +1534,22 @@ void setupSigSegvAction(void) {
sigaction (SIGFPE, &act, NULL);
sigaction (SIGILL, &act, NULL);
sigaction (SIGBUS, &act, NULL);
}
void setupSigTermAction(void) {
struct sigaction act;
sigemptyset (&act.sa_mask);
act.sa_flags = SA_NODEFER | SA_ONSTACK | SA_RESETHAND;
act.sa_handler = sigtermHandler;
sigaction (SIGTERM, &act, NULL);
return;
}
#else /* HAVE_BACKTRACE */
void setupSigSegvAction(void) {
}
void setupSigTermAction(void) {
}
#endif /* HAVE_BACKTRACE */
/* The End */
+7
View File
@@ -260,6 +260,8 @@ typedef struct redisDb {
dict *io_keys; /* Keys with clients waiting for VM I/O */
dict *watched_keys; /* WATCHED keys for MULTI/EXEC CAS */
int id;
unsigned long expired_count;
unsigned long prematurely_expired_count;
} redisDb;
/* Client MULTI/EXEC state */
@@ -371,6 +373,7 @@ struct redisServer {
char *requirepass;
int rdbcompression;
int activerehashing;
int dumpcore;
/* Replication related */
int isslave;
char *masterauth;
@@ -380,6 +383,8 @@ struct redisServer {
int replstate;
unsigned int maxclients;
unsigned long long maxmemory;
unsigned long long maxmemory_margin; /* bytes to keep free for new data */
int memory_pressure_selection; /* # of candidates to choose from in freeMemoryIfNeeded(). */
unsigned int blpop_blocked_clients;
unsigned int vm_blocked_clients;
/* Sort parameters - qsort_r() is only available under BSD so we
@@ -680,6 +685,7 @@ void zslInsert(zskiplist *zsl, double score, robj *obj);
void freeMemoryIfNeeded(void);
int processCommand(redisClient *c);
void setupSigSegvAction(void);
void setupSigTermAction(void);
struct redisCommand *lookupCommand(char *name);
void call(redisClient *c, struct redisCommand *cmd);
int prepareForShutdown();
@@ -790,6 +796,7 @@ void decrbyCommand(redisClient *c);
void selectCommand(redisClient *c);
void randomkeyCommand(redisClient *c);
void keysCommand(redisClient *c);
void keystolistCommand(redisClient *c);
void dbsizeCommand(redisClient *c);
void lastsaveCommand(redisClient *c);
void saveCommand(redisClient *c);