@@ -143,9 +143,9 @@ public final class ValkeyClusterClient: Sendable {
143
143
/// - Other errors if the command execution or parsing fails
144
144
@inlinable
145
145
public func execute< Command: ValkeyCommand > ( _ command: Command ) async throws -> Command . Response {
146
- let hashSlots = command . keysAffected . map { HashSlot ( key : $0 ) }
146
+ let hashSlot = try self . hashSlot ( for : command . keysAffected )
147
147
var clientSelector : ( ) async throws -> ValkeyNodeClient = {
148
- try await self . nodeClient ( for: hashSlots )
148
+ try await self . nodeClient ( for: hashSlot . map { [ $0 ] } ?? [ ] )
149
149
}
150
150
151
151
while !Task. isCancelled {
@@ -178,8 +178,8 @@ public final class ValkeyClusterClient: Sendable {
178
178
isolation: isolated ( any Actor ) ? = #isolation,
179
179
operation: ( ValkeyConnection ) async throws -> sending Value
180
180
) async throws -> Value {
181
- let hashSlots = keys . map { HashSlot ( key : $0 ) }
182
- let node = try await self . nodeClient ( for: hashSlots )
181
+ let hashSlot = try self . hashSlot ( for : keys )
182
+ let node = try await self . nodeClient ( for: hashSlot . map { [ $0 ] } ?? [ ] )
183
183
return try await node. withConnection ( isolation: isolation, operation: operation)
184
184
}
185
185
@@ -234,6 +234,20 @@ public final class ValkeyClusterClient: Sendable {
234
234
235
235
// MARK: - Private methods -
236
236
237
+ /// Return HashSlot for collection of keys.
238
+ ///
239
+ /// If collection is empty return `nil`
240
+ /// If collection of keys use a variety of hash slot then throw an error
241
+ @usableFromInline
242
+ /* private */ func hashSlot( for keys: some Collection < ValkeyKey > ) throws -> HashSlot ? {
243
+ guard let firstKey = keys. first else { return nil }
244
+ let hashSlot = HashSlot ( key: firstKey)
245
+ for key in keys. dropFirst ( ) {
246
+ guard hashSlot == HashSlot ( key: key) else { throw ValkeyClusterError . keysInCommandRequireMultipleHashSlots }
247
+ }
248
+ return hashSlot
249
+ }
250
+
237
251
private func queueAction( _ action: RunAction ) {
238
252
self . actionStreamContinuation. yield ( action)
239
253
}
@@ -418,7 +432,7 @@ public final class ValkeyClusterClient: Sendable {
418
432
/// - `ValkeyClusterError.clusterIsUnavailable` if no healthy nodes are available
419
433
/// - `ValkeyClusterError.clusterIsMissingSlotAssignment` if the slot assignment cannot be determined
420
434
@inlinable
421
- func nodeClient( for slots: some ( Collection < HashSlot > & Sendable ) ) async throws -> ValkeyNodeClient {
435
+ func nodeClient( for slots: [ HashSlot ] ) async throws -> ValkeyNodeClient {
422
436
var retries = 0
423
437
while retries < 3 {
424
438
defer { retries += 1 }
0 commit comments