mirror of
https://github.com/fluencelabs/redis
synced 2025-03-17 16:10:50 +00:00
high resolution expires API modified to use separated commands. AOF transation to PEXPIREAT of all the expire-style commands fixed.
This commit is contained in:
parent
dab5332f95
commit
12d293ca6e
52
src/aof.c
52
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);
|
||||
|
54
src/db.c
54
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;
|
||||
|
||||
|
10
src/redis.c
10
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},
|
||||
|
@ -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);
|
||||
|
@ -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) {
|
||||
|
Loading…
x
Reference in New Issue
Block a user