1
1
# Aedis
2
2
3
3
Aedis is a [ Redis] ( https://redis.io/ ) client library built on top of
4
- [ Asio] ( https://www.boost.org/doc/libs/release/doc/html/boost_asio.html )
4
+ [ Boost. Asio] ( https://www.boost.org/doc/libs/release/doc/html/boost_asio.html )
5
5
that implements the latest version of the Redis communication
6
6
protocol
7
7
[ RESP3] ( https://github.com/redis/redis-specifications/blob/master/protocol/RESP3.md ) .
8
- It makes communication with a Redis server easy by hiding some of
9
- the low-level Asio-related code away from the user, which in the majority of
10
- the cases will be concerned with only three library entities
8
+ It makes communication with a Redis server easy by hiding low-level
9
+ Asio-related code away from the user, which, in the majority of the
10
+ cases will be concerned with only three library entities
11
11
12
12
* ` aedis::connection ` : A connection to the Redis server.
13
13
* ` aedis::resp3::request ` : A container of Redis commands.
14
- * ` aedis::adapt() ` : Adapts data structures to receive responses.
14
+ * ` aedis::adapt() ` : A function that adapts data structures to receive responses.
15
15
16
- For example, the coroutine below uses a short-lived connection to read Redis
17
- [ hashes] ( https://redis.io/docs/data-types/hashes/ )
18
- in a ` std::map `
16
+ The requirements for using Aedis are
17
+
18
+ * Boost 1.80 or greater.
19
+ * C++17 minimum.
20
+ * Redis 6 or higher (must support RESP3).
21
+ * Have basic-level knowledge about Redis and understand Asio and its asynchronous model.
22
+
23
+ Readers that are not familiar with Redis can learn more about
24
+ it on https://redis.io/docs/ , in essence
25
+
26
+ > Redis is an open source (BSD licensed), in-memory data structure
27
+ > store used as a database, cache, message broker, and streaming
28
+ > engine. Redis provides data structures such as strings, hashes,
29
+ > lists, sets, sorted sets with range queries, bitmaps, hyperloglogs,
30
+ > geospatial indexes, and streams. Redis has built-in replication, Lua
31
+ > scripting, LRU eviction, transactions, and different levels of
32
+ > on-disk persistence, and provides high availability via Redis
33
+ > Sentinel and automatic partitioning with Redis Cluster.
34
+
35
+ <a name =" connection " ></a >
36
+ ## Connection
37
+
38
+ Let us start with a simple application that uses a short-lived
39
+ connection to read Redis
40
+ [ hashes] ( https://redis.io/docs/data-types/hashes/ ) in a ` std::map `
19
41
20
42
``` cpp
21
43
auto async_main () -> net::awaitable<void>
@@ -31,27 +53,21 @@ auto async_main() -> net::awaitable<void>
31
53
req.push("HGETALL", "hset-key");
32
54
req.push("QUIT");
33
55
34
- // Responses as tuple elements.
56
+ // The tuple elements below will store the response to each
57
+ // individual command. The responses to HELLO and QUIT are being
58
+ // ignored for simplicity.
35
59
std::tuple<ignore, std::map<std::string, std::string>, ignore> resp;
36
60
37
- // Executes the request and reads the response .
61
+ // Executes the request. See below why we are using operator || .
38
62
co_await (conn->async_run() || conn->async_exec(req, adapt(resp)));
39
-
40
63
// Use the map from std::get<1>(resp) ...
41
64
}
42
65
```
43
66
44
- For other versions of this example that use different styles see
45
-
46
- * cpp20_intro.cpp: Does not use awaitable operators.
47
- * cpp20_intro_awaitable_ops.cpp: The version from above.
48
- * cpp17_intro.cpp: Uses callbacks and requires C++17.
49
- * cpp20_intro_tls.cpp: Communicates over TLS.
50
-
51
- The execution of ` connection::async_exec ` in the example above is composed with
52
- ` connection::async_run ` with the aid of the Asio awaitable ` operator || `
53
- that ensures that one operation is cancelled as soon as the other
54
- completes. These functions play the following roles
67
+ The example above uses the Asio awaitable ` operator || ` to launch
68
+ ` connection::async_exec ` and ` connection::async_run ` in parallel and
69
+ to cancel one of the operations when the other completes. The role
70
+ played by these functions are
55
71
56
72
* ` connection::async_exec ` : Execute commands by queuing the request
57
73
for writing and wait for the response sent back by
@@ -62,11 +78,12 @@ completes. These functions play the following roles
62
78
server-push is received. It will also trigger writes of pending
63
79
requests when a reconnection occurs.
64
80
65
- The role played by ` async_run ` can be better understood in the context
66
- of long-lived connections, which we will cover in the next section.
81
+ The example above is also available in other programming styles for comparison
67
82
68
- <a name =" connection " ></a >
69
- ## Connection
83
+ * cpp20_intro.cpp: Does not use awaitable operators.
84
+ * cpp20_intro_awaitable_ops.cpp: The version from above.
85
+ * cpp17_intro.cpp: Uses callbacks and requires C++17.
86
+ * cpp20_intro_tls.cpp: Communicates over TLS.
70
87
71
88
For performance reasons we will usually want to perform multiple
72
89
requests in the same connection. We can do this in the example above
@@ -132,10 +149,7 @@ auto run(std::shared_ptr<connection> conn) -> net::awaitable<void>
132
149
}
133
150
```
134
151
135
- Here we use Asio awaitable operators for simplicity, the same
136
- functionality can be achieved by means of the
137
- ` aedis::connection::cancel ` function. The definition of the
138
- ` healthy_checker ` used above can be found in common.cpp.
152
+ The definition of the ` healthy_checker ` used above can be found in common.cpp.
139
153
140
154
### Server pushes
141
155
@@ -146,8 +160,8 @@ them are
146
160
* [ Keyspace notification] ( https://redis.io/docs/manual/keyspace-notifications/ )
147
161
* [ Client-side caching] ( https://redis.io/docs/manual/client-side-caching/ )
148
162
149
- The connection class supports that by means of the
150
- ` aedis::connection::async_receive ` function
163
+ The connection class supports server pushes by means of the
164
+ ` aedis::connection::async_receive ` function, for example
151
165
152
166
``` cpp
153
167
auto receiver (std::shared_ptr<connection > conn) -> net::awaitable<void >
@@ -161,8 +175,8 @@ auto receiver(std::shared_ptr<connection> conn) -> net::awaitable<void>
161
175
}
162
176
```
163
177
164
- This function can be also easily incorporated in the run function from
165
- above, for example
178
+ The `receiver` defined above can be run detached or incorporated
179
+ in the `run` function as we did for the `healthy_checker`
166
180
167
181
```cpp
168
182
auto run(std::shared_ptr<connection> conn) -> net::awaitable<void>
@@ -207,26 +221,13 @@ to force a failover). It would be annoying if each individual section
207
221
were to throw exceptions and handle error. With the pattern shown
208
222
above the only place that has to managed is the run function.
209
223
210
- At this point the reasons for why `async_run` was introduced in Aedis
211
- might have become apparent to the reader
212
-
213
- * Provide quick reaction to disconnections and hence faster failovers.
214
- * Support server pushes and requests in the same connection object, concurrently.
215
- * Separate requests, handling of server pushes and reconnect operations.
216
-
217
224
### Cancellation
218
225
219
226
Aedis supports both implicit and explicit cancellation of connection
220
227
operations. Explicit cancellation is supported by means of the
221
- `aedis::connection::cancel` member function. Implicit cancellation,
222
- like those that may happen when using Asio awaitable operators `&&` and
223
- `||` will be discussed with more detail below.
224
-
225
- ```cpp
226
- co_await (conn.async_run(...) && conn.async_exec(...))
227
- ```
228
-
229
- * Provides a simple way to send HELLO and perform channel subscription.
228
+ `aedis::connection::cancel` member function. Implicit
229
+ terminal-cancellation, like those that happen when using Asio
230
+ awaitable `operator ||` will be discussed with more detail below.
230
231
231
232
```cpp
232
233
co_await (conn.async_run(...) || conn.async_exec(...))
@@ -244,20 +245,14 @@ co_await (conn.async_exec(...) || time.async_wait(...))
244
245
* NOTE: It is usually a better idea to have a healthy checker than adding
245
246
per request timeout, see cpp20_subscriber.cpp for an example.
246
247
247
- ``` cpp
248
- co_await (conn.async_run(...) || time.async_wait(...))
249
- ```
250
-
251
- * Sets a limit on how long the connection should live.
252
-
253
248
```cpp
254
249
co_await (conn.async_exec(...) || conn.async_exec(...) || ... || conn.async_exec(...))
255
250
```
256
251
257
252
* This works but is unnecessary. Unless the user has set
258
253
` aedis::resp3::request::config::coalesce ` to ` false ` , and he
259
254
usually shouldn't, the connection will automatically merge the
260
- individual requests into a single payload anyway .
255
+ individual requests into a single payload.
261
256
262
257
<a name =" requests " ></a >
263
258
## Requests
@@ -334,7 +329,7 @@ reader is advised to read it carefully.
334
329
Aedis uses the following strategy to support Redis responses
335
330
336
331
* **Static**: For `aedis::resp3::request` whose sizes are known at compile time
337
- std::tuple is supported.
332
+ ` std::tuple` is supported.
338
333
* **Dynamic**: Otherwise use `std::vector<aedis::resp3::node<std::string>>`.
339
334
340
335
For example, below is a request with a compile time size
@@ -717,10 +712,10 @@ stars, namely
717
712
718
713
### Aedis vs Redis-plus-plus
719
714
720
- Before we start it is important to mentioning some of the things
715
+ Before we start it is important to mention some of the things
721
716
redis-plus-plus does not support
722
717
723
- * The latest version of the communication protocol RESP3. Without it it is impossible to support some important Redis features like client side caching, among other things.
718
+ * The latest version of the communication protocol RESP3. Without that it is impossible to support some important Redis features like client side caching, among other things.
724
719
* Coroutines.
725
720
* Reading responses directly in user data structures to avoid creating temporaries.
726
721
* Error handling with support for error-code.
@@ -817,7 +812,7 @@ It is also not clear how are pipelines realised with this design
817
812
## Reference
818
813
819
814
* [High-Level](#high-level-api): Covers the topics discussed in this document.
820
- * [Low-Level](#low-level-api): Covers low-level building blocks. Provided mostly for developers, most users won' t need any information provided here.
815
+ * [Low-Level](#low-level-api): Covers low-level building blocks. Provided mostly for developers, users won' t usually need any information provided here.
821
816
822
817
## Installation
823
818
@@ -837,13 +832,6 @@ examples and test cmake is supported, for example
837
832
BOOST_ROOT=/opt/boost_1_80_0 cmake --preset dev
838
833
```
839
834
840
- The requirements for using Aedis are
841
-
842
- - Boost 1.80 or greater.
843
- - C++17 minimum.
844
- - Redis 6 or higher (must support RESP3).
845
- - Optionally also redis-cli and Redis Sentinel.
846
-
847
835
The following compilers are supported
848
836
849
837
- Gcc: 10, 11, 12.
0 commit comments