Skip to content

Commit

Permalink
Add GETPXT (Get with millisecond expiration) command
Browse files Browse the repository at this point in the history
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]>
  • Loading branch information
arcivanov committed Dec 18, 2024
1 parent ba25b58 commit 7ccf367
Show file tree
Hide file tree
Showing 5 changed files with 151 additions and 0 deletions.
25 changes: 25 additions & 0 deletions src/commands.def
Original file line number Diff line number Diff line change
Expand Up @@ -10354,6 +10354,30 @@ struct COMMAND_ARG GETEX_Args[] = {
{MAKE_ARG("expiration",ARG_TYPE_ONEOF,-1,NULL,NULL,NULL,CMD_ARG_OPTIONAL,5,NULL),.subargs=GETEX_expiration_Subargs},
};

/********** GETPXT ********************/

#ifndef SKIP_CMD_HISTORY_TABLE
/* GETPXT history */
#define GETPXT_History NULL
#endif

#ifndef SKIP_CMD_TIPS_TABLE
/* GETPXT tips */
#define GETPXT_Tips NULL
#endif

#ifndef SKIP_CMD_KEY_SPECS_TABLE
/* GETPXT key specs */
keySpec GETPXT_Keyspecs[1] = {
{NULL,CMD_KEY_RO|CMD_KEY_ACCESS,KSPEC_BS_INDEX,.bs.index={1},KSPEC_FK_RANGE,.fk.range={0,1,0}}
};
#endif

/* GETPXT argument table */
struct COMMAND_ARG GETPXT_Args[] = {
{MAKE_ARG("key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
};

/********** GETRANGE ********************/

#ifndef SKIP_CMD_HISTORY_TABLE
Expand Down Expand Up @@ -11131,6 +11155,7 @@ struct COMMAND_STRUCT serverCommandTable[] = {
{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},
{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},
{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},
{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},
{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},
{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},
{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},
Expand Down
80 changes: 80 additions & 0 deletions src/commands/getpxt.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
{
"GETPXT": {
"summary": "Returns the string value of a key and the expiration time as a Unix milliseconds timestamp, if set.",
"complexity": "O(1)",
"group": "string",
"since": "8.0.2",
"arity": 2,
"function": "getpxtCommand",
"command_flags": [
"READONLY",
"FAST"
],
"acl_categories": [
"STRING",
"KEYSPACE"
],
"key_specs": [
{
"flags": [
"RO",
"ACCESS"
],
"begin_search": {
"index": {
"pos": 1
}
},
"find_keys": {
"range": {
"lastkey": 0,
"step": 1,
"limit": 0
}
}
}
],
"reply_schema": {
"oneOf": [
{
"type": "array",
"items": {
"type": "array",
"minItems": 2,
"maxItems": 2,
"items": [
{
"description": "The value of the key.",
"type": "string"
},
{
"oneOf": [
{
"type": "integer",
"description": "Expiration Unix timestamp in milliseconds.",
"minimum": 0
},
{
"const": -1,
"description": "The key exists but has no associated expiration time."
}
]
}
]
}
},
{
"description": "Key does not exist.",
"type": "null"
}
]
},
"arguments": [
{
"name": "key",
"type": "key",
"key_spec_index": 0
}
]
}
}
1 change: 1 addition & 0 deletions src/server.h
Original file line number Diff line number Diff line change
Expand Up @@ -3784,6 +3784,7 @@ void psetexCommand(client *c);
void getCommand(client *c);
void getexCommand(client *c);
void getdelCommand(client *c);
void getpxtCommand(client *c);
void delCommand(client *c);
void unlinkCommand(client *c);
void existsCommand(client *c);
Expand Down
28 changes: 28 additions & 0 deletions src/t_string.c
Original file line number Diff line number Diff line change
Expand Up @@ -393,6 +393,34 @@ void getCommand(client *c) {
getGenericCommand(c);
}

void getExpireGenericCommand(client *c, int output_ms) {
long long expire;
robj *o;

if ((o = lookupKeyReadOrReply(c, c->argv[1], shared.null[c->resp])) == NULL)
return;

if (checkType(c, o, OBJ_STRING)) {
return;
}

addReplyArrayLen(c, 2);
addReplyBulk(c, o);

/* The key exists. Return -1 if it has no expire, or the actual
* expire value otherwise. */
expire = getExpire(c->db, c->argv[1]);
if (expire == -1) {
addReplyLongLong(c, -1);
} else {
addReplyLongLong(c, output_ms ? expire : ((expire + 500) / 1000));
}
}

void getpxtCommand(client *c) {
getExpireGenericCommand(c, 1);
}

/*
* GETEX <key> [PERSIST][EX seconds][PX milliseconds][EXAT seconds-timestamp][PXAT milliseconds-timestamp]
*
Expand Down
17 changes: 17 additions & 0 deletions tests/unit/type/string.tcl
Original file line number Diff line number Diff line change
Expand Up @@ -658,6 +658,23 @@ if {[string match {*jemalloc*} [s mem_allocator]]} {
assert_range [r ttl foo] 5 10
}

test "GETPXT after SET PXAT" {
r del foo
r set foo bar pxat 17344823940230
r getpxt foo
} {bar 17344823940230}

test "GETPXT after SET" {
r del foo
r set foo bar
r getpxt foo
} {bar -1}

test "GETPXT with no entry" {
r del foo
r getpxt foo
} {}

test "SET EXAT / PXAT Expiration time is expired" {
r debug set-active-expire 0
set repl [attach_to_replication_stream]
Expand Down

0 comments on commit 7ccf367

Please sign in to comment.