Skip to content

Commit 65d0afd

Browse files
committed
Add RedisDriver to eventually replace NIORedis that works more deeply with NIO
1 parent 2d95af5 commit 65d0afd

File tree

2 files changed

+81
-0
lines changed

2 files changed

+81
-0
lines changed

Sources/NIORedis/NIORedis.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import NIO
22
import NIOConcurrencyHelpers
33

44
/// A factory that handles all necessary details for creating connections to a Redis database instance.
5+
@available(*, deprecated)
56
public final class NIORedis {
67
/// The threading model to use for asynchronous tasks.
78
///

Sources/NIORedis/RedisDriver.swift

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
import NIO
2+
import NIOConcurrencyHelpers
3+
4+
public final class RedisDriver {
5+
/// The threading model to use for asynchronous tasks.
6+
///
7+
/// Using `.eventLoopGroup` will allow an external provider to handle the lifetime of the `EventLoopGroup`,
8+
/// while using `spawnThreads` will cause this `NIORedis` instance to handle the lifetime of a new `EventLoopGroup`.
9+
public enum ExecutionModel {
10+
case spawnThreads(Int)
11+
case eventLoopGroup(EventLoopGroup)
12+
}
13+
14+
private let executionModel: ExecutionModel
15+
private let eventLoopGroup: EventLoopGroup
16+
17+
private let isRunning = Atomic<Bool>(value: true)
18+
19+
deinit { assert(!isRunning.load(), "Redis driver was not properly shut down!") }
20+
21+
/// Creates a driver instance to create connections to a Redis.
22+
/// - Important: Call `terminate()` before deinitializing to properly cleanup resources.
23+
/// - Parameter executionModel: The model to use for handling connection resources.
24+
public init(executionModel model: ExecutionModel) {
25+
self.executionModel = model
26+
27+
switch model {
28+
case .spawnThreads(let count):
29+
self.eventLoopGroup = MultiThreadedEventLoopGroup(numberOfThreads: count)
30+
case .eventLoopGroup(let group):
31+
self.eventLoopGroup = group
32+
}
33+
}
34+
35+
/// Handles the proper shutdown of managed resources.
36+
/// - Important: This method should always be called, even when running in `.eventLoopGroup` mode.
37+
public func terminate() throws {
38+
guard isRunning.exchange(with: false) else { return }
39+
40+
switch executionModel {
41+
case .spawnThreads: try self.eventLoopGroup.syncShutdownGracefully()
42+
case .eventLoopGroup: return
43+
}
44+
}
45+
46+
/// Creates a new `RedisConnection` with the parameters provided.
47+
public func makeConnection(
48+
hostname: String = "localhost",
49+
port: Int = 6379,
50+
password: String? = nil
51+
) -> EventLoopFuture<RedisConnection> {
52+
let bootstrap = ClientBootstrap(group: eventLoopGroup)
53+
.channelOption(ChannelOptions.socket(SocketOptionLevel(SOL_SOCKET), SO_REUSEADDR), value: 1)
54+
.channelInitializer { channel in
55+
return channel.pipeline.addHandlers(
56+
RESPEncoder(),
57+
ByteToMessageHandler(RESPDecoder()),
58+
RedisCommandHandler()
59+
)
60+
}
61+
62+
return bootstrap.connect(host: hostname, port: port)
63+
.map { return RedisConnection(channel: $0) }
64+
.then { connection in
65+
guard let pw = password else {
66+
return self.eventLoopGroup.next().makeSucceededFuture(result: connection)
67+
}
68+
69+
#warning("TODO - Fix command extensions")
70+
return self.eventLoopGroup.next().makeSucceededFuture(result: connection)
71+
// return connection.authorize(with: pw).map { _ in return connection }
72+
}
73+
}
74+
}
75+
76+
private extension ChannelPipeline {
77+
func addHandlers(_ handlers: ChannelHandler...) -> EventLoopFuture<Void> {
78+
return EventLoopFuture<Void>.andAll(handlers.map { add(handler: $0) }, eventLoop: eventLoop)
79+
}
80+
}

0 commit comments

Comments
 (0)