Skip to content

Commit 3c67130

Browse files
committed
Refactor RedisCommand into a general use object
Motivation: RediStack today represents a command as a temporary object for the purpose of writing to the channel. While it is useful to have an object for that purpose, commands handled in this way require immediate execution and aren't available for other purposes. Commands can serve a better purpose as a lightweight object to support delayed execution, so that pipeling as described in issue #63 could be possible. Modifications: - Add: `get` overloads for JSON codable interaction on `RedisClient` - Add: New `RedisZRangeResultOption` type for better interactions with zrange operations that can optionally return scores - Add: New `RedisHashFieldKey` for type-safety when working with Hash field keys much like `RedisKey` - Change: A few API types from enums to structs for library evolution - Change: `RedisCommandHandler` to operate on a tuple of `RESPValue, EventLoopPromise<RESPValue>` rather than `RedisCommand` - Change: `RedisCommand` to be a generic struct with the keyword, arguments, and a transform closure to defer execution - Change: Almost all `RedisClient` command extensions to be factory methods on `RedisCommand` instead - Change: Many response types to be optional to avoid developers having to do `isNull` checks on their own - Change: `RedisClient.send(command:arguments:)` to be generic with `send(_:)` as the signature - Rename: RedisClient extensions for scan methods to be more discoverable and legible as `scanHashField`, etc. Result: It should be easier to support a clean pipelining API with deferred command execution, with extensions being easier for 2nd party developers, and the maintenance overhead of all of the command extensions should be a little lighter when it comes to changing HOW commands are sent such as adding a context parameter
1 parent 61fb40b commit 3c67130

32 files changed

+3308
-4615
lines changed

Sources/RediStack/ChannelHandlers/RedisCommandHandler.swift

Lines changed: 13 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -14,27 +14,18 @@
1414

1515
import NIO
1616

17-
/// The `NIO.ChannelOutboundHandler.OutboundIn` type for `RedisCommandHandler`.
18-
///
19-
/// This holds the full command message to be sent to Redis, and an `NIO.EventLoopPromise` to be fulfilled when a response has been received.
20-
/// - Important: This struct has _reference semantics_ due to the retention of the `NIO.EventLoopPromise`.
21-
public struct RedisCommand {
22-
/// A message waiting to be sent to Redis. A full message contains a command keyword and its arguments stored as a single `RESPValue.array`.
23-
public let message: RESPValue
24-
/// A promise to be fulfilled with the sent message's response from Redis.
25-
public let responsePromise: EventLoopPromise<RESPValue>
26-
27-
public init(message: RESPValue, responsePromise promise: EventLoopPromise<RESPValue>) {
28-
self.message = message
29-
self.responsePromise = promise
30-
}
31-
}
32-
3317
/// An object that operates in a First In, First Out (FIFO) request-response cycle.
3418
///
3519
/// `RedisCommandHandler` is a `NIO.ChannelDuplexHandler` that sends `RedisCommand` instances to Redis,
3620
/// and fulfills the command's `NIO.EventLoopPromise` as soon as a `RESPValue` response has been received from Redis.
3721
public final class RedisCommandHandler {
22+
/// The data payload that the command handler is expecting to receive in the channel to process sending to Redis.
23+
/// ## message
24+
/// This value is expected to be a fully serialized command with it's keyword and arguments in a bulk string array ready to be sent to Redis as-is.
25+
/// ## responsePromise
26+
/// This is a `NIO.EventLoopPromise` that will be resolved once a response from Redis has been received.
27+
public typealias OutboundCommandPayload = (message: RESPValue, responsePromise: EventLoopPromise<RESPValue>)
28+
3829
/// FIFO queue of promises waiting to receive a response value from a sent command.
3930
private var commandResponseQueue: CircularBuffer<EventLoopPromise<RESPValue>>
4031
private var state: State = .default
@@ -115,27 +106,25 @@ extension RedisCommandHandler: ChannelInboundHandler {
115106
// MARK: ChannelOutboundHandler
116107

117108
extension RedisCommandHandler: ChannelOutboundHandler {
118-
/// See `NIO.ChannelOutboundHandler.OutboundIn`
119-
public typealias OutboundIn = RedisCommand
120-
/// See `NIO.ChannelOutboundHandler.OutboundOut`
109+
public typealias OutboundIn = OutboundCommandPayload
121110
public typealias OutboundOut = RESPValue
122111

123112
/// Invoked by SwiftNIO when a `write` has been requested on the `Channel`.
124113
///
125-
/// This unwraps a `RedisCommand`, storing the `NIO.EventLoopPromise` in a command queue,
114+
/// This unwraps a `OutboundCommandPayload` tuple, storing the `NIO.EventLoopPromise` in a command queue
126115
/// to fulfill later with the response to the command that is about to be sent through the `NIO.Channel`.
127116
///
128117
/// See `NIO.ChannelOutboundHandler.write(context:data:promise:)`
129118
public func write(context: ChannelHandlerContext, data: NIOAny, promise: EventLoopPromise<Void>?) {
130-
let commandContext = self.unwrapOutboundIn(data)
119+
let commandPayload = self.unwrapOutboundIn(data)
131120

132121
switch self.state {
133-
case let .error(e): commandContext.responsePromise.fail(e)
122+
case let .error(e): commandPayload.responsePromise.fail(e)
134123

135124
case .default:
136-
self.commandResponseQueue.append(commandContext.responsePromise)
125+
self.commandResponseQueue.append(commandPayload.responsePromise)
137126
context.write(
138-
self.wrapOutboundOut(commandContext.message),
127+
self.wrapOutboundOut(commandPayload.message),
139128
promise: promise
140129
)
141130
}

Sources/RediStack/Commands/BasicCommands.swift

Lines changed: 0 additions & 244 deletions
This file was deleted.

0 commit comments

Comments
 (0)