@@ -155,26 +155,25 @@ public final class ValkeyClusterClient: Sendable {
155
155
@inlinable
156
156
public func send< Command: ValkeyCommand > ( command: Command ) async throws -> Command . Response {
157
157
let hashSlots = command. keysAffected. map { HashSlot ( key: $0) }
158
- let clientSelector : ( ) async throws -> ValkeyClient = {
158
+ var clientSelector : ( ) async throws -> ValkeyClient = {
159
159
try await self . client ( for: hashSlots)
160
160
}
161
161
162
- var respToken = try await self . retryingSend (
163
- clientSelector: clientSelector,
164
- command: command,
165
- logger: logger
166
- )
167
-
168
- // TODO: We might want to collect redirects here for diagnostic purposes.
169
- while let movedError = respToken. parseMovedError ( ) {
170
- respToken = try await self . retryingSend (
171
- clientSelector: { try await self . client ( for: movedError) } ,
172
- command: command,
173
- logger: logger
174
- )
162
+ while true {
163
+ do {
164
+ let respToken = try await self . retryingSend (
165
+ clientSelector: clientSelector,
166
+ command: command,
167
+ logger: logger
168
+ )
169
+ return try Command . Response ( fromRESP: respToken)
170
+ } catch let error as ValkeyClientError where error. errorCode == . commandError {
171
+ guard let errorMessage = error. message, let movedError = ValkeyMovedError ( errorMessage) else {
172
+ throw error
173
+ }
174
+ clientSelector = { try await self . client ( for: movedError) }
175
+ }
175
176
}
176
-
177
- return try Command . Response ( fromRESP: respToken)
178
177
}
179
178
180
179
/// Starts running the cluster client.
@@ -195,7 +194,7 @@ public final class ValkeyClusterClient: Sendable {
195
194
/// group.addTask {
196
195
/// await client.run()
197
196
/// }
198
- ///
197
+ ///
199
198
/// // use the client here
200
199
/// let foo = try await client.get(key: "foo")
201
200
/// }
@@ -209,7 +208,7 @@ public final class ValkeyClusterClient: Sendable {
209
208
self . actionStreamContinuation. yield ( . runClusterDiscovery( runNodeDiscovery: true ) )
210
209
211
210
await withTaskCancellationHandler {
212
- await withDiscardingTaskGroup ( ) { taskGroup in
211
+ await withDiscardingTaskGroup { taskGroup in
213
212
await self . runUsingTaskGroup ( & taskGroup)
214
213
}
215
214
} onCancel: {
@@ -427,7 +426,6 @@ public final class ValkeyClusterClient: Sendable {
427
426
try await withCheckedThrowingContinuation {
428
427
( continuation: CheckedContinuation < Void , any Error > ) in
429
428
430
-
431
429
let action = self . stateLock. withLock {
432
430
$0. waitForHealthy ( waiterID: waiterID, successNotifier: continuation)
433
431
}
@@ -486,9 +484,6 @@ public final class ValkeyClusterClient: Sendable {
486
484
return try await client. _send ( command)
487
485
} catch ValkeyClusterError . noNodeToTalkTo {
488
486
// TODO: Rerun node discovery!
489
- } catch let error as ValkeyClientError {
490
- fatalError ( " TODO: error received: \( error) " )
491
- break
492
487
}
493
488
}
494
489
@@ -528,13 +523,14 @@ public final class ValkeyClusterClient: Sendable {
528
523
/// - Parameter runNodeDiscoveryFirst: Whether to run node discovery before querying for cluster topology.
529
524
private func runClusterDiscovery( runNodeDiscoveryFirst: Bool ) async {
530
525
do {
531
- let voters = if runNodeDiscoveryFirst {
532
- try await self . runNodeDiscovery ( )
533
- } else {
534
- self . stateLock. withLock {
535
- $0. getInitialVoters ( )
526
+ let voters =
527
+ if runNodeDiscoveryFirst {
528
+ try await self . runNodeDiscovery ( )
529
+ } else {
530
+ self . stateLock. withLock {
531
+ $0. getInitialVoters ( )
532
+ }
536
533
}
537
- }
538
534
539
535
let clusterDescription = try await self . runClusterDiscoveryFindingConsensus ( voters: voters)
540
536
let action = self . stateLock. withLock {
@@ -543,9 +539,12 @@ public final class ValkeyClusterClient: Sendable {
543
539
544
540
self . runClusterDiscoverySucceededAction ( action)
545
541
} catch {
546
- self . logger. debug ( " Valkey cluster discovery failed " , metadata: [
547
- " error " : " \( error) " ,
548
- ] )
542
+ self . logger. debug (
543
+ " Valkey cluster discovery failed " ,
544
+ metadata: [
545
+ " error " : " \( error) "
546
+ ]
547
+ )
549
548
let action = self . stateLock. withLock {
550
549
$0. valkeyClusterDiscoveryFailed ( error)
551
550
}
@@ -567,30 +566,36 @@ public final class ValkeyClusterClient: Sendable {
567
566
let actions = self . stateLock. withLock {
568
567
$0. updateValkeyServiceNodes ( mapped)
569
568
}
570
- self . logger. debug ( " Discovered nodes " , metadata: [
571
- " node_count " : " \( nodes. count) " ,
572
- ] )
569
+ self . logger. debug (
570
+ " Discovered nodes " ,
571
+ metadata: [
572
+ " node_count " : " \( nodes. count) "
573
+ ]
574
+ )
573
575
self . runUpdateValkeyNodesAction ( actions)
574
576
return actions. voters
575
577
} catch {
576
- self . logger. debug ( " Failed to discover nodes " , metadata: [
577
- " error " : " \( error) " ,
578
- ] )
578
+ self . logger. debug (
579
+ " Failed to discover nodes " ,
580
+ metadata: [
581
+ " error " : " \( error) "
582
+ ]
583
+ )
579
584
throw error
580
585
}
581
586
}
582
587
583
588
/// Establishes consensus on the cluster topology by querying multiple nodes.
584
589
///
585
- /// This method uses a voting mechanism to establish consensus among multiple nodes
590
+ /// This method uses a voting mechanism to establish consensus among multiple nodes
586
591
/// about the current cluster topology. It requires a quorum of nodes to agree
587
592
/// on the topology before accepting it.
588
593
///
589
594
/// - Parameter voters: The list of nodes that can vote on cluster topology.
590
595
/// - Returns: The agreed-upon cluster description.
591
596
/// - Throws: `ValkeyClusterError.clusterIsUnavailable` if consensus cannot be reached.
592
597
private func runClusterDiscoveryFindingConsensus( voters: [ ValkeyClusterVoter < ValkeyClient > ] ) async throws -> ValkeyClusterDescription {
593
- return try await withThrowingTaskGroup ( of: ( ValkeyClusterDescription, ValkeyNodeID) . self) { taskGroup in
598
+ try await withThrowingTaskGroup ( of: ( ValkeyClusterDescription, ValkeyNodeID) . self) { taskGroup in
594
599
for voter in voters {
595
600
taskGroup. addTask {
596
601
( try await voter. client. clusterShards ( ) , voter. nodeID)
@@ -606,17 +611,23 @@ public final class ValkeyClusterClient: Sendable {
606
611
do {
607
612
let metrics = try election. voteReceived ( for: description, from: nodeID)
608
613
609
- self . logger. debug ( " Vote received " , metadata: [
610
- " candidate_count " : " \( metrics. candidateCount) " ,
611
- " candidate " : " \( metrics. candidate) " ,
612
- " votes_received " : " \( metrics. votesReceived) " ,
613
- " votes_needed " : " \( metrics. votesNeeded) "
614
- ] )
614
+ self . logger. debug (
615
+ " Vote received " ,
616
+ metadata: [
617
+ " candidate_count " : " \( metrics. candidateCount) " ,
618
+ " candidate " : " \( metrics. candidate) " ,
619
+ " votes_received " : " \( metrics. votesReceived) " ,
620
+ " votes_needed " : " \( metrics. votesNeeded) " ,
621
+ ]
622
+ )
615
623
} catch let error as ValkeyClusterError {
616
- self . logger. debug ( " Vote invalid " , metadata: [
617
- " nodeID " : " \( nodeID) " ,
618
- " error " : " \( error) " ,
619
- ] )
624
+ self . logger. debug (
625
+ " Vote invalid " ,
626
+ metadata: [
627
+ " nodeID " : " \( nodeID) " ,
628
+ " error " : " \( error) " ,
629
+ ]
630
+ )
620
631
continue
621
632
}
622
633
@@ -636,9 +647,12 @@ public final class ValkeyClusterClient: Sendable {
636
647
}
637
648
638
649
case . failure( let error) :
639
- self . logger. debug ( " Received an error while asking for cluster topology " , metadata: [
640
- " error " : " \( error) "
641
- ] )
650
+ self . logger. debug (
651
+ " Received an error while asking for cluster topology " ,
652
+ metadata: [
653
+ " error " : " \( error) "
654
+ ]
655
+ )
642
656
}
643
657
}
644
658
0 commit comments