diff --git a/src/commands.def b/src/commands.def index 10fbd9f4a..deba47fea 100644 --- a/src/commands.def +++ b/src/commands.def @@ -10861,7 +10861,7 @@ struct COMMAND_STRUCT redisCommandTable[] = { {MAKE_CMD("xlen","Return the number of messages in a stream.","O(1)","5.0.0",CMD_DOC_NONE,NULL,NULL,"stream",COMMAND_GROUP_STREAM,XLEN_History,0,XLEN_Tips,0,xlenCommand,2,CMD_READONLY|CMD_FAST,ACL_CATEGORY_STREAM,XLEN_Keyspecs,1,NULL,1),.args=XLEN_Args}, {MAKE_CMD("xpending","Returns the information and entries from a stream consumer group's pending entries list.","O(N) with N being the number of elements returned, so asking for a small fixed number of entries per call is O(1). O(M), where M is the total number of entries scanned when used with the IDLE filter. When the command returns just the summary and the list of consumers is small, it runs in O(1) time; otherwise, an additional O(N) time for iterating every consumer.","5.0.0",CMD_DOC_NONE,NULL,NULL,"stream",COMMAND_GROUP_STREAM,XPENDING_History,1,XPENDING_Tips,1,xpendingCommand,-3,CMD_READONLY,ACL_CATEGORY_STREAM,XPENDING_Keyspecs,1,NULL,3),.args=XPENDING_Args}, {MAKE_CMD("xrange","Returns the messages from a stream within a range of IDs.","O(N) with N being the number of elements being returned. If N is constant (e.g. always asking for the first 10 elements with COUNT), you can consider it O(1).","5.0.0",CMD_DOC_NONE,NULL,NULL,"stream",COMMAND_GROUP_STREAM,XRANGE_History,1,XRANGE_Tips,0,xrangeCommand,-4,CMD_READONLY,ACL_CATEGORY_STREAM,XRANGE_Keyspecs,1,NULL,4),.args=XRANGE_Args}, -{MAKE_CMD("xread","Returns messages from multiple streams with IDs greater than the ones requested. Blocks until a message is available otherwise.",NULL,"5.0.0",CMD_DOC_NONE,NULL,NULL,"stream",COMMAND_GROUP_STREAM,XREAD_History,0,XREAD_Tips,0,xreadCommand,-4,CMD_BLOCKING|CMD_READONLY|CMD_BLOCKING,ACL_CATEGORY_STREAM,XREAD_Keyspecs,1,xreadGetKeys,3),.args=XREAD_Args}, +{MAKE_CMD("xread","Returns messages from multiple streams with IDs greater than the ones requested. Blocks until a message is available otherwise.",NULL,"5.0.0",CMD_DOC_NONE,NULL,NULL,"stream",COMMAND_GROUP_STREAM,XREAD_History,0,XREAD_Tips,0,xreadCommand,-4,CMD_BLOCKING|CMD_READONLY,ACL_CATEGORY_STREAM,XREAD_Keyspecs,1,xreadGetKeys,3),.args=XREAD_Args}, {MAKE_CMD("xreadgroup","Returns new or historical messages from a stream for a consumer in a group. Blocks until a message is available otherwise.","For each stream mentioned: O(M) with M being the number of elements returned. If M is constant (e.g. always asking for the first 10 elements with COUNT), you can consider it O(1). On the other side when XREADGROUP blocks, XADD will pay the O(N) time in order to serve the N clients blocked on the stream getting new data.","5.0.0",CMD_DOC_NONE,NULL,NULL,"stream",COMMAND_GROUP_STREAM,XREADGROUP_History,0,XREADGROUP_Tips,0,xreadCommand,-7,CMD_BLOCKING|CMD_WRITE,ACL_CATEGORY_STREAM,XREADGROUP_Keyspecs,1,xreadGetKeys,5),.args=XREADGROUP_Args}, {MAKE_CMD("xrevrange","Returns the messages from a stream within a range of IDs in reverse order.","O(N) with N being the number of elements returned. If N is constant (e.g. always asking for the first 10 elements with COUNT), you can consider it O(1).","5.0.0",CMD_DOC_NONE,NULL,NULL,"stream",COMMAND_GROUP_STREAM,XREVRANGE_History,1,XREVRANGE_Tips,0,xrevrangeCommand,-4,CMD_READONLY,ACL_CATEGORY_STREAM,XREVRANGE_Keyspecs,1,NULL,4),.args=XREVRANGE_Args}, {MAKE_CMD("xsetid","An internal command for replicating stream values.","O(1)","5.0.0",CMD_DOC_NONE,NULL,NULL,"stream",COMMAND_GROUP_STREAM,XSETID_History,1,XSETID_Tips,0,xsetidCommand,-3,CMD_WRITE|CMD_DENYOOM|CMD_FAST,ACL_CATEGORY_STREAM,XSETID_Keyspecs,1,NULL,4),.args=XSETID_Args}, diff --git a/src/commands/xread.json b/src/commands/xread.json index 3a78ffb22..95e22c494 100644 --- a/src/commands/xread.json +++ b/src/commands/xread.json @@ -8,8 +8,7 @@ "get_keys_function": "xreadGetKeys", "command_flags": [ "BLOCKING", - "READONLY", - "BLOCKING" + "READONLY" ], "acl_categories": [ "STREAM" diff --git a/src/t_stream.c b/src/t_stream.c index 5fcb631ab..ccb566bae 100644 --- a/src/t_stream.c +++ b/src/t_stream.c @@ -2188,14 +2188,6 @@ void xreadCommand(client *c) { int moreargs = c->argc-i-1; char *o = c->argv[i]->ptr; if (!strcasecmp(o,"BLOCK") && moreargs) { - if (c->flags & CLIENT_SCRIPT) { - /* - * Although the CLIENT_DENY_BLOCKING flag should protect from blocking the client - * on Lua/MULTI/RM_Call we want special treatment for Lua to keep backward compatibility. - * There is no sense to use BLOCK option within Lua. */ - addReplyErrorFormat(c, "%s command is not allowed with BLOCK option from scripts", (char *)c->argv[0]->ptr); - return; - } i++; if (getTimeoutFromObjectOrReply(c,c->argv[i],&timeout, UNIT_MILLISECONDS) != C_OK) return; diff --git a/tests/unit/scripting.tcl b/tests/unit/scripting.tcl index c2f79a742..c16d050c5 100644 --- a/tests/unit/scripting.tcl +++ b/tests/unit/scripting.tcl @@ -257,17 +257,38 @@ start_server {tags {"scripting"}} { run_script {return redis.pcall('wait','1','0')} 0 } {0} - test {EVAL - Scripts can't run XREAD and XREADGROUP with BLOCK option} { + test {EVAL - Scripts do not block on XREAD with BLOCK option} { r del s r xgroup create s g $ MKSTREAM set res [run_script {return redis.pcall('xread','STREAMS','s','$')} 1 s] assert {$res eq {}} - assert_error "*xread command is not allowed with BLOCK option from scripts" {run_script {return redis.pcall('xread','BLOCK',0,'STREAMS','s','$')} 1 s} + run_script {return redis.pcall('xread','BLOCK',0,'STREAMS','s','$')} 1 s + } {} + + test {EVAL - Scripts do not block on XREADGROUP with BLOCK option} { set res [run_script {return redis.pcall('xreadgroup','group','g','c','STREAMS','s','>')} 1 s] assert {$res eq {}} - assert_error "*xreadgroup command is not allowed with BLOCK option from scripts" {run_script {return redis.pcall('xreadgroup','group','g','c','BLOCK',0,'STREAMS','s','>')} 1 s} + run_script {return redis.pcall('xreadgroup','group','g','c','BLOCK',0,'STREAMS','s','>')} 1 s + } {} + + test {EVAL - Scripts do not block on XREAD with BLOCK option -- non empty stream} { + r XADD s * a 1 + set res [run_script {return redis.pcall('xread','BLOCK',0,'STREAMS','s','$')} 1 s] + assert {$res eq {}} + + set res [run_script {return redis.pcall('xread','BLOCK',0,'STREAMS','s','0-0')} 1 s] + assert {[lrange [lindex $res 0 1 0 1] 0 1] eq {a 1}} } + test {EVAL - Scripts do not block on XREADGROUP with BLOCK option -- non empty stream} { + r XADD s * b 2 + set res [ + run_script {return redis.pcall('xreadgroup','group','g','c','BLOCK',0,'STREAMS','s','>')} 1 s + ] + assert {[llength [lindex $res 0 1]] == 2} + lindex $res 0 1 0 1 + } {a 1} + test {EVAL - Scripts can run non-deterministic commands} { set e {} catch {