From 2eb781b35bfa9bde5ab88b192cd3e666e7872625 Mon Sep 17 00:00:00 2001 From: antirez Date: Tue, 10 Dec 2013 18:18:24 +0100 Subject: [PATCH] dict.c: added optional callback to dictEmpty(). Redis hash table implementation has many non-blocking features like incremental rehashing, however while deleting a large hash table there was no way to have a callback called to do some incremental work. This commit adds this support, as an optiona callback argument to dictEmpty() that is currently called at a fixed interval (one time every 65k deletions). --- src/db.c | 12 ++++++------ src/debug.c | 4 ++-- src/dict.c | 15 ++++++++------- src/dict.h | 2 +- src/redis.h | 2 +- src/replication.c | 4 ++-- src/sentinel.c | 2 +- src/t_list.c | 2 +- 8 files changed, 22 insertions(+), 21 deletions(-) diff --git a/src/db.c b/src/db.c index be71d2592..5bea8db8e 100644 --- a/src/db.c +++ b/src/db.c @@ -170,14 +170,14 @@ int dbDelete(redisDb *db, robj *key) { } } -long long emptyDb() { +long long emptyDb(void(callback)(void*)) { int j; long long removed = 0; for (j = 0; j < server.dbnum; j++) { removed += dictSize(server.db[j].dict); - dictEmpty(server.db[j].dict); - dictEmpty(server.db[j].expires); + dictEmpty(server.db[j].dict,callback); + dictEmpty(server.db[j].expires,callback); } if (server.cluster_enabled) slotToKeyFlush(); return removed; @@ -214,15 +214,15 @@ void signalFlushedDb(int dbid) { void flushdbCommand(redisClient *c) { server.dirty += dictSize(c->db->dict); signalFlushedDb(c->db->id); - dictEmpty(c->db->dict); - dictEmpty(c->db->expires); + dictEmpty(c->db->dict,NULL); + dictEmpty(c->db->expires,NULL); if (server.cluster_enabled) slotToKeyFlush(); addReply(c,shared.ok); } void flushallCommand(redisClient *c) { signalFlushedDb(-1); - server.dirty += emptyDb(); + server.dirty += emptyDb(NULL); addReply(c,shared.ok); if (server.rdb_child_pid != -1) { kill(server.rdb_child_pid,SIGUSR1); diff --git a/src/debug.c b/src/debug.c index e8e16cc8b..7d9a8bfe5 100644 --- a/src/debug.c +++ b/src/debug.c @@ -261,7 +261,7 @@ void debugCommand(redisClient *c) { addReply(c,shared.err); return; } - emptyDb(); + emptyDb(NULL); if (rdbLoad(server.rdb_filename) != REDIS_OK) { addReplyError(c,"Error trying to load the RDB dump"); return; @@ -269,7 +269,7 @@ void debugCommand(redisClient *c) { redisLog(REDIS_WARNING,"DB reloaded by DEBUG RELOAD"); addReply(c,shared.ok); } else if (!strcasecmp(c->argv[1]->ptr,"loadaof")) { - emptyDb(); + emptyDb(NULL); if (loadAppendOnlyFile(server.aof_filename) != REDIS_OK) { addReply(c,shared.err); return; diff --git a/src/dict.c b/src/dict.c index c6ba2b745..17a322870 100644 --- a/src/dict.c +++ b/src/dict.c @@ -444,14 +444,15 @@ int dictDeleteNoFree(dict *ht, const void *key) { } /* Destroy an entire dictionary */ -int _dictClear(dict *d, dictht *ht) -{ +int _dictClear(dict *d, dictht *ht, void(callback)(void *)) { unsigned long i; /* Free all the elements */ for (i = 0; i < ht->size && ht->used > 0; i++) { dictEntry *he, *nextHe; + if (callback && (i & 65535) == 0) callback(d->privdata); + if ((he = ht->table[i]) == NULL) continue; while(he) { nextHe = he->next; @@ -472,8 +473,8 @@ int _dictClear(dict *d, dictht *ht) /* Clear & Release the hash table */ void dictRelease(dict *d) { - _dictClear(d,&d->ht[0]); - _dictClear(d,&d->ht[1]); + _dictClear(d,&d->ht[0],NULL); + _dictClear(d,&d->ht[1],NULL); zfree(d); } @@ -882,9 +883,9 @@ static int _dictKeyIndex(dict *d, const void *key) return idx; } -void dictEmpty(dict *d) { - _dictClear(d,&d->ht[0]); - _dictClear(d,&d->ht[1]); +void dictEmpty(dict *d, void(callback)(void*)) { + _dictClear(d,&d->ht[0],callback); + _dictClear(d,&d->ht[1],callback); d->rehashidx = -1; d->iterators = 0; } diff --git a/src/dict.h b/src/dict.h index 11e1b97ee..3385e9f06 100644 --- a/src/dict.h +++ b/src/dict.h @@ -160,7 +160,7 @@ dictEntry *dictGetRandomKey(dict *d); void dictPrintStats(dict *d); unsigned int dictGenHashFunction(const void *key, int len); unsigned int dictGenCaseHashFunction(const unsigned char *buf, int len); -void dictEmpty(dict *d); +void dictEmpty(dict *d, void(callback)(void*)); void dictEnableResize(void); void dictDisableResize(void); int dictRehash(dict *d, int n); diff --git a/src/redis.h b/src/redis.h index a9b06838b..16403a8b5 100644 --- a/src/redis.h +++ b/src/redis.h @@ -1212,7 +1212,7 @@ void setKey(redisDb *db, robj *key, robj *val); int dbExists(redisDb *db, robj *key); robj *dbRandomKey(redisDb *db); int dbDelete(redisDb *db, robj *key); -long long emptyDb(); +long long emptyDb(void(callback)(void*)); int selectDb(redisClient *c, int id); void signalModifiedKey(redisDb *db, robj *key); void signalFlushedDb(int dbid); diff --git a/src/replication.c b/src/replication.c index 0b4ac16d9..5044ca331 100644 --- a/src/replication.c +++ b/src/replication.c @@ -796,7 +796,7 @@ void readSyncBulkPayload(aeEventLoop *el, int fd, void *privdata, int mask) { } redisLog(REDIS_NOTICE, "MASTER <-> SLAVE sync: Flushing old data"); signalFlushedDb(-1); - emptyDb(); + emptyDb(NULL); /* Before loading the DB into memory we need to delete the readable * handler, otherwise it will get called recursively since * rdbLoad() will call the event loop to process events from time to @@ -1468,7 +1468,7 @@ void replicationScriptCacheInit(void) { * to reclaim otherwise unused memory. */ void replicationScriptCacheFlush(void) { - dictEmpty(server.repl_scriptcache_dict); + dictEmpty(server.repl_scriptcache_dict,NULL); listRelease(server.repl_scriptcache_fifo); server.repl_scriptcache_fifo = listCreate(); } diff --git a/src/sentinel.c b/src/sentinel.c index 3bd1ee8c8..9a5012df3 100644 --- a/src/sentinel.c +++ b/src/sentinel.c @@ -391,7 +391,7 @@ void initSentinel(void) { /* Remove usual Redis commands from the command table, then just add * the SENTINEL command. */ - dictEmpty(server.commands); + dictEmpty(server.commands,NULL); for (j = 0; j < sizeof(sentinelcmds)/sizeof(sentinelcmds[0]); j++) { int retval; struct redisCommand *cmd = sentinelcmds+j; diff --git a/src/t_list.c b/src/t_list.c index 216e071ba..70f5cf164 100644 --- a/src/t_list.c +++ b/src/t_list.c @@ -835,7 +835,7 @@ void unblockClientWaitingData(redisClient *c) { dictReleaseIterator(di); /* Cleanup the client structure */ - dictEmpty(c->bpop.keys); + dictEmpty(c->bpop.keys,NULL); if (c->bpop.target) { decrRefCount(c->bpop.target); c->bpop.target = NULL;