From c4a6da6191f93246477ca42ae5c164397e2c0721 Mon Sep 17 00:00:00 2001 From: Deeka Wong <8337659+huangdijia@users.noreply.github.com> Date: Sat, 22 Nov 2025 12:03:03 +0800 Subject: [PATCH 1/2] feat: add server address tracing for RedisCluster connections This commit enhances the RedisConnectionAspect to support server address tracing for RedisCluster connections by implementing slot-based node resolution. The implementation includes: - Added WeakMap-based caching for cluster slot information to minimize redundant CLUSTER SLOTS commands - Implemented getSlotByKey() to determine the hash slot for a given key - Implemented findNodeBySlot() to locate the master node responsible for a specific slot range - Extracts and sets server address (host/port) in tracing context for RedisCluster operations This enables distributed tracing systems to accurately track which Redis cluster node handles each operation, improving observability for clustered Redis deployments. --- .../Tracing/Aspect/RedisConnectionAspect.php | 49 ++++++++++++++++++- 1 file changed, 48 insertions(+), 1 deletion(-) diff --git a/src/sentry/src/Tracing/Aspect/RedisConnectionAspect.php b/src/sentry/src/Tracing/Aspect/RedisConnectionAspect.php index 9763e67fa..1ab3f6b96 100644 --- a/src/sentry/src/Tracing/Aspect/RedisConnectionAspect.php +++ b/src/sentry/src/Tracing/Aspect/RedisConnectionAspect.php @@ -17,6 +17,7 @@ use Hyperf\Di\Aop\ProceedingJoinPoint; use Redis; use RedisCluster; +use WeakMap; use function Hyperf\Tappable\tap; @@ -26,6 +27,13 @@ class RedisConnectionAspect extends AbstractAspect 'Hyperf\Redis\RedisConnection::__call', ]; + private WeakMap $slotNodeCache; + + public function __construct() + { + $this->slotNodeCache = new WeakMap(); + } + public function process(ProceedingJoinPoint $proceedingJoinPoint) { return tap($proceedingJoinPoint->process(), function ($result) use ($proceedingJoinPoint) { @@ -38,8 +46,47 @@ public function process(ProceedingJoinPoint $proceedingJoinPoint) } if ($connection instanceof RedisCluster) { // RedisCluster - // TODO: support RedisCluster + $arguments = $proceedingJoinPoint->arguments['keys']['arguments'] ?? []; + $key = $arguments[0] ?? null; + if (is_string($key)) { + $slot = $this->getSlotByKey($connection, $key); + $slots = $this->getSlots($connection); + $node = $this->findNodeBySlot($slots, $slot); + if (is_array($node)) { + Context::set(Constants::TRACE_REDIS_SERVER_ADDRESS, $node['host']); + Context::set(Constants::TRACE_REDIS_SERVER_PORT, $node['port']); + } + } } }); } + + private function getSlotByKey(RedisCluster $rc, string $key): int + { + return (int) $rc->cluster('CLUSTER', 'KEYSLOT', $key); + } + + private function getSlots(RedisCluster $rc): array + { + $this->slotNodeCache[$rc] ??= $rc->cluster('CLUSTER', 'SLOTS'); + + return $this->slotNodeCache[$rc]; + } + + private function findNodeBySlot(array $slots, int $slot): ?array + { + foreach ($slots as $range) { + [$start, $end, $master] = $range; + if ($slot >= $start && $slot <= $end) { + // $master = [host, port, nodeId] + return [ + 'host' => $master[0], + 'port' => $master[1], + 'nodeId' => $master[2] ?? null, + ]; + } + } + + return null; + } } From 0c0e85eea08196743ef140b04daead96f002c58a Mon Sep 17 00:00:00 2001 From: Deeka Wong <8337659+huangdijia@users.noreply.github.com> Date: Sat, 22 Nov 2025 12:13:43 +0800 Subject: [PATCH 2/2] feat: optimize RedisCluster node tracing by consolidating slot retrieval methods --- .../Tracing/Aspect/RedisConnectionAspect.php | 21 +++++-------------- 1 file changed, 5 insertions(+), 16 deletions(-) diff --git a/src/sentry/src/Tracing/Aspect/RedisConnectionAspect.php b/src/sentry/src/Tracing/Aspect/RedisConnectionAspect.php index 1ab3f6b96..11ce2b435 100644 --- a/src/sentry/src/Tracing/Aspect/RedisConnectionAspect.php +++ b/src/sentry/src/Tracing/Aspect/RedisConnectionAspect.php @@ -49,10 +49,8 @@ public function process(ProceedingJoinPoint $proceedingJoinPoint) $arguments = $proceedingJoinPoint->arguments['keys']['arguments'] ?? []; $key = $arguments[0] ?? null; if (is_string($key)) { - $slot = $this->getSlotByKey($connection, $key); - $slots = $this->getSlots($connection); - $node = $this->findNodeBySlot($slots, $slot); - if (is_array($node)) { + $node = $this->getClusterNodeBySlot($connection, $key); + if ($node !== null) { Context::set(Constants::TRACE_REDIS_SERVER_ADDRESS, $node['host']); Context::set(Constants::TRACE_REDIS_SERVER_PORT, $node['port']); } @@ -61,20 +59,11 @@ public function process(ProceedingJoinPoint $proceedingJoinPoint) }); } - private function getSlotByKey(RedisCluster $rc, string $key): int + private function getClusterNodeBySlot(RedisCluster $rc, string $key) { - return (int) $rc->cluster('CLUSTER', 'KEYSLOT', $key); - } - - private function getSlots(RedisCluster $rc): array - { - $this->slotNodeCache[$rc] ??= $rc->cluster('CLUSTER', 'SLOTS'); + $slot = $rc->cluster('CLUSTER', 'KEYSLOT', $key); + $slots = ($this->slotNodeCache[$rc] ??= $rc->cluster('CLUSTER', 'SLOTS')); // @phpstan-ignore-line - return $this->slotNodeCache[$rc]; - } - - private function findNodeBySlot(array $slots, int $slot): ?array - { foreach ($slots as $range) { [$start, $end, $master] = $range; if ($slot >= $start && $slot <= $end) {