Skip to content

Commit 3a26585

Browse files
committed
Add pitch doc
1 parent 314532e commit 3a26585

File tree

1 file changed

+155
-2
lines changed

1 file changed

+155
-2
lines changed

README.md

Lines changed: 155 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,155 @@
1-
# nio-redis
2-
Non-blocking, event-driven Swift client for Redis.
1+
# NIORedis: Client for Redis server built on NIO
2+
This package includes two modules: `NIORedis` and `Redis`, which provide clients that handle connection to, authorizing, and
3+
executing commands against a Redis server.
4+
5+
`NIORedis` provides channel handlers for encoding / decoding between Swift native types and [Redis' Serialization Protocol (RESP)](https://redis.io/topics/protocol).
6+
7+
`Redis` is an abstraction layer that wraps `NIORedis` to be callback based with `DispatchQueue`.
8+
9+
# Motivation
10+
Implementations of Redis connections have decayed as newer capabilities of the Swift STD Library, SwiftNIO, and the Swift language itself have developed.
11+
12+
As part of the iniative of trying to push the ecosystem to be centered around SwiftNIO, a framework-agnostic driver on Redis can provide an
13+
easier time for feature development on Redis.
14+
15+
# Proposed Solution
16+
A barebones implementation is available at [mordil/nio-redis](https://github.com/mordil/nio-redis).
17+
18+
The following are already implemented, with unit tests:
19+
20+
- Connection and Authorization
21+
- Raw commands
22+
- Convienence methods for:
23+
- GET
24+
- SET
25+
- AUTH
26+
- DEL
27+
- SELECT
28+
- EXPIRE
29+
- NIO-wrapped abstractions for
30+
- Client
31+
- Connection
32+
- GET command
33+
- Unit tests for
34+
- Response decoding to native Swift
35+
- Message encoding to RESP
36+
- Connections
37+
- implemented commands
38+
39+
This package is a re-implementation of [vapor/redis](https://github.com/vapor/redis) stripped down to only build on SwiftNIO to be framework agnostic.
40+
41+
Much of this was inspired by the [NIOPostgres pitch](https://forums.swift.org/t/pitch-swiftnio-based-postgresql-client/18020).
42+
43+
# Details Solution
44+
45+
> **NOTE: This this is written against SwiftNIO 2.0, and as such requires Swift 5.0!**
46+
47+
This is to take advantage of the [`Result`](https://github.com/apple/swift-evolution/blob/master/proposals/0235-add-result.md) type in the `Redis` module,
48+
and to stay ahead of development of the next version of SwiftNIO.
49+
50+
## NIORedis
51+
Most use of this library will be focused on a `NIORedisConnection` type that works explicitly in a SwiftNIO `EventLoop` context - with
52+
return values all being `EventLoopFuture`.
53+
54+
```swift
55+
import NIORedis
56+
57+
let elg = MultiThreadedEventLoopGroup(numberOfThreads: 1)
58+
let redis = NIORedis(executionModel: .eventLoopGroup(elg))
59+
60+
// connections
61+
62+
// passing a value to `password` will automatically authenticate with Redis before resolving the connection
63+
let connection = try redis.makeConnection(
64+
hostname: "localhost", // this is the default
65+
port: 6379, // this is the default
66+
password: "MY_PASS" // default is `nil`
67+
).wait()
68+
print(connection) // NIORedisConnection
69+
70+
// convienence methods for commands
71+
72+
let result = try connection.set("my_key", to: "some value")
73+
.then {
74+
return connection.get("my_key")
75+
}.wait()
76+
print(result) // Optional("some value")
77+
78+
// raw commands
79+
80+
let keyCount = try connection.command("DEL", [RedisData(bulk: "my_key")])
81+
.thenThrowing { res in
82+
guard case let .integer(count) else {
83+
// throw Error
84+
}
85+
return count
86+
}.wait()
87+
print(keyCount) // 1
88+
89+
// cleanup
90+
91+
connection.close()
92+
try redis.terminate()
93+
try elg.syncShutdownGracefully()
94+
```
95+
96+
### RedisData & RedisDataConvertible
97+
This is a 1:1 mapping enum of the `RESP` types: `Simple String`, `Bulk String`, `Array`, `Integer` and `Error`.
98+
99+
Conforming to `RedisDataConvertible` allows Swift types to more easily convert between `RedisData` and native types.
100+
101+
`Array`, `Data`, `Float`, `Double`, `FixedWidthInteger`, `String`, and of course `RedisData` all conform in this package.
102+
103+
A `ByteToMessageDecoder` and `MessageToByteEncoder` are used for the conversion process on connections.
104+
105+
### NIORedisConnection
106+
This class uses a `ChannelInboundHandler` that handles the actual process of sending and receiving commands.
107+
108+
While it does handle a "pipeline" queue of messages, so as to not be blocking, it is _not_ the same as [Redis' Pipelining](https://redis.io/topics/pipelining).
109+
110+
That is a feature for future development.
111+
112+
## Redis
113+
114+
To support contexts where someone either doesn't want to work in a SwiftNIO context, the `Redis` module provides a callback-based interface
115+
that wraps all of `NIORedis`.
116+
117+
A `Redis` instance manages a `NIORedis` object under the hood, with `RedisConnection` doing the same for `NIORedisConnection`.
118+
119+
```swift
120+
import Redis
121+
122+
let redis = Redis(threadCount: 1) // default is 1
123+
124+
// connections
125+
126+
// passing a value to `password` will automatically authenticate with Redis before resolving the connection
127+
redis.makeConnection(
128+
hostname: "localhost", // this is the default
129+
port: 6379, // this is the default
130+
password: "MY_PASS", // default is `nil`
131+
queue: DispatchQueue(label: "com.MyPackage.redis") // default is `.main`
132+
) { result in
133+
switch result {
134+
case .success(let conn):
135+
showCommands(on: conn)
136+
case .failure(let error):
137+
fatalError("Could not create RedisConnection!")
138+
}
139+
}
140+
141+
// convienence methods for commands
142+
143+
func showCommands(on conn: RedisConnection) {
144+
conn.get("my_key") { result in
145+
switch result {
146+
case .success(let value):
147+
// use value, which is String?
148+
case .failure(let error):
149+
// do something on error
150+
}
151+
}
152+
}
153+
154+
// cleanup is handled by deinit blocks
155+
```

0 commit comments

Comments
 (0)