From: antirez Date: Thu, 10 Nov 2011 16:52:02 +0000 (+0100) Subject: high resolution expires API modified to use separated commands. AOF transation to... X-Git-Url: https://git.saurik.com/redis.git/commitdiff_plain/12d293ca6edab6cc94cbd90840c629a535a12aef high resolution expires API modified to use separated commands. AOF transation to PEXPIREAT of all the expire-style commands fixed. --- diff --git a/src/aof.c b/src/aof.c index 6706d554..8f3f5d59 100644 --- a/src/aof.c +++ b/src/aof.c @@ -181,21 +181,38 @@ sds catAppendOnlyGenericCommand(sds dst, int argc, robj **argv) { return dst; } -sds catAppendOnlyExpireAtCommand(sds buf, robj *key, robj *seconds) { - int argc = 3; - long when; +/* Create the sds representation of an PEXPIREAT command, using + * 'seconds' as time to live and 'cmd' to understand what command + * we are translating into a PEXPIREAT. + * + * This command is used in order to translate EXPIRE and PEXPIRE commands + * into PEXPIREAT command so that we retain precision in the append only + * file, and the time is always absolute and not relative. */ +sds catAppendOnlyExpireAtCommand(sds buf, struct redisCommand *cmd, robj *key, robj *seconds) { + long long when; robj *argv[3]; /* Make sure we can use strtol */ seconds = getDecodedObject(seconds); - when = time(NULL)+strtol(seconds->ptr,NULL,10); + when = strtoll(seconds->ptr,NULL,10); + /* Convert argument into milliseconds for EXPIRE, SETEX, EXPIREAT */ + if (cmd->proc == expireCommand || cmd->proc == setexCommand || + cmd->proc == expireatCommand) + { + when *= 1000; + } + /* Convert into absolute time for EXPIRE, PEXPIRE, SETEX, PSETEX */ + if (cmd->proc == expireCommand || cmd->proc == pexpireCommand || + cmd->proc == setexCommand || cmd->proc == psetexCommand) + { + when += mstime(); + } decrRefCount(seconds); - argv[0] = createStringObject("EXPIREAT",8); + argv[0] = createStringObject("PEXPIREAT",9); argv[1] = key; - argv[2] = createObject(REDIS_STRING, - sdscatprintf(sdsempty(),"%ld",when)); - buf = catAppendOnlyGenericCommand(buf, argc, argv); + argv[2] = createStringObjectFromLongLong(when); + buf = catAppendOnlyGenericCommand(buf, 3, argv); decrRefCount(argv[0]); decrRefCount(argv[2]); return buf; @@ -216,18 +233,22 @@ void feedAppendOnlyFile(struct redisCommand *cmd, int dictid, robj **argv, int a server.appendseldb = dictid; } - if (cmd->proc == expireCommand) { - /* Translate EXPIRE into EXPIREAT */ - buf = catAppendOnlyExpireAtCommand(buf,argv[1],argv[2]); - } else if (cmd->proc == setexCommand) { - /* Translate SETEX to SET and EXPIREAT */ + if (cmd->proc == expireCommand || cmd->proc == pexpireCommand || + cmd->proc == expireatCommand) { + /* Translate EXPIRE/PEXPIRE/EXPIREAT into PEXPIREAT */ + buf = catAppendOnlyExpireAtCommand(buf,cmd,argv[1],argv[2]); + } else if (cmd->proc == setexCommand || cmd->proc == psetexCommand) { + /* Translate SETEX/PSETEX to SET and PEXPIREAT */ tmpargv[0] = createStringObject("SET",3); tmpargv[1] = argv[1]; tmpargv[2] = argv[3]; buf = catAppendOnlyGenericCommand(buf,3,tmpargv); decrRefCount(tmpargv[0]); - buf = catAppendOnlyExpireAtCommand(buf,argv[1],argv[2]); + buf = catAppendOnlyExpireAtCommand(buf,cmd,argv[1],argv[2]); } else { + /* All the other commands don't need translation or need the + * same translation already operated in the command vector + * for the replication itself. */ buf = catAppendOnlyGenericCommand(buf,argc,argv); } @@ -608,13 +629,12 @@ int rewriteAppendOnlyFile(char *filename) { } /* Save the expire time */ if (expiretime != -1) { - char cmd[]="*4\r\n$8\r\nEXPIREAT\r\n"; + char cmd[]="*3\r\n$9\r\nPEXPIREAT\r\n"; /* If this key is already expired skip it */ if (expiretime < now) continue; if (rioWrite(&aof,cmd,sizeof(cmd)-1) == 0) goto werr; if (rioWriteBulkObject(&aof,&key) == 0) goto werr; if (rioWriteBulkLongLong(&aof,expiretime) == 0) goto werr; - if (rioWriteBulkString(&aof,"ms",2) == 0) goto werr; } } dictReleaseIterator(di); diff --git a/src/db.c b/src/db.c index 755e295e..38983cb5 100644 --- a/src/db.c +++ b/src/db.c @@ -522,25 +522,15 @@ int stringObjectEqualsMs(robj *a) { return tolower(arg[0]) == 'm' && tolower(arg[1]) == 's' && arg[2] == '\0'; } -void expireGenericCommand(redisClient *c, long long offset) { +void expireGenericCommand(redisClient *c, long long offset, int unit) { dictEntry *de; robj *key = c->argv[1], *param = c->argv[2]; long long milliseconds; - int time_in_seconds = 1; if (getLongLongFromObjectOrReply(c, param, &milliseconds, NULL) != REDIS_OK) return; - /* If no "ms" argument was passed the time is in second, so we need - * to multilpy it by 1000 */ - if (c->argc == 4) { - if (!stringObjectEqualsMs(c->argv[3])) { - addReply(c,shared.syntaxerr); - return; - } - time_in_seconds = 0; /* "ms" argument passed. */ - } - if (time_in_seconds) milliseconds *= 1000; + if (unit == UNIT_SECONDS) milliseconds *= 1000; milliseconds -= offset; de = dictFind(c->db->dict,key->ptr); @@ -578,33 +568,23 @@ void expireGenericCommand(redisClient *c, long long offset) { } void expireCommand(redisClient *c) { - if (c->argc > 4) { - addReply(c,shared.syntaxerr); - return; - } - expireGenericCommand(c,0); + expireGenericCommand(c,0,UNIT_SECONDS); } void expireatCommand(redisClient *c) { - if (c->argc > 4) { - addReply(c,shared.syntaxerr); - return; - } - expireGenericCommand(c,mstime()); + expireGenericCommand(c,mstime(),UNIT_SECONDS); } -void ttlCommand(redisClient *c) { - long long expire, ttl = -1; - int output_ms = 0; +void pexpireCommand(redisClient *c) { + expireGenericCommand(c,0,UNIT_MILLISECONDS); +} - if (c->argc == 3) { - if (stringObjectEqualsMs(c->argv[2])) { - output_ms = 1; - } else { - addReply(c,shared.syntaxerr); - return; - } - } +void pexpireatCommand(redisClient *c) { + expireGenericCommand(c,mstime(),UNIT_MILLISECONDS); +} + +void ttlGenericCommand(redisClient *c, int output_ms) { + long long expire, ttl = -1; expire = getExpire(c->db,c->argv[1]); if (expire != -1) { @@ -618,6 +598,14 @@ void ttlCommand(redisClient *c) { } } +void ttlCommand(redisClient *c) { + ttlGenericCommand(c, 0); +} + +void pttlCommand(redisClient *c) { + ttlGenericCommand(c, 1); +} + void persistCommand(redisClient *c) { dictEntry *de; diff --git a/src/redis.c b/src/redis.c index b8853253..7d209439 100644 --- a/src/redis.c +++ b/src/redis.c @@ -91,6 +91,7 @@ struct redisCommand redisCommandTable[] = { {"set",setCommand,3,"wm",0,noPreloadGetKeys,1,1,1,0,0}, {"setnx",setnxCommand,3,"wm",0,noPreloadGetKeys,1,1,1,0,0}, {"setex",setexCommand,4,"wm",0,noPreloadGetKeys,2,2,1,0,0}, + {"psetex",psetexCommand,4,"wm",0,noPreloadGetKeys,2,2,1,0,0}, {"append",appendCommand,3,"wm",0,NULL,1,1,1,0,0}, {"strlen",strlenCommand,2,"r",0,NULL,1,1,1,0,0}, {"del",delCommand,-2,"w",0,noPreloadGetKeys,1,-1,1,0,0}, @@ -172,8 +173,10 @@ struct redisCommand redisCommandTable[] = { {"move",moveCommand,3,"w",0,NULL,1,1,1,0,0}, {"rename",renameCommand,3,"w",0,renameGetKeys,1,2,1,0,0}, {"renamenx",renamenxCommand,3,"w",0,renameGetKeys,1,2,1,0,0}, - {"expire",expireCommand,-3,"w",0,NULL,1,1,1,0,0}, - {"expireat",expireatCommand,-3,"w",0,NULL,1,1,1,0,0}, + {"expire",expireCommand,3,"w",0,NULL,1,1,1,0,0}, + {"expireat",expireatCommand,3,"w",0,NULL,1,1,1,0,0}, + {"pexpire",pexpireCommand,3,"w",0,NULL,1,1,1,0,0}, + {"pexpireat",pexpireatCommand,3,"w",0,NULL,1,1,1,0,0}, {"keys",keysCommand,2,"r",0,NULL,0,0,0,0,0}, {"dbsize",dbsizeCommand,1,"r",0,NULL,0,0,0,0,0}, {"auth",authCommand,2,"r",0,NULL,0,0,0,0,0}, @@ -194,7 +197,8 @@ struct redisCommand redisCommandTable[] = { {"sort",sortCommand,-2,"wm",0,NULL,1,1,1,0,0}, {"info",infoCommand,-1,"r",0,NULL,0,0,0,0,0}, {"monitor",monitorCommand,1,"ars",0,NULL,0,0,0,0,0}, - {"ttl",ttlCommand,-2,"r",0,NULL,1,1,1,0,0}, + {"ttl",ttlCommand,2,"r",0,NULL,1,1,1,0,0}, + {"pttl",pttlCommand,2,"r",0,NULL,1,1,1,0,0}, {"persist",persistCommand,2,"w",0,NULL,1,1,1,0,0}, {"slaveof",slaveofCommand,3,"aws",0,NULL,0,0,0,0,0}, {"debug",debugCommand,-2,"aw",0,NULL,0,0,0,0,0}, diff --git a/src/redis.h b/src/redis.h index 9ec42aa9..d660cae9 100644 --- a/src/redis.h +++ b/src/redis.h @@ -211,6 +211,10 @@ /* Scripting */ #define REDIS_LUA_TIME_LIMIT 5000 /* milliseconds */ +/* Units */ +#define UNIT_SECONDS 0 +#define UNIT_MILLISECONDS 1 + /* We can print the stacktrace, so our assert is defined this way: */ #define redisAssertWithInfo(_c,_o,_e) ((_e)?(void)0 : (_redisAssertWithInfo(_c,_o,#_e,__FILE__,__LINE__),_exit(1))) #define redisAssert(_e) ((_e)?(void)0 : (_redisAssert(#_e,__FILE__,__LINE__),_exit(1))) @@ -988,6 +992,7 @@ void echoCommand(redisClient *c); void setCommand(redisClient *c); void setnxCommand(redisClient *c); void setexCommand(redisClient *c); +void psetexCommand(redisClient *c); void getCommand(redisClient *c); void delCommand(redisClient *c); void existsCommand(redisClient *c); @@ -1048,8 +1053,11 @@ void mgetCommand(redisClient *c); void monitorCommand(redisClient *c); void expireCommand(redisClient *c); void expireatCommand(redisClient *c); +void pexpireCommand(redisClient *c); +void pexpireatCommand(redisClient *c); void getsetCommand(redisClient *c); void ttlCommand(redisClient *c); +void pttlCommand(redisClient *c); void persistCommand(redisClient *c); void slaveofCommand(redisClient *c); void debugCommand(redisClient *c); diff --git a/src/t_string.c b/src/t_string.c index aea58872..ce6233a9 100644 --- a/src/t_string.c +++ b/src/t_string.c @@ -12,16 +12,17 @@ static int checkStringLength(redisClient *c, long long size) { return REDIS_OK; } -void setGenericCommand(redisClient *c, int nx, robj *key, robj *val, robj *expire) { - long seconds = 0; /* initialized to avoid an harmness warning */ +void setGenericCommand(redisClient *c, int nx, robj *key, robj *val, robj *expire, int unit) { + long long milliseconds = 0; /* initialized to avoid an harmness warning */ if (expire) { - if (getLongFromObjectOrReply(c, expire, &seconds, NULL) != REDIS_OK) + if (getLongLongFromObjectOrReply(c, expire, &milliseconds, NULL) != REDIS_OK) return; - if (seconds <= 0) { + if (milliseconds <= 0) { addReplyError(c,"invalid expire time in SETEX"); return; } + if (unit == UNIT_SECONDS) milliseconds *= 1000; } if (lookupKeyWrite(c->db,key) != NULL && nx) { @@ -30,23 +31,28 @@ void setGenericCommand(redisClient *c, int nx, robj *key, robj *val, robj *expir } setKey(c->db,key,val); server.dirty++; - if (expire) setExpire(c->db,key,(time(NULL)+seconds)*1000); + if (expire) setExpire(c->db,key,mstime()+milliseconds); addReply(c, nx ? shared.cone : shared.ok); } void setCommand(redisClient *c) { c->argv[2] = tryObjectEncoding(c->argv[2]); - setGenericCommand(c,0,c->argv[1],c->argv[2],NULL); + setGenericCommand(c,0,c->argv[1],c->argv[2],NULL,0); } void setnxCommand(redisClient *c) { c->argv[2] = tryObjectEncoding(c->argv[2]); - setGenericCommand(c,1,c->argv[1],c->argv[2],NULL); + setGenericCommand(c,1,c->argv[1],c->argv[2],NULL,0); } void setexCommand(redisClient *c) { c->argv[3] = tryObjectEncoding(c->argv[3]); - setGenericCommand(c,0,c->argv[1],c->argv[3],c->argv[2]); + setGenericCommand(c,0,c->argv[1],c->argv[3],c->argv[2],UNIT_SECONDS); +} + +void psetexCommand(redisClient *c) { + c->argv[3] = tryObjectEncoding(c->argv[3]); + setGenericCommand(c,0,c->argv[1],c->argv[3],c->argv[2],UNIT_MILLISECONDS); } int getGenericCommand(redisClient *c) {