X-Git-Url: https://git.saurik.com/redis.git/blobdiff_plain/8562798308391d489016b3995d438b6187b5980a..ab551808832a9f3097861a706b2c2f57302a3992:/src/db.c diff --git a/src/db.c b/src/db.c index a0775af9..e78b0d53 100644 --- a/src/db.c +++ b/src/db.c @@ -10,28 +10,6 @@ void SlotToKeyDel(robj *key); * C-level DB API *----------------------------------------------------------------------------*/ -/* Important notes on lookup and disk store. - * - * When disk store is enabled on lookup we can have different cases. - * - * a) The key is in memory: - * - If the key is not in IO_SAVEINPROG state we can access it. - * As if it's just IO_SAVE this means we have the key in the IO queue - * but can't be accessed by the IO thread (it requires to be - * translated into an IO Job by the cache cron function.) - * - If the key is in IO_SAVEINPROG we can't touch the key and have - * to blocking wait completion of operations. - * b) The key is not in memory: - * - If it's marked as non existing on disk as well (negative cache) - * we don't need to perform the disk access. - * - if the key MAY EXIST, but is not in memory, and it is marked as IO_SAVE - * then the key can only be a deleted one. As IO_SAVE keys are never - * evicted (dirty state), so the only possibility is that key was deleted. - * - if the key MAY EXIST we need to blocking load it. - * We check that the key is not in IO_SAVEINPROG state before accessing - * the disk object. If it is in this state, we wait. - */ - robj *lookupKey(redisDb *db, robj *key) { dictEntry *de = dictFind(db->dict,key->ptr); if (de) { @@ -86,7 +64,6 @@ void dbAdd(redisDb *db, robj *key, robj *val) { int retval = dictAdd(db->dict, copy, val); redisAssertWithInfo(NULL,key,retval == REDIS_OK); - if (server.cluster_enabled) SlotToKeyAdd(key); } /* Overwrite an existing key with a new value. Incrementing the reference @@ -154,15 +131,12 @@ int dbDelete(redisDb *db, robj *key) { * the key, because it is shared with the main dictionary. */ if (dictSize(db->expires) > 0) dictDelete(db->expires,key->ptr); if (dictDelete(db->dict,key->ptr) == DICT_OK) { - if (server.cluster_enabled) SlotToKeyDel(key); return 1; } else { return 0; } } -/* Empty the whole database. - * If diskstore is enabled this function will just flush the in-memory cache. */ long long emptyDb() { int j; long long removed = 0; @@ -252,12 +226,12 @@ void existsCommand(redisClient *c) { } void selectCommand(redisClient *c) { - int id = atoi(c->argv[1]->ptr); + long id; - if (server.cluster_enabled && id != 0) { - addReplyError(c,"SELECT is not allowed in cluster mode"); + if (getLongFromObjectOrReply(c, c->argv[1], &id, + "invalid DB index") != REDIS_OK) return; - } + if (selectDb(c,id) == REDIS_ERR) { addReplyError(c,"invalid DB index"); } else { @@ -285,7 +259,7 @@ void keysCommand(redisClient *c) { unsigned long numkeys = 0; void *replylen = addDeferredMultiBulkLength(c); - di = dictGetIterator(c->db->dict); + di = dictGetSafeIterator(c->db->dict); allkeys = (pattern[0] == '*' && pattern[1] == '\0'); while((de = dictNext(di)) != NULL) { sds key = dictGetKey(de); @@ -398,11 +372,6 @@ void moveCommand(redisClient *c) { redisDb *src, *dst; int srcid; - if (server.cluster_enabled) { - addReplyError(c,"MOVE is not allowed in cluster mode"); - return; - } - /* Obtain source and target DB pointers */ src = c->db; srcid = c->db->id; @@ -518,7 +487,7 @@ int expireIfNeeded(redisDb *db, robj *key) { * that is, 0 if we think the key should be still valid, 1 if * we think the key is expired at this time. */ if (server.masterhost != NULL) { - return time(NULL) > when; + return mstime() > when; } /* Return when this key has not expired */ @@ -534,24 +503,23 @@ int expireIfNeeded(redisDb *db, robj *key) { * Expires Commands *----------------------------------------------------------------------------*/ -/* Given an string object return true if it contains exactly the "ms" - * or "MS" string. This is used in order to check if the last argument - * of EXPIRE, EXPIREAT or TTL is "ms" to switch into millisecond input/output */ -int stringObjectEqualsMs(robj *a) { - char *arg = a->ptr; - return tolower(arg[0]) == 'm' && tolower(arg[1]) == 's' && arg[2] == '\0'; -} - -void expireGenericCommand(redisClient *c, long long offset, int unit) { +/* This is the generic command implementation for EXPIRE, PEXPIRE, EXPIREAT + * and PEXPIREAT. Because the commad second argument may be relative or absolute + * the "basetime" argument is used to signal what the base time is (either 0 + * for *AT variants of the command, or the current time for relative expires). + * + * unit is either UNIT_SECONDS or UNIT_MILLISECONDS, and is only used for + * the argv[2] parameter. The basetime is always specified in milliesconds. */ +void expireGenericCommand(redisClient *c, long long basetime, int unit) { dictEntry *de; robj *key = c->argv[1], *param = c->argv[2]; - long long milliseconds; + long long when; /* unix time in milliseconds when the key will expire. */ - if (getLongLongFromObjectOrReply(c, param, &milliseconds, NULL) != REDIS_OK) + if (getLongLongFromObjectOrReply(c, param, &when, NULL) != REDIS_OK) return; - if (unit == UNIT_SECONDS) milliseconds *= 1000; - milliseconds -= offset; + if (unit == UNIT_SECONDS) when *= 1000; + when += basetime; de = dictFind(c->db->dict,key->ptr); if (de == NULL) { @@ -564,7 +532,7 @@ void expireGenericCommand(redisClient *c, long long offset, int unit) { * * Instead we take the other branch of the IF statement setting an expire * (possibly in the past) and wait for an explicit DEL from the master. */ - if (milliseconds <= 0 && !server.loading && !server.masterhost) { + if (when <= mstime() && !server.loading && !server.masterhost) { robj *aux; redisAssertWithInfo(c,key,dbDelete(c->db,key)); @@ -578,7 +546,6 @@ void expireGenericCommand(redisClient *c, long long offset, int unit) { addReply(c, shared.cone); return; } else { - long long when = mstime()+milliseconds; setExpire(c->db,key,when); addReply(c,shared.cone); signalModifiedKey(c->db,key); @@ -588,19 +555,19 @@ void expireGenericCommand(redisClient *c, long long offset, int unit) { } void expireCommand(redisClient *c) { - expireGenericCommand(c,0,UNIT_SECONDS); + expireGenericCommand(c,mstime(),UNIT_SECONDS); } void expireatCommand(redisClient *c) { - expireGenericCommand(c,mstime(),UNIT_SECONDS); + expireGenericCommand(c,0,UNIT_SECONDS); } void pexpireCommand(redisClient *c) { - expireGenericCommand(c,0,UNIT_MILLISECONDS); + expireGenericCommand(c,mstime(),UNIT_MILLISECONDS); } void pexpireatCommand(redisClient *c) { - expireGenericCommand(c,mstime(),UNIT_MILLISECONDS); + expireGenericCommand(c,0,UNIT_MILLISECONDS); } void ttlGenericCommand(redisClient *c, int output_ms) { @@ -714,35 +681,3 @@ int *zunionInterGetKeys(struct redisCommand *cmd,robj **argv, int argc, int *num *numkeys = num; return keys; } - -/* Slot to Key API. This is used by Redis Cluster in order to obtain in - * a fast way a key that belongs to a specified hash slot. This is useful - * while rehashing the cluster. */ -void SlotToKeyAdd(robj *key) { - unsigned int hashslot = keyHashSlot(key->ptr,sdslen(key->ptr)); - - zslInsert(server.cluster.slots_to_keys,hashslot,key); - incrRefCount(key); -} - -void SlotToKeyDel(robj *key) { - unsigned int hashslot = keyHashSlot(key->ptr,sdslen(key->ptr)); - - zslDelete(server.cluster.slots_to_keys,hashslot,key); -} - -unsigned int GetKeysInSlot(unsigned int hashslot, robj **keys, unsigned int count) { - zskiplistNode *n; - zrangespec range; - int j = 0; - - range.min = range.max = hashslot; - range.minex = range.maxex = 0; - - n = zslFirstInRange(server.cluster.slots_to_keys, range); - while(n && n->score == hashslot && count--) { - keys[j++] = n->obj; - n = n->level[0].forward; - } - return j; -}