Files
redis/tests/unit/cluster/sharded-pubsub.tcl
4da375cf5e Fix sharded pubsub unsubscribe lookup using cached command slot (#15094)
Fixes #15085 

## Problem
getKeySlot() may return `server.current_client->slot` while a command is
executing instead of computing the slot from the provided string.

The unsubscribe can be triggered by another client, in which case server.current_client is not the client being unsubscribed, so getKeySlot() would return that client's cached slot. Using this wrong slot would make the lookup in type.serverPubSubChannels miss the channel and ultimately trigger the assertion below.

## Fix
Always use keyHashSlot() instead of getKeySlot() on unsubscribe.

---------

Co-authored-by: debing.sun <[email protected]>
2026-04-29 22:04:06 +08:00

95 lines
3.0 KiB
Tcl

#
# Copyright (c) 2009-Present, Redis Ltd.
# All rights reserved.
#
# Licensed under your choice of (a) the Redis Source Available License 2.0
# (RSALv2); or (b) the Server Side Public License v1 (SSPLv1); or (c) the
# GNU Affero General Public License v3 (AGPLv3).
#
# Portions of this file are available under BSD3 terms; see REDISCONTRIBUTIONS for more information.
#
start_cluster 1 1 {tags {external:skip cluster}} {
set primary_id 0
set replica1_id 1
set primary [Rn $primary_id]
set replica [Rn $replica1_id]
test "Sharded pubsub publish behavior within multi/exec" {
foreach {node} {primary replica} {
set node [set $node]
$node MULTI
$node SPUBLISH ch1 "hello"
$node EXEC
}
}
test "Sharded pubsub within multi/exec with cross slot operation" {
$primary MULTI
$primary SPUBLISH ch1 "hello"
$primary GET foo
catch {$primary EXEC} err
assert_match {CROSSSLOT*} $err
}
test "Sharded pubsub publish behavior within multi/exec with read operation on primary" {
$primary MULTI
$primary SPUBLISH foo "hello"
$primary GET foo
$primary EXEC
} {0 {}}
test "Sharded pubsub publish behavior within multi/exec with read operation on replica" {
$replica MULTI
$replica SPUBLISH foo "hello"
catch {[$replica GET foo]} err
assert_match {MOVED*} $err
catch {[$replica EXEC]} err
assert_match {EXECABORT*} $err
}
test "Sharded pubsub publish behavior within multi/exec with write operation on primary" {
$primary MULTI
$primary SPUBLISH foo "hello"
$primary SET foo bar
$primary EXEC
} {0 OK}
test "Sharded pubsub publish behavior within multi/exec with write operation on replica" {
$replica MULTI
$replica SPUBLISH foo "hello"
catch {[$replica SET foo bar]} err
assert_match {MOVED*} $err
catch {[$replica EXEC]} err
assert_match {EXECABORT*} $err
}
# Regression: shard channel slot must not follow getKeySlot() current_client
# cache when CLIENT KILL runs inside another client's EXEC (pubsubUnsubscribeChannel).
test {Shard pubsub: CLIENT KILL subscriber inside MULTI/EXEC (cross-slot)} {
# SET fixes the transaction client's slot to keyk's slot; the subscriber must
# use a shard channel in a different slot so a wrong-slot lookup would fail.
set keyk "{06S}k"
set channel "{Qi}ch"
assert {[R 0 cluster keyslot $channel] != [R 0 cluster keyslot $keyk]}
set rd_sub [redis_deferring_client]
$rd_sub client id
set cid [$rd_sub read]
$rd_sub ssubscribe $channel
$rd_sub read
$primary multi
$primary set $keyk v
$primary client kill id $cid
set got [$primary exec]
assert_equal {OK 1} $got
assert_equal PONG [$primary ping]
catch {$rd_sub read}
$rd_sub close
}
}