Skip to content

Commit 7ccf367

Browse files
committed
Add GETPXT (Get with millisecond expiration) command
Returns null if the value not found or expired. Returns an array of length 2 as [<string key value>, <integer expiration>]. If expiration is not set on the key, expiration returned is -1. Added tests to cover Signed-off-by: Arcadiy Ivanov <[email protected]>
1 parent ba25b58 commit 7ccf367

File tree

5 files changed

+151
-0
lines changed

5 files changed

+151
-0
lines changed

src/commands.def

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10354,6 +10354,30 @@ struct COMMAND_ARG GETEX_Args[] = {
1035410354
{MAKE_ARG("expiration",ARG_TYPE_ONEOF,-1,NULL,NULL,NULL,CMD_ARG_OPTIONAL,5,NULL),.subargs=GETEX_expiration_Subargs},
1035510355
};
1035610356

10357+
/********** GETPXT ********************/
10358+
10359+
#ifndef SKIP_CMD_HISTORY_TABLE
10360+
/* GETPXT history */
10361+
#define GETPXT_History NULL
10362+
#endif
10363+
10364+
#ifndef SKIP_CMD_TIPS_TABLE
10365+
/* GETPXT tips */
10366+
#define GETPXT_Tips NULL
10367+
#endif
10368+
10369+
#ifndef SKIP_CMD_KEY_SPECS_TABLE
10370+
/* GETPXT key specs */
10371+
keySpec GETPXT_Keyspecs[1] = {
10372+
{NULL,CMD_KEY_RO|CMD_KEY_ACCESS,KSPEC_BS_INDEX,.bs.index={1},KSPEC_FK_RANGE,.fk.range={0,1,0}}
10373+
};
10374+
#endif
10375+
10376+
/* GETPXT argument table */
10377+
struct COMMAND_ARG GETPXT_Args[] = {
10378+
{MAKE_ARG("key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
10379+
};
10380+
1035710381
/********** GETRANGE ********************/
1035810382

1035910383
#ifndef SKIP_CMD_HISTORY_TABLE
@@ -11131,6 +11155,7 @@ struct COMMAND_STRUCT serverCommandTable[] = {
1113111155
{MAKE_CMD("get","Returns the string value of a key.","O(1)","1.0.0",CMD_DOC_NONE,NULL,NULL,"string",COMMAND_GROUP_STRING,GET_History,0,GET_Tips,0,getCommand,2,CMD_READONLY|CMD_FAST,ACL_CATEGORY_STRING,GET_Keyspecs,1,NULL,1),.args=GET_Args},
1113211156
{MAKE_CMD("getdel","Returns the string value of a key after deleting the key.","O(1)","6.2.0",CMD_DOC_NONE,NULL,NULL,"string",COMMAND_GROUP_STRING,GETDEL_History,0,GETDEL_Tips,0,getdelCommand,2,CMD_WRITE|CMD_FAST,ACL_CATEGORY_STRING,GETDEL_Keyspecs,1,NULL,1),.args=GETDEL_Args},
1113311157
{MAKE_CMD("getex","Returns the string value of a key after setting its expiration time.","O(1)","6.2.0",CMD_DOC_NONE,NULL,NULL,"string",COMMAND_GROUP_STRING,GETEX_History,0,GETEX_Tips,0,getexCommand,-2,CMD_WRITE|CMD_FAST,ACL_CATEGORY_STRING,GETEX_Keyspecs,1,NULL,2),.args=GETEX_Args},
11158+
{MAKE_CMD("getpxt","Returns the string value of a key and the expiration time as a Unix milliseconds timestamp, if set.","O(1)","8.0.2",CMD_DOC_NONE,NULL,NULL,"string",COMMAND_GROUP_STRING,GETPXT_History,0,GETPXT_Tips,0,getpxtCommand,2,CMD_READONLY|CMD_FAST,ACL_CATEGORY_STRING|ACL_CATEGORY_KEYSPACE,GETPXT_Keyspecs,1,NULL,1),.args=GETPXT_Args},
1113411159
{MAKE_CMD("getrange","Returns a substring of the string stored at a key.","O(N) where N is the length of the returned string. The complexity is ultimately determined by the returned length, but because creating a substring from an existing string is very cheap, it can be considered O(1) for small strings.","2.4.0",CMD_DOC_NONE,NULL,NULL,"string",COMMAND_GROUP_STRING,GETRANGE_History,0,GETRANGE_Tips,0,getrangeCommand,4,CMD_READONLY,ACL_CATEGORY_STRING,GETRANGE_Keyspecs,1,NULL,3),.args=GETRANGE_Args},
1113511160
{MAKE_CMD("getset","Returns the previous string value of a key after setting it to a new value.","O(1)","1.0.0",CMD_DOC_DEPRECATED,"`SET` with the `!GET` argument","6.2.0","string",COMMAND_GROUP_STRING,GETSET_History,0,GETSET_Tips,0,getsetCommand,3,CMD_WRITE|CMD_DENYOOM|CMD_FAST,ACL_CATEGORY_STRING,GETSET_Keyspecs,1,NULL,2),.args=GETSET_Args},
1113611161
{MAKE_CMD("incr","Increments the integer value of a key by one. Uses 0 as initial value if the key doesn't exist.","O(1)","1.0.0",CMD_DOC_NONE,NULL,NULL,"string",COMMAND_GROUP_STRING,INCR_History,0,INCR_Tips,0,incrCommand,2,CMD_WRITE|CMD_DENYOOM|CMD_FAST,ACL_CATEGORY_STRING,INCR_Keyspecs,1,NULL,1),.args=INCR_Args},

src/commands/getpxt.json

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
{
2+
"GETPXT": {
3+
"summary": "Returns the string value of a key and the expiration time as a Unix milliseconds timestamp, if set.",
4+
"complexity": "O(1)",
5+
"group": "string",
6+
"since": "8.0.2",
7+
"arity": 2,
8+
"function": "getpxtCommand",
9+
"command_flags": [
10+
"READONLY",
11+
"FAST"
12+
],
13+
"acl_categories": [
14+
"STRING",
15+
"KEYSPACE"
16+
],
17+
"key_specs": [
18+
{
19+
"flags": [
20+
"RO",
21+
"ACCESS"
22+
],
23+
"begin_search": {
24+
"index": {
25+
"pos": 1
26+
}
27+
},
28+
"find_keys": {
29+
"range": {
30+
"lastkey": 0,
31+
"step": 1,
32+
"limit": 0
33+
}
34+
}
35+
}
36+
],
37+
"reply_schema": {
38+
"oneOf": [
39+
{
40+
"type": "array",
41+
"items": {
42+
"type": "array",
43+
"minItems": 2,
44+
"maxItems": 2,
45+
"items": [
46+
{
47+
"description": "The value of the key.",
48+
"type": "string"
49+
},
50+
{
51+
"oneOf": [
52+
{
53+
"type": "integer",
54+
"description": "Expiration Unix timestamp in milliseconds.",
55+
"minimum": 0
56+
},
57+
{
58+
"const": -1,
59+
"description": "The key exists but has no associated expiration time."
60+
}
61+
]
62+
}
63+
]
64+
}
65+
},
66+
{
67+
"description": "Key does not exist.",
68+
"type": "null"
69+
}
70+
]
71+
},
72+
"arguments": [
73+
{
74+
"name": "key",
75+
"type": "key",
76+
"key_spec_index": 0
77+
}
78+
]
79+
}
80+
}

src/server.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3784,6 +3784,7 @@ void psetexCommand(client *c);
37843784
void getCommand(client *c);
37853785
void getexCommand(client *c);
37863786
void getdelCommand(client *c);
3787+
void getpxtCommand(client *c);
37873788
void delCommand(client *c);
37883789
void unlinkCommand(client *c);
37893790
void existsCommand(client *c);

src/t_string.c

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -393,6 +393,34 @@ void getCommand(client *c) {
393393
getGenericCommand(c);
394394
}
395395

396+
void getExpireGenericCommand(client *c, int output_ms) {
397+
long long expire;
398+
robj *o;
399+
400+
if ((o = lookupKeyReadOrReply(c, c->argv[1], shared.null[c->resp])) == NULL)
401+
return;
402+
403+
if (checkType(c, o, OBJ_STRING)) {
404+
return;
405+
}
406+
407+
addReplyArrayLen(c, 2);
408+
addReplyBulk(c, o);
409+
410+
/* The key exists. Return -1 if it has no expire, or the actual
411+
* expire value otherwise. */
412+
expire = getExpire(c->db, c->argv[1]);
413+
if (expire == -1) {
414+
addReplyLongLong(c, -1);
415+
} else {
416+
addReplyLongLong(c, output_ms ? expire : ((expire + 500) / 1000));
417+
}
418+
}
419+
420+
void getpxtCommand(client *c) {
421+
getExpireGenericCommand(c, 1);
422+
}
423+
396424
/*
397425
* GETEX <key> [PERSIST][EX seconds][PX milliseconds][EXAT seconds-timestamp][PXAT milliseconds-timestamp]
398426
*

tests/unit/type/string.tcl

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -658,6 +658,23 @@ if {[string match {*jemalloc*} [s mem_allocator]]} {
658658
assert_range [r ttl foo] 5 10
659659
}
660660

661+
test "GETPXT after SET PXAT" {
662+
r del foo
663+
r set foo bar pxat 17344823940230
664+
r getpxt foo
665+
} {bar 17344823940230}
666+
667+
test "GETPXT after SET" {
668+
r del foo
669+
r set foo bar
670+
r getpxt foo
671+
} {bar -1}
672+
673+
test "GETPXT with no entry" {
674+
r del foo
675+
r getpxt foo
676+
} {}
677+
661678
test "SET EXAT / PXAT Expiration time is expired" {
662679
r debug set-active-expire 0
663680
set repl [attach_to_replication_stream]

0 commit comments

Comments
 (0)