Skip to content

Commit 56bcdb7

Browse files
committed
Improvements in the docs.
1 parent 73ad66e commit 56bcdb7

File tree

4 files changed

+120
-91
lines changed

4 files changed

+120
-91
lines changed

README.md

Lines changed: 95 additions & 78 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ auto async_main() -> net::awaitable<void>
3232
req.push("QUIT");
3333

3434
// Responses as tuple elements.
35-
std::tuple<aedis::ignore, std::map<std::string, std::string>, aedis::ignore> resp;
35+
std::tuple<ignore, std::map<std::string, std::string>, ignore> resp;
3636

3737
// Executes the request and reads the response.
3838
co_await (conn->async_run() || conn->async_exec(req, adapt(resp)));
@@ -41,129 +41,135 @@ auto async_main() -> net::awaitable<void>
4141
}
4242
```
4343

44-
For different versions of this example using different styles see
44+
For other versions of this example that use different styles see
4545

46-
* cpp20_intro.cpp: Does not use awaitable operators
47-
* cpp20_intro_awaitable_ops.cpp: The version above.
48-
* cpp17_intro.cpp: Requires C++17 only.
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.
4949
* cpp20_intro_tls.cpp: Communicates over TLS.
5050

5151
The execution of `connection::async_exec` above is composed with
5252
`connection::async_run` with the aid of the Asio awaitable `operator ||`
5353
that ensures that one operation is cancelled as soon as the other
54-
completes, these functions play the following roles
54+
completes. These functions play the following roles
5555

5656
* `connection::async_exec`: Execute commands by queuing the request
5757
for writing. It will wait for the response sent back by Redis and
5858
can be called from multiple places in your code concurrently.
5959
* `connection::async_run`: Coordinate low-level read and write
6060
operations. More specifically, it will hand IO control to
61-
`async_exec` when a response arrives, to
62-
`async_receive` when a server-push is received
63-
and will trigger writes of pending requests when a reconnection
64-
occurs.
61+
`async_exec` when a response arrives and to `async_receive` when a
62+
server-push is received. It will also trigger writes of pending
63+
requests when a reconnection occurs.
6564

6665
The role played by `async_run` can be better understood in the context
6766
of long-lived connections, which we will cover in the next section.
68-
Before that however, the reader might want to skim over some further examples
69-
70-
* cpp20_containers.cpp: Shows how to send and receive STL containers and how to use transactions.
71-
* cpp20_serialization.cpp: Shows how to serialize types using Boost.Json.
72-
* cpp20_resolve_with_sentinel.cpp: Shows how to resolve a master address using sentinels.
73-
* cpp20_subscriber.cpp: Shows how to implement pubsub with reconnection re-subscription.
74-
* cpp20_echo_server.cpp: A simple TCP echo server.
75-
* cpp20_chat_room.cpp: A command line chat built on Redis pubsub.
76-
* cpp20_low_level_async.cpp: Sends a ping asynchronously using the low-level API.
77-
* cpp17_low_level_sync.cpp: Sends a ping synchronously using the low-level API.
78-
79-
To avoid repetition code that is common to some examples has been
80-
grouped in common.hpp. The main function used in some async examples
81-
has been factored out in the main.cpp file.
8267

8368
<a name="connection"></a>
8469
## Connection
8570

8671
For performance reasons we will usually want to perform multiple
87-
requests on the same connection. We can do this with the example above
88-
by decoupling the `HELLO` command and the call to `async_run` in a
89-
separate coroutine
72+
requests in the same connection. We can do this in the example above
73+
by letting `async_run` run detached in a separate coroutine, for
74+
example (see cpp20_intro.cpp)
9075

9176
```cpp
9277
auto run(std::shared_ptr<connection> conn) -> net::awaitable<void>
9378
{
9479
co_await connect(conn, "127.0.0.1", "6379");
80+
co_await conn->async_run();
81+
}
9582

83+
auto hello(std::shared_ptr<connection> conn) -> net::awaitable<void>
84+
{
9685
resp3::request req;
97-
req.push("HELLO", 3); // Upgrade to RESP3
86+
req.push("HELLO", 3);
9887

99-
// Notice we use && instead of || so async_run is not cancelled
100-
// when the HELLO response arrives. We are also ignoring the
101-
// response for simplicity.
102-
co_await (conn->async_run() && conn->async_exec(req));
88+
co_await conn->async_exec(req);
10389
}
104-
```
105-
We can now let `run` run detached in the background while other
106-
coroutines perform requests on the connection, for example
10790

108-
```cpp
109-
auto async_main() -> net::awaitable<void>
91+
auto ping(std::shared_ptr<connection> conn) -> net::awaitable<void>
11092
{
111-
auto conn = std::make_shared<connection>(co_await net::this_coro::executor);
112-
113-
// Run detached.
114-
net::co_spawn(ex, run(conn), net::detached);
115-
116-
// Here we can use the connection to perform requests and pass it
117-
// around to other coroutines so they can make requests.
118-
11993
resp3::request req;
12094
req.push("PING", "Hello world");
121-
co_await conn->async_exec(req);
95+
req.push("QUIT");
12296

123-
...
97+
std::tuple<std::string, aedis::ignore> resp;
98+
co_await conn->async_exec(req, adapt(resp));
99+
// Use the response ...
100+
}
124101

125-
// Cancels the run operation so we can exit.
126-
conn->cancel(operation::run);
102+
auto async_main() -> net::awaitable<void>
103+
{
104+
auto ex = co_await net::this_coro::executor;
105+
auto conn = std::make_shared<connection>(ex);
106+
net::co_spawn(ex, run(conn), net::detached);
107+
co_await hello(conn);
108+
co_await ping(conn);
109+
110+
// Here we can pass conn to other coroutines that need to
111+
// communicate with Redis.
127112
}
128113
```
129114
130-
With this separation, it is now easy to incorporate other operations
131-
in our application, for example, to cancel the connection on `SIGINT`
132-
and `SIGTERM` we can extend `run` as follows
115+
With this separation, it is now easy to incorporate other long-running
116+
operations in our application, for example, the run coroutine below
117+
adds signal handling and a healthy checker
133118
134119
```cpp
135120
auto run(std::shared_ptr<connection> conn) -> net::awaitable<void>
136121
{
137-
co_await connect(conn, "127.0.0.1", "6379");
138122
signal_set sig{ex, SIGINT, SIGTERM};
123+
co_await connect(conn, "127.0.0.1", "6379");
124+
co_await (conn->async_run() || sig.async_wait() || healthy_checker(conn));
125+
}
126+
```
139127

140-
resp3::request req;
141-
req.push("HELLO", 3);
128+
Here we use Asio awaitable operator for simplicity, the same
129+
functionality can be achieved by means of the
130+
`aedis::connection::cancel` function. The definition of the
131+
`healthy_checker` used above can be found in common.cpp.
132+
133+
### Server pushes
142134

143-
co_await ((conn->async_run() || sig.async_wait()) && conn->async_exec(req));
135+
Redis servers can also send a variety of pushes to the client, some of
136+
them are
137+
138+
* [Pubsub](https://redis.io/docs/manual/pubsub/)
139+
* [Keyspace notification](https://redis.io/docs/manual/keyspace-notifications/)
140+
* [Client-side caching](https://redis.io/docs/manual/client-side-caching/)
141+
142+
The connection class supports that by means of the
143+
`aedis::connection::async_receive` function
144+
145+
```cpp
146+
auto receiver(std::shared_ptr<connection> conn) -> net::awaitable<void>
147+
{
148+
using resp_type = std::vector<resp3::node<std::string>>;
149+
for (resp_type resp;;) {
150+
co_await conn->async_receive(adapt(resp));
151+
// Use resp and clear the response for a new push.
152+
resp.clear();
153+
}
144154
}
145155
```
146156
147-
Likewise we can incorporate support for server pushes, healthy checks and pubsub
157+
This function can be also easily incorporated in the run function from
158+
above, for example
148159
149160
```cpp
150161
auto run(std::shared_ptr<connection> conn) -> net::awaitable<void>
151162
{
152-
co_await connect(conn, "127.0.0.1", "6379");
153163
signal_set sig{ex, SIGINT, SIGTERM};
154-
155-
resp3::request req;
156-
req.push("HELLO", 3);
157-
req.push("SUBSCRIBE", "channel1", "channel2");
158-
159-
co_await ((conn->async_run() || sig.async_wait() || receiver(conn) || healthy_checker(conn))
160-
&& conn->async_exec(req));
164+
co_await connect(conn, "127.0.0.1", "6379");
165+
co_await (conn->async_run() || sig.async_wait() || healthy_checker(conn) || receiver(conn));
161166
}
162167
```
163168

164-
The definition of `receiver` and `healthy_checker` above can be found
165-
in cpp20_subscriber.cpp. Adding a loop around `async_run` produces a simple
166-
way to support reconnection _while there are pending operations on the connection_,
169+
### Reconnecting
170+
171+
Adding a loop around `async_run` produces a simple way to support
172+
reconnection _while there are pending operations on the connection_,
167173
for example, to reconnect to the same address
168174

169175
```cpp
@@ -172,15 +178,11 @@ auto run(std::shared_ptr<connection> conn) -> net::awaitable<void>
172178
auto ex = co_await net::this_coro::executor;
173179
steady_timer timer{ex};
174180

175-
resp3::request req;
176-
req.push("HELLO", 3);
177-
req.push("SUBSCRIBE", "channel1", "channel2");
178-
179181
for (;;) {
180182
co_await connect(conn, "127.0.0.1", "6379");
181-
co_await ((conn->async_run() || healthy_checker(conn) || receiver(conn)) && conn->async_exec(req));
183+
co_await (conn->async_run() || healthy_checker(conn) || receiver(conn);
182184

183-
// Prepare the stream to a new connection.
185+
// Prepare the stream for a new connection.
184186
conn->reset_stream();
185187

186188
// Waits one second before trying to reconnect.
@@ -225,8 +227,6 @@ co_await (conn.async_exec(...) || time.async_wait(...))
225227

226228
* Provides a way to limit how long the execution of a single request
227229
should last.
228-
* The cancellation will be ignored if the request has already
229-
been written to the socket.
230230
* NOTE: It is usually a better idea to have a healthy checker than adding
231231
per request timeout, see cpp20_subscriber.cpp for an example.
232232

@@ -242,8 +242,8 @@ co_await (conn.async_exec(...) || conn.async_exec(...) || ... || conn.async_exec
242242

243243
* This works but is unnecessary. Unless the user has set
244244
`aedis::resp3::request::config::coalesce` to `false`, and he
245-
shouldn't, the connection will automatically merge the individual
246-
requests into a single payload anyway.
245+
usually shouldn't, the connection will automatically merge the
246+
individual requests into a single payload anyway.
247247

248248
<a name="requests"></a>
249249
## Requests
@@ -583,6 +583,23 @@ In addition to the above users can also use unordered versions of the
583583
containers. The same reasoning also applies to sets e.g. `SMEMBERS`
584584
and other data structures in general.
585585

586+
## Examples
587+
588+
The examples below show how to use the features discussed so far
589+
590+
* cpp20_containers.cpp: Shows how to send and receive STL containers and how to use transactions.
591+
* cpp20_serialization.cpp: Shows how to serialize types using Boost.Json.
592+
* cpp20_resolve_with_sentinel.cpp: Shows how to resolve a master address using sentinels.
593+
* cpp20_subscriber.cpp: Shows how to implement pubsub with reconnection re-subscription.
594+
* cpp20_echo_server.cpp: A simple TCP echo server.
595+
* cpp20_chat_room.cpp: A command line chat built on Redis pubsub.
596+
* cpp20_low_level_async.cpp: Sends a ping asynchronously using the low-level API.
597+
* cpp17_low_level_sync.cpp: Sends a ping synchronously using the low-level API.
598+
599+
To avoid repetition code that is common to some examples has been
600+
grouped in common.hpp. The main function used in some async examples
601+
has been factored out in the main.cpp file.
602+
586603
## Echo server benchmark
587604

588605
This document benchmarks the performance of TCP echo servers I

examples/cpp20_intro.cpp

Lines changed: 20 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -12,29 +12,43 @@
1212
namespace net = boost::asio;
1313
namespace resp3 = aedis::resp3;
1414
using aedis::adapt;
15+
using aedis::operation;
1516

1617
auto run(std::shared_ptr<connection> conn) -> net::awaitable<void>
1718
{
1819
co_await connect(conn, "127.0.0.1", "6379");
1920
co_await conn->async_run();
2021
}
2122

22-
// Called from the main function (see main.cpp)
23-
auto async_main() -> net::awaitable<void>
23+
auto hello(std::shared_ptr<connection> conn) -> net::awaitable<void>
2424
{
2525
resp3::request req;
2626
req.push("HELLO", 3);
27+
28+
co_await conn->async_exec(req);
29+
}
30+
31+
auto ping(std::shared_ptr<connection> conn) -> net::awaitable<void>
32+
{
33+
resp3::request req;
2734
req.push("PING", "Hello world");
2835
req.push("QUIT");
2936

30-
std::tuple<aedis::ignore, std::string, aedis::ignore> resp;
37+
std::tuple<std::string, aedis::ignore> resp;
38+
39+
co_await conn->async_exec(req, adapt(resp));
40+
41+
std::cout << "PING: " << std::get<0>(resp) << std::endl;
42+
}
3143

44+
// Called from the main function (see main.cpp)
45+
auto async_main() -> net::awaitable<void>
46+
{
3247
auto ex = co_await net::this_coro::executor;
3348
auto conn = std::make_shared<connection>(ex);
3449
net::co_spawn(ex, run(conn), net::detached);
35-
co_await conn->async_exec(req, adapt(resp));
36-
37-
std::cout << "PING: " << std::get<1>(resp) << std::endl;
50+
co_await hello(conn);
51+
co_await ping(conn);
3852
}
3953

4054
#endif // defined(BOOST_ASIO_HAS_CO_AWAIT)

examples/cpp20_subscriber.cpp

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@
1414
namespace net = boost::asio;
1515
namespace resp3 = aedis::resp3;
1616
using namespace net::experimental::awaitable_operators;
17-
using signal_set = net::use_awaitable_t<>::as_default_on_t<net::signal_set>;
1817
using steady_timer = net::use_awaitable_t<>::as_default_on_t<net::steady_timer>;
1918
using aedis::adapt;
2019

@@ -37,7 +36,8 @@ using aedis::adapt;
3736
// Receives pushes.
3837
auto receiver(std::shared_ptr<connection> conn) -> net::awaitable<void>
3938
{
40-
for (std::vector<resp3::node<std::string>> resp;;) {
39+
using resp_type = std::vector<resp3::node<std::string>>;
40+
for (resp_type resp;;) {
4141
co_await conn->async_receive(adapt(resp));
4242
std::cout << resp.at(1).value << " " << resp.at(2).value << " " << resp.at(3).value << std::endl;
4343
resp.clear();
@@ -48,7 +48,6 @@ auto async_main() -> net::awaitable<void>
4848
{
4949
auto ex = co_await net::this_coro::executor;
5050
auto conn = std::make_shared<connection>(ex);
51-
signal_set sig{ex, SIGINT, SIGTERM};
5251
steady_timer timer{ex};
5352

5453
resp3::request req;
@@ -58,8 +57,7 @@ auto async_main() -> net::awaitable<void>
5857
// The loop will reconnect on connection lost. To exit type Ctrl-C twice.
5958
for (;;) {
6059
co_await connect(conn, "127.0.0.1", "6379");
61-
co_await ((conn->async_run() || healthy_checker(conn) || sig.async_wait() ||
62-
receiver(conn)) && conn->async_exec(req));
60+
co_await ((conn->async_run() || healthy_checker(conn) || receiver(conn)) && conn->async_exec(req));
6361

6462
conn->reset_stream();
6563
timer.expires_after(std::chrono::seconds{1});

include/aedis/connection.hpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -120,7 +120,7 @@ class basic_connection :
120120
* @param adapter Response adapter.
121121
* @param token Asio completion token.
122122
*
123-
* For an example see echo_server.cpp. The completion token must
123+
* For an example see cpp20_echo_server.cpp. The completion token must
124124
* have the following signature
125125
*
126126
* @code
@@ -150,7 +150,7 @@ class basic_connection :
150150
* @param adapter The response adapter.
151151
* @param token The Asio completion token.
152152
*
153-
* For an example see subscriber.cpp. The completion token must
153+
* For an example see cpp20_subscriber.cpp. The completion token must
154154
* have the following signature
155155
*
156156
* @code

0 commit comments

Comments
 (0)