@@ -386,41 +386,9 @@ public final actor ValkeyConnection: ValkeyClientProtocol, Sendable {
386386 return try await _execute (
387387 buffer: encoder. buffer,
388388 promises: promises,
389- valkeyPromises: promises. map { . nio( $0) }
390- ) { promises -> Result < [ Result < RESPToken , Error > ] , any Error > in
391- let responses : EXEC . Response
392- do {
393- let execFutureResult = promises. last!. futureResult
394- responses = try await execFutureResult. get ( ) . decode ( as: EXEC . Response. self)
395- } catch let error as ValkeyClientError where error. errorCode == . commandError {
396- // we received an error while running the EXEC command. Extract queuing
397- // results and throw error
398- var results : [ Result < RESPToken , Error > ] = . init( )
399- results. reserveCapacity ( promises. count - 2 )
400- for promise in promises [ 1 ..< ( promises. count - 1 ) ] {
401- results. append ( await promise. futureResult. _result ( ) )
402- }
403- return . failure( ValkeyTransactionError . transactionErrors ( queuedResults: results, execError: error) )
404- } catch {
405- return . failure( error)
406- }
407- // If EXEC returned nil then transaction was aborted because a
408- // WATCHed variable changed
409- guard let responses else {
410- return . failure( ValkeyTransactionError . transactionAborted)
411- }
412- // We convert all the RESP errors in the response from EXEC to Result.failure
413- return . success(
414- responses. map {
415- switch $0. identifier {
416- case . simpleError, . bulkError:
417- . failure( ValkeyClientError ( . commandError, message: $0. errorString. map { Swift . String ( buffer: $0) } ) )
418- default :
419- . success( $0)
420- }
421- }
422- )
423- } . get ( )
389+ valkeyPromises: promises. map { . nio( $0) } ,
390+ processResults: self . _processTransactionPromises
391+ ) . get ( )
424392 }
425393
426394 /// Pipeline a series of commands to Valkey connection and precede each command with an ASKING
@@ -468,6 +436,48 @@ public final actor ValkeyConnection: ValkeyClientProtocol, Sendable {
468436 }
469437 }
470438
439+ /// Pipeline a series of commands as a transaction preceded with an ASKING command
440+ ///
441+ /// Once all the responses for the commands have been received the function returns
442+ /// an array of RESPToken Results, one for each command.
443+ ///
444+ /// This is an internal function used by the cluster client
445+ ///
446+ /// - Parameter commands: Collection of ValkeyCommands
447+ /// - Returns: Array holding the RESPToken responses of all the commands
448+ @usableFromInline
449+ func transactionWithAsk(
450+ _ commands: some Collection < any ValkeyCommand >
451+ ) async throws -> [ Result < RESPToken , any Error > ] {
452+ self . logger. trace ( " transaction asking " , metadata: [ " commands " : . string( Self . concatenateCommandNames ( commands) ) ] )
453+ var promises : [ EventLoopPromise < RESPToken > ] = [ ]
454+ promises. reserveCapacity ( commands. count)
455+ var valkeyPromises : [ ValkeyPromise < RESPToken > ] = [ ]
456+ valkeyPromises. reserveCapacity ( commands. count + 3 )
457+ var encoder = ValkeyCommandEncoder ( )
458+ ASKING ( ) . encode ( into: & encoder)
459+ MULTI ( ) . encode ( into: & encoder)
460+ promises. append ( channel. eventLoop. makePromise ( of: RESPToken . self) )
461+ valkeyPromises. append ( . forget)
462+ valkeyPromises. append ( . nio( promises. last!) )
463+
464+ for command in commands {
465+ command. encode ( into: & encoder)
466+ promises. append ( channel. eventLoop. makePromise ( of: RESPToken . self) )
467+ valkeyPromises. append ( . nio( promises. last!) )
468+ }
469+ EXEC ( ) . encode ( into: & encoder)
470+ promises. append ( channel. eventLoop. makePromise ( of: RESPToken . self) )
471+ valkeyPromises. append ( . nio( promises. last!) )
472+
473+ return try await _execute (
474+ buffer: encoder. buffer,
475+ promises: promises,
476+ valkeyPromises: valkeyPromises,
477+ processResults: self . _processTransactionPromises
478+ ) . get ( )
479+ }
480+
471481 /// Execute stream of commands written into buffer
472482 ///
473483 /// The function is provided with an array of EventLoopPromises for the responses of commands
@@ -498,6 +508,44 @@ public final actor ValkeyConnection: ValkeyClientProtocol, Sendable {
498508 }
499509 }
500510
511+ @usableFromInline
512+ func _processTransactionPromises(
513+ _ promises: [ EventLoopPromise < RESPToken > ]
514+ ) async -> sending Result< [ Result < RESPToken , Error > ] , any Error > {
515+ let responses : EXEC . Response
516+ do {
517+ let execFutureResult = promises. last!. futureResult
518+ responses = try await execFutureResult. get ( ) . decode ( as: EXEC . Response. self)
519+ } catch let error as ValkeyClientError where error. errorCode == . commandError {
520+ // we received an error while running the EXEC command. Extract queuing
521+ // results and throw error
522+ var results : [ Result < RESPToken , Error > ] = . init( )
523+ results. reserveCapacity ( promises. count - 2 )
524+ for promise in promises [ 1 ..< ( promises. count - 1 ) ] {
525+ results. append ( await promise. futureResult. _result ( ) )
526+ }
527+ return . failure( ValkeyTransactionError . transactionErrors ( queuedResults: results, execError: error) )
528+ } catch {
529+ return . failure( error)
530+ }
531+ // If EXEC returned nil then transaction was aborted because a
532+ // WATCHed variable changed
533+ guard let responses else {
534+ return . failure( ValkeyTransactionError . transactionAborted)
535+ }
536+ // We convert all the RESP errors in the response from EXEC to Result.failure
537+ return . success(
538+ responses. map {
539+ switch $0. identifier {
540+ case . simpleError, . bulkError:
541+ . failure( ValkeyClientError ( . commandError, message: $0. errorString. map { Swift . String ( buffer: $0) } ) )
542+ default :
543+ . success( $0)
544+ }
545+ }
546+ )
547+ }
548+
501549 #if DistributedTracingSupport
502550 @usableFromInline
503551 func applyCommonAttributes( to attributes: inout SpanAttributes , commandName: String ) {
0 commit comments