diff --git a/src/pubsub.c b/src/pubsub.c index 9e3958b36..1a151b96c 100644 --- a/src/pubsub.c +++ b/src/pubsub.c @@ -290,7 +290,7 @@ int pubsubSubscribeChannel(client *c, robj *channel, pubsubtype type) { incrRefCount(channel); /* Add the client to the channel -> list of clients hash table */ if (server.cluster_enabled && type.shard) { - slot = c->slot; + slot = getKeySlot(channel->ptr); } d_ptr = type.serverPubSubChannels(slot); if (*d_ptr == NULL) { @@ -332,7 +332,7 @@ int pubsubUnsubscribeChannel(client *c, robj *channel, int notify, pubsubtype ty retval = 1; /* Remove the client from the channel -> clients list hash table */ if (server.cluster_enabled && type.shard) { - slot = c->slot != -1 ? c->slot : (int)keyHashSlot(channel->ptr, sdslen(channel->ptr)); + slot = getKeySlot(channel->ptr); } d = *type.serverPubSubChannels(slot); serverAssertWithInfo(c,NULL,d != NULL); diff --git a/src/script.c b/src/script.c index 678773d96..4a6461c0b 100644 --- a/src/script.c +++ b/src/script.c @@ -209,6 +209,7 @@ int scriptPrepareForRun(scriptRunCtx *run_ctx, client *engine_client, client *ca run_ctx->c = engine_client; run_ctx->original_client = caller; run_ctx->funcname = funcname; + run_ctx->slot = caller->slot; client *script_client = run_ctx->c; client *curr_client = run_ctx->original_client; @@ -262,6 +263,8 @@ void scriptResetRun(scriptRunCtx *run_ctx) { unprotectClient(run_ctx->original_client); } + run_ctx->slot = -1; + preventCommandPropagation(run_ctx->original_client); /* unset curr_run_ctx so we will know there is no running script */ @@ -463,14 +466,18 @@ static int scriptVerifyClusterState(scriptRunCtx *run_ctx, client *c, client *or * already been thrown. This is only checking for cross slot keys being accessed * that weren't pre-declared. */ if (hashslot != -1 && !(run_ctx->flags & SCRIPT_ALLOW_CROSS_SLOT)) { - if (original_c->slot == -1) { - original_c->slot = hashslot; - } else if (original_c->slot != hashslot) { + if (run_ctx->slot == -1) { + run_ctx->slot = hashslot; + } else if (run_ctx->slot != hashslot) { *err = sdsnew("Script attempted to access keys that do not hash to " "the same slot"); return C_ERR; } } + + c->slot = hashslot; + original_c->slot = hashslot; + return C_OK; } diff --git a/src/script.h b/src/script.h index c487165d6..caf95ef95 100644 --- a/src/script.h +++ b/src/script.h @@ -74,6 +74,7 @@ struct scriptRunCtx { int flags; int repl_flags; monotime start_time; + int slot; }; /* Scripts flags */ diff --git a/src/sort.c b/src/sort.c index a8b9391b1..bef260555 100644 --- a/src/sort.c +++ b/src/sort.c @@ -239,7 +239,7 @@ void sortCommandGeneric(client *c, int readonly) { /* If BY is specified with a real pattern, we can't accept it in cluster mode, * unless we can make sure the keys formed by the pattern are in the same slot * as the key to sort. */ - if (server.cluster_enabled && patternHashSlot(sortby->ptr, sdslen(sortby->ptr)) != c->slot) { + if (server.cluster_enabled && patternHashSlot(sortby->ptr, sdslen(sortby->ptr)) != getKeySlot(c->argv[1]->ptr)) { addReplyError(c, "BY option of SORT denied in Cluster mode when " "keys formed by the pattern may be in different slots."); syntax_error++; @@ -258,7 +258,7 @@ void sortCommandGeneric(client *c, int readonly) { /* If GET is specified with a real pattern, we can't accept it in cluster mode, * unless we can make sure the keys formed by the pattern are in the same slot * as the key to sort. */ - if (server.cluster_enabled && patternHashSlot(c->argv[j+1]->ptr, sdslen(c->argv[j+1]->ptr)) != c->slot) { + if (server.cluster_enabled && patternHashSlot(c->argv[j+1]->ptr, sdslen(c->argv[j+1]->ptr)) != getKeySlot(c->argv[1]->ptr)) { addReplyError(c, "GET option of SORT denied in Cluster mode when " "keys formed by the pattern may be in different slots."); syntax_error++; diff --git a/tests/unit/cluster/scripting.tcl b/tests/unit/cluster/scripting.tcl index b60c1255b..76aa882e8 100644 --- a/tests/unit/cluster/scripting.tcl +++ b/tests/unit/cluster/scripting.tcl @@ -62,6 +62,15 @@ start_cluster 1 0 {tags {external:skip cluster}} { } 1 bar} } + test {Cross slot commands are allowed by default if they disagree with pre-declared keys} { + r 0 flushall + r 0 eval "redis.call('set', 'foo', 'bar')" 1 bar + + # Make sure the script writes to the right slot + assert_equal 1 [r 0 cluster COUNTKEYSINSLOT 12182] ;# foo slot + assert_equal 0 [r 0 cluster COUNTKEYSINSLOT 5061] ;# bar slot + } + test "Function no-cluster flag" { R 0 function load {#!lua name=test redis.register_function{function_name='f1', callback=function() return 'hello' end, flags={'no-cluster'}}