Skip to content

Commit 066a5c3

Browse files
committed
Resolve RedisPipeline Proposal Discussion Topic
Motivation: During the discussion thread of the NIORedis SSWG proposal, there were concerns expressed over the implementation of `RedisPipeline` that covered the following areas: 1. Clashes with SwiftNIO concepts such as "Pipeline" 2. Misleading users on library behavior with using a Pipeline vs. a single `RedisConnection` 3. The implementation was "too clever" in that it allowed users to easily find themselves in "Undefined Behavior" due to lack of enough type safety in the API However, the value in being able to control how frequently "flush" happens on a socket was discussed and considered high - so something still needs to be in place to force flushes by users. It was decided to leave the implementation of batching commands and their responses to library users, perhaps in higher level frameworks while the library will provide said mechanism for controlling writing and flushing of commands. Modifications: - Add: `sendCommandsImmediately` bool property to `RedisConnection` to handle choice of flushing after each command - Remove: `RedisPipeline` Results: Users should now have a more clear understanding on what type of control they have over the timing of when commands are actually sent over the wire, with the greater emphasis placed on `RedisConnection`.
1 parent 8bde6fd commit 066a5c3

File tree

4 files changed

+22
-222
lines changed

4 files changed

+22
-222
lines changed

Sources/NIORedis/RedisClient.swift

Lines changed: 22 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ private let loggingKeyID = "RedisConnection"
3636
/// A `RedisClient` implementation that represents an individual connection
3737
/// to a Redis database instance.
3838
///
39-
/// `RedisConnection` comes with logging and a method for creating `RedisPipeline` instances.
39+
/// `RedisConnection` comes with logging by default.
4040
///
4141
/// See `RedisClient`
4242
public final class RedisConnection: RedisClient {
@@ -49,10 +49,23 @@ public final class RedisConnection: RedisClient {
4949
public var eventLoop: EventLoop { return channel.eventLoop }
5050
/// Is the client still connected to Redis?
5151
public var isConnected: Bool { return state != .closed }
52+
/// Controls the timing behavior of sending commands over this connection. The default is `true`.
53+
///
54+
/// When set to `false`, the host will "queue" commands and determine when to send all at once,
55+
/// while `true` will force each command to be sent as soon as they are "queued".
56+
/// - Note: Setting this to `true` will trigger all "queued" commands to be sent.
57+
public var sendCommandsImmediately: Bool {
58+
get { return autoflush.load() }
59+
set(newValue) {
60+
if newValue { channel.flush() }
61+
autoflush.store(newValue)
62+
}
63+
}
5264

5365
private let channel: Channel
5466
private var logger: Logger
5567

68+
private let autoflush = Atomic<Bool>(value: true)
5669
private let _stateLock = Lock()
5770
private var _state: ConnectionState
5871
private var state: ConnectionState {
@@ -112,19 +125,12 @@ public final class RedisConnection: RedisClient {
112125
return result
113126
}
114127

115-
/// Creates a `RedisPipeline` for executing a batch of commands.
116-
/// - Note: The instance is given a `Logger` with the metadata property "RedisConnection"
117-
/// that contains the unique ID of the `RedisConnection` that created it.
128+
/// Sends commands to the Redis instance this connection is tied to.
118129
///
119-
/// - Returns: An `EventLoopFuture` resolving the `RedisPipeline` instance.
120-
public func makePipeline() -> RedisPipeline {
121-
var logger = Logger(label: "NIORedis.RedisPipeline")
122-
logger[metadataKey: loggingKeyID] = self.logger[metadataKey: loggingKeyID]
123-
124-
return RedisPipeline(channel: channel, logger: logger)
125-
}
126-
127130
/// See `RedisClient.send(command:with:)`
131+
///
132+
/// - Note: The timing of when commands are actually sent to Redis are controlled by
133+
/// the `sendCommandsImmediately` property.
128134
public func send(
129135
command: String,
130136
with arguments: [RESPValueConvertible]
@@ -148,8 +154,9 @@ public final class RedisConnection: RedisClient {
148154
}
149155
logger.debug("Sending command \"\(command)\" with \(arguments) encoded as \(args)")
150156

151-
_ = channel.writeAndFlush(context)
152-
153-
return promise.futureResult
157+
guard sendCommandsImmediately else {
158+
return channel.write(context).flatMap { promise.futureResult }
159+
}
160+
return channel.writeAndFlush(context).flatMap { promise.futureResult }
154161
}
155162
}

Sources/NIORedis/RedisPipeline.swift

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

Tests/NIORedisTests/RedisPipelineTests.swift

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

Tests/NIORedisTests/XCTestManifests.swift

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@ public func allTests() -> [XCTestCaseEntry] {
99
testCase(RESPTranslatorWritingTests.allTests),
1010
testCase(BasicCommandsTests.allTests),
1111
testCase(SetCommandsTests.allTests),
12-
testCase(RedisPipelineTests.allTests),
1312
testCase(HashCommandsTests.allTests),
1413
testCase(ListCommandsTests.allTests),
1514
testCase(SortedSetCommandsTests.allTests),

0 commit comments

Comments
 (0)