77//
88import NIOCore
99
10- /// Represents a `MOVED` redirection error from a Valkey cluster node.
10+ /// Represents a redirection error from a Valkey cluster node.
1111///
1212/// When a client sends a command to a Valkey cluster node that doesn't own
1313/// the hash slot for the specified key, the node responds with a `MOVED` error
1414/// containing information about which node actually owns that slot.
1515///
16+ /// When a client sends a command to a Valkey cluster node for a hash slot that
17+ /// is currently migrating for a key that does not exist the node responds
18+ /// with a `ASK` error containing information about which node is importing
19+ /// that hash slot
20+ ///
1621/// This error provides the necessary information for clients to redirect their
1722/// request to the correct node in the cluster.
1823@usableFromInline
19- package struct ValkeyMovedError : Hashable , Sendable {
24+ package struct ValkeyClusterRedirectionError : Hashable , Sendable {
25+ @usableFromInline
26+ package enum Redirection : Sendable {
27+ case move
28+ case ask
29+ }
30+
31+ /// Request type
32+ @usableFromInline
33+ package var redirection : Redirection
34+
2035 /// The hash slot number that triggered the redirection.
2136 package var slot : HashSlot
2237
@@ -26,7 +41,8 @@ package struct ValkeyMovedError: Hashable, Sendable {
2641 /// The port number of the node that owns the requested hash slot.
2742 package var port : Int
2843
29- package init ( slot: HashSlot , endpoint: String , port: Int ) {
44+ package init ( request: Redirection , slot: HashSlot , endpoint: String , port: Int ) {
45+ self . redirection = request
3046 self . slot = slot
3147 self . endpoint = endpoint
3248 self . port = port
@@ -38,25 +54,33 @@ package struct ValkeyMovedError: Hashable, Sendable {
3854 }
3955}
4056
41- extension ValkeyMovedError {
57+ extension ValkeyClusterRedirectionError {
4258 static let movedPrefix = " MOVED "
59+ static let askPrefix = " ASK "
4360
44- /// Attempts to parse a Valkey MOVED error from a String.
61+ /// Attempts to parse a Valkey MOVED/ASK error from a String.
4562 ///
4663 /// This method extracts the hash slot, endpoint, and port information from the string
47- /// if it represents a Valkey MOVED error. MOVED errors are returned by Valkey cluster
48- /// nodes when a client attempts to access a key that belongs to a different node.
64+ /// if it represents a Valkey MOVED/ASK error. Redirection errors are returned by Valkey
65+ /// cluster nodes when a client attempts to access a key that belongs to a different node
66+ /// or the hashslot is currently migrating.
4967 ///
5068 /// The error format is expected to be: `"MOVED <slot> <endpoint>:<port>"`
5169 ///
52- /// - Returns: A `ValkeyMovedError ` if the token represents a valid MOVED error, or `nil` otherwise.
70+ /// - Returns: A `ValkeyClusterRedirectionError ` if the token represents a valid MOVED/ASK error, or `nil` otherwise.
5371 @usableFromInline
5472 init ? ( _ errorMessage: String ) {
55- guard errorMessage. hasPrefix ( Self . movedPrefix) else {
73+ let msg : String . SubSequence
74+ let request : Redirection
75+ if errorMessage. hasPrefix ( Self . movedPrefix) {
76+ msg = errorMessage. dropFirst ( Self . movedPrefix. count)
77+ request = . move
78+ } else if errorMessage. hasPrefix ( Self . askPrefix) {
79+ msg = errorMessage. dropFirst ( Self . askPrefix. count)
80+ request = . ask
81+ } else {
5682 return nil
5783 }
58-
59- let msg = errorMessage. dropFirst ( Self . movedPrefix. count)
6084 guard let spaceAfterSlotIndex = msg. firstIndex ( where: { $0 == " " } ) else {
6185 return nil
6286 }
@@ -79,6 +103,6 @@ extension ValkeyMovedError {
79103 return nil
80104 }
81105
82- self = ValkeyMovedError ( slot: slot, endpoint: Swift . String ( endpoint) , port: port)
106+ self = ValkeyClusterRedirectionError ( request : request , slot: slot, endpoint: Swift . String ( endpoint) , port: port)
83107 }
84108}
0 commit comments