Compare commits
8
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7f8305df0f | ||
|
|
497299df83 | ||
|
|
9b48a5896a | ||
|
|
30f0fd8f9f | ||
|
|
3862529a39 | ||
|
|
79fda18d2b | ||
|
|
0bccb9f08c | ||
|
|
f737f27dd7 |
@@ -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;
|
||||
|
||||
@@ -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
@@ -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 */
|
||||
|
||||
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user