From 41d804d9dc48292f61fc1e1efd2241404dc9a6e8 Mon Sep 17 00:00:00 2001 From: antirez Date: Tue, 14 Jun 2016 15:33:59 +0200 Subject: [PATCH] TTL and TYPE LRU access fixed. TOUCH implemented. --- src/db.c | 61 ++++++++++++++++++++++++++++++++++++++++++++++------ src/server.c | 1 + src/server.h | 6 +++++- 3 files changed, 60 insertions(+), 8 deletions(-) diff --git a/src/db.c b/src/db.c index a7701c459..4db7d890f 100644 --- a/src/db.c +++ b/src/db.c @@ -38,7 +38,10 @@ * C-level DB API *----------------------------------------------------------------------------*/ -robj *lookupKey(redisDb *db, robj *key) { +/* Low level key lookup API, not actually called directly from commands + * implementations that should instead rely on lookupKeyRead(), + * lookupKeyWrite() and lookupKeyReadWithFlags(). */ +robj *lookupKey(redisDb *db, robj *key, int flags) { dictEntry *de = dictFind(db->dict,key->ptr); if (de) { robj *val = dictGetVal(de); @@ -46,15 +49,40 @@ robj *lookupKey(redisDb *db, robj *key) { /* Update the access time for the ageing algorithm. * Don't do it if we have a saving child, as this will trigger * a copy on write madness. */ - if (server.rdb_child_pid == -1 && server.aof_child_pid == -1) + if (server.rdb_child_pid == -1 && + server.aof_child_pid == -1 && + !(flags & LOOKUP_NOTOUCH)) + { val->lru = LRU_CLOCK(); + } return val; } else { return NULL; } } -robj *lookupKeyRead(redisDb *db, robj *key) { +/* Lookup a key for read operations, or return NULL if the key is not found + * in the specified DB. + * + * As a side effect of calling this function: + * 1. A key gets expired if it reached it's TTL. + * 2. The key last access time is updated. + * 3. The global keys hits/misses stats are updated (reported in INFO). + * + * This API should not be used when we write to the key after obtaining + * the object linked to the key, but only for read only operations. + * + * Flags change the behavior of this command: + * + * LOOKUP_NONE (or zero): no special flags are passed. + * LOOKUP_NOTOUCH: don't alter the last access time of the key. + * + * Note: this function also returns NULL is the key is logically expired + * but still existing, in case this is a slave, since this API is called only + * for read operations. Even if the key expiry is master-driven, we can + * correctly report a key is expired on slaves even if the master is lagging + * expiring our key via DELs in the replication link. */ +robj *lookupKeyReadWithFlags(redisDb *db, robj *key, int flags) { robj *val; if (expireIfNeeded(db,key) == 1) { @@ -83,7 +111,7 @@ robj *lookupKeyRead(redisDb *db, robj *key) { return NULL; } } - val = lookupKey(db,key); + val = lookupKey(db,key,flags); if (val == NULL) server.stat_keyspace_misses++; else @@ -91,9 +119,20 @@ robj *lookupKeyRead(redisDb *db, robj *key) { return val; } +/* Like lookupKeyReadWithFlags(), but does not use any flag, which is the + * common case. */ +robj *lookupKeyRead(redisDb *db, robj *key) { + return lookupKeyReadWithFlags(db,key,LOOKUP_NONE); +} + +/* Lookup a key for write operations, and as a side effect, if needed, expires + * the key if its TTL is reached. + * + * Returns the linked value object if the key exists or NULL if the key + * does not exist in the specified DB. */ robj *lookupKeyWrite(redisDb *db, robj *key) { expireIfNeeded(db,key); - return lookupKey(db,key); + return lookupKey(db,key,LOOKUP_NONE); } robj *lookupKeyReadOrReply(client *c, robj *key, robj *reply) { @@ -721,7 +760,7 @@ void typeCommand(client *c) { robj *o; char *type; - o = lookupKeyRead(c->db,c->argv[1]); + o = lookupKeyReadWithFlags(c->db,c->argv[1],LOOKUP_NOTOUCH); if (o == NULL) { type = "none"; } else { @@ -1049,7 +1088,7 @@ void ttlGenericCommand(client *c, int output_ms) { long long expire, ttl = -1; /* If the key does not exist at all, return -2 */ - if (lookupKeyRead(c->db,c->argv[1]) == NULL) { + if (lookupKeyReadWithFlags(c->db,c->argv[1],LOOKUP_NOTOUCH) == NULL) { addReplyLongLong(c,-2); return; } @@ -1091,6 +1130,14 @@ void persistCommand(client *c) { } } +/* TOUCH key1 [key2 key3 ... keyN] */ +void touchCommand(client *c) { + int touched = 0; + for (int j = 1; j < c->argc; j++) + if (lookupKeyRead(c->db,c->argv[j]) != NULL) touched++; + addReplyLongLong(c,touched); +} + /* ----------------------------------------------------------------------------- * API to get key arguments from commands * ---------------------------------------------------------------------------*/ diff --git a/src/server.c b/src/server.c index 72a237214..e2a636258 100644 --- a/src/server.c +++ b/src/server.c @@ -250,6 +250,7 @@ struct redisCommand redisCommandTable[] = { {"info",infoCommand,-1,"lt",0,NULL,0,0,0,0,0}, {"monitor",monitorCommand,1,"as",0,NULL,0,0,0,0,0}, {"ttl",ttlCommand,2,"rF",0,NULL,1,1,1,0,0}, + {"touch",touchCommand,-2,"rF",0,NULL,1,1,1,0,0}, {"pttl",pttlCommand,2,"rF",0,NULL,1,1,1,0,0}, {"persist",persistCommand,2,"wF",0,NULL,1,1,1,0,0}, {"slaveof",slaveofCommand,3,"ast",0,NULL,0,0,0,0,0}, diff --git a/src/server.h b/src/server.h index 2719eef9c..10fbf3237 100644 --- a/src/server.h +++ b/src/server.h @@ -1533,11 +1533,14 @@ void propagateExpire(redisDb *db, robj *key, int lazy); int expireIfNeeded(redisDb *db, robj *key); long long getExpire(redisDb *db, robj *key); void setExpire(redisDb *db, robj *key, long long when); -robj *lookupKey(redisDb *db, robj *key); +robj *lookupKey(redisDb *db, robj *key, int flags); robj *lookupKeyRead(redisDb *db, robj *key); robj *lookupKeyWrite(redisDb *db, robj *key); robj *lookupKeyReadOrReply(client *c, robj *key, robj *reply); robj *lookupKeyWriteOrReply(client *c, robj *key, robj *reply); +robj *lookupKeyReadWithFlags(redisDb *db, robj *key, int flags); +#define LOOKUP_NONE 0 +#define LOOKUP_NOTOUCH (1<<0) void dbAdd(redisDb *db, robj *key, robj *val); void dbOverwrite(redisDb *db, robj *key, robj *val); void setKey(redisDb *db, robj *key, robj *val); @@ -1693,6 +1696,7 @@ void pexpireCommand(client *c); void pexpireatCommand(client *c); void getsetCommand(client *c); void ttlCommand(client *c); +void touchCommand(client *c); void pttlCommand(client *c); void persistCommand(client *c); void slaveofCommand(client *c); -- GitLab