Skip to content

Commit 4ac2509

Browse files
committed
Improvements in the docs and examples.
1 parent e9dab97 commit 4ac2509

14 files changed

+122
-99
lines changed

CMakeLists.txt

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -100,10 +100,14 @@ add_test(intro_tls intro_tls)
100100
target_link_libraries(intro_tls OpenSSL::Crypto OpenSSL::SSL)
101101
target_link_libraries(intro_tls common)
102102

103+
add_executable(low_level_async examples/low_level_async.cpp)
104+
target_compile_features(low_level_async PUBLIC cxx_std_20)
105+
add_test(low_level_async low_level_async)
106+
target_link_libraries(low_level_async common)
107+
103108
add_executable(echo_server_client benchmarks/cpp/asio/echo_server_client.cpp)
104109
add_executable(echo_server_direct benchmarks/cpp/asio/echo_server_direct.cpp)
105110
add_executable(low_level_sync examples/low_level_sync.cpp)
106-
add_executable(low_level_async examples/low_level_async.cpp)
107111
add_executable(test_conn_exec tests/conn_exec.cpp)
108112
add_executable(test_conn_push tests/conn_push.cpp)
109113
add_executable(test_conn_quit tests/conn_quit.cpp)
@@ -119,7 +123,6 @@ add_executable(test_request tests/request.cpp)
119123
target_compile_features(echo_server_client PUBLIC cxx_std_20)
120124
target_compile_features(echo_server_direct PUBLIC cxx_std_20)
121125
target_compile_features(low_level_sync PUBLIC cxx_std_17)
122-
target_compile_features(low_level_async PUBLIC cxx_std_20)
123126
target_compile_features(test_conn_exec PUBLIC cxx_std_20)
124127
target_compile_features(test_conn_push PUBLIC cxx_std_20)
125128
target_compile_features(test_conn_quit PUBLIC cxx_std_17)
@@ -139,7 +142,6 @@ target_link_libraries(test_conn_tls OpenSSL::Crypto OpenSSL::SSL)
139142

140143
#add_test(intro_sync intro_sync)
141144
add_test(low_level_sync low_level_sync)
142-
add_test(low_level_async low_level_async)
143145
add_test(test_low_level test_low_level)
144146
add_test(test_conn_exec test_conn_exec)
145147
add_test(test_conn_push test_conn_push)

README.md

Lines changed: 48 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ auto hgetall(std::shared_ptr<connection> conn) -> net::awaitable<void>
3939
}
4040
```
4141
42-
The execution of calls to `connection::async_exec` like above are
42+
The execution of `connection::async_exec` as shown above is
4343
triggered by the `connection::async_run` member function, which is
4444
required to be running concurrently for as long as the connection
4545
stands. For example, the code below uses a short-lived connection to
@@ -68,22 +68,31 @@ reading from the socket. The reationale behind this design is
6868
concurrently.
6969

7070
In the following sections we will discuss with more details the main
71-
entities Aedis users are concerned with, namely
71+
code entities Aedis users are concerned with, namely
7272

7373
* `aedis::resp3::request`: A container of Redis commands.
7474
* `aedis::adapt()`: A function that adapts data structures to receive Redis responses.
7575
* `aedis::connection`: A connection to the Redis server.
7676

7777
before that however, users might find it helpful to skim over the
78-
examples, to gain a better feeling about the library capabilities
78+
examples, to gain a better feeling about the library capabilities.
7979

80-
* intro.cpp: The Aedis hello-world program. It sends one command to Redis and quits the connection.
80+
* intro.cpp: The Aedis hello-world program. Sends one command to Redis and quits the connection.
8181
* intro_tls.cpp: Same as intro.cpp but over TLS.
82-
* containers.cpp: Shows how to send and receive stl containers and how to use transactions.
82+
* containers.cpp: Shows how to send and receive STL containers and how to use transactions.
8383
* serialization.cpp: Shows how to serialize types using Boost.Json.
84-
* subscriber.cpp: Shows how to implement pubsub that reconnects and resubscribes when the connection is lost.
84+
* resolve_with_sentinel.cpp: Shows how to resolve a master address using sentinels.
85+
* subscriber.cpp: Shows how to implement pubsub with reconnection re-subscription.
8586
* echo_server.cpp: A simple TCP echo server.
86-
* chat_room.cpp: A command line chat room built on Redis pubsub.
87+
* chat_room.cpp: A command line chat built on Redis pubsub.
88+
89+
The next two examples uses the Aedis low-level API
90+
91+
* low_level_sync.cpp: Sends a ping synchronously.
92+
* low_level_async.cpp: Sends a ping asynchronously
93+
94+
To avoid repetition code that is common to all examples have been
95+
grouped in common.hpp.
8796

8897
<a name="requests"></a>
8998
### Requests
@@ -93,25 +102,22 @@ Redis documentation they are called
93102
[pipelines](https://redis.io/topics/pipelining)). For example
94103

95104
```cpp
105+
// Some example containers.
106+
std::list<std::string> list {...};
107+
std::map<std::string, mystruct> map { ...};
108+
96109
request req;
97110

98111
// Command with variable length of arguments.
99112
req.push("SET", "key", "some value", "EX", "2");
100113

101114
// Pushes a list.
102-
std::list<std::string> list
103-
{"channel1", "channel2", "channel3"};
104-
105115
req.push_range("SUBSCRIBE", list);
106116

107117
// Same as above but as an iterator range.
108118
req.push_range("SUBSCRIBE", std::cbegin(list), std::cend(list));
109119

110120
// Pushes a map.
111-
std::map<std::string, mystruct> map
112-
{ {"key1", "value1"}
113-
, {"key2", "value2"}
114-
, {"key3", "value3"}};
115121
req.push_range("HSET", "key", map);
116122
```
117123
@@ -121,9 +127,9 @@ Sending a request to Redis is performed with `aedis::connection::async_exec` as
121127
122128
#### Serialization
123129
124-
The `push` and `push_range` functions above work with integers
125-
e.g. `int` and `std::string` out of the box. To send your own
126-
data type define a `to_bulk` function like this
130+
The `resp3::request::push` and `resp3::request::push_range` member functions work
131+
with integer data types e.g. `int` and `std::string` out of the box.
132+
To send your own data type define a `to_bulk` function like this
127133
128134
```cpp
129135
// Example struct.
@@ -175,18 +181,20 @@ To read the response to this request users can use the following tuple
175181
std::tuple<std::string, int, std::string>
176182
```
177183

178-
The pattern may have become apparent to the user, the tuple must have
179-
the same size as the request (exceptions below) and each element must
180-
be able to store the response to the command it refers to. To ignore
181-
responses to individual commands in the request use the tag
184+
The pattern might have become apparent to the reader: the tuple must
185+
have as many elements as the request has commands (exceptions below).
186+
It is also necessary that each tuple element is capable of storing the
187+
response to the command it refers to, otherwise an error will ocurr.
188+
To ignore responses to individual commands in the request use the tag
182189
`aedis::ignore`
183190

184191
```cpp
185192
// Ignore the second and last responses.
186193
std::tuple<std::string, aedis::ignore, std::string, aedis::ignore>
187194
```
188195

189-
The following table provides the response types of some commands
196+
The following table provides the resp3-types returned by some Redis
197+
commands
190198

191199
Command | RESP3 type | Documentation
192200
---------|-------------------------------------|--------------
@@ -249,7 +257,11 @@ If the intention is to ignore the response to all commands altogether
249257
use `adapt()` without arguments instead
250258
251259
```cpp
260+
// Uses the ignore adapter explicitly.
252261
co_await conn->async_exec(req, adapt());
262+
263+
// Ignore adapter is also the default argument.
264+
co_await conn->async_exec(req);
253265
```
254266

255267
Responses that contain nested aggregates or heterogeneous data
@@ -258,15 +270,15 @@ of this writing, not all RESP3 types are used by the Redis server,
258270
which means in practice users will be concerned with a reduced
259271
subset of the RESP3 specification.
260272

261-
#### Push
273+
#### Pushes
262274

263275
Commands that have push response like
264276

265277
* `"SUBSCRIBE"`
266278
* `"PSUBSCRIBE"`
267279
* `"UNSUBSCRIBE"`
268280

269-
must be not be included in the tuple. For example, the request below
281+
must be **NOT** be included in the tuple. For example, the request below
270282

271283
```cpp
272284
request req;
@@ -290,19 +302,19 @@ std::tuple<
290302
std::optional<A>,
291303
std::optional<B>,
292304
...
293-
> response;
305+
> resp;
294306

295-
co_await conn->async_exec(req, adapt(response));
307+
co_await conn->async_exec(req, adapt(resp));
296308
```
297309
298310
Everything else stays pretty much the same.
299311
300312
#### Transactions
301313
302314
To read responses to transactions we must first observe that Redis will
303-
queue its commands and send their responses to the user as elements
304-
of an array, after the `EXEC` command comes. For example, to read
305-
the response to this request
315+
queue the transaction commands and send their individual responses as elements
316+
of an array, the array is itself the response to the `EXEC` command.
317+
For example, to read the response to this request
306318
307319
```cpp
308320
req.push("MULTI");
@@ -342,9 +354,9 @@ For a complete example see containers.cpp.
342354
As mentioned in \ref serialization, it is common practice to
343355
serialize data before sending it to Redis e.g. as json strings.
344356
For performance and convenience reasons, we may also want to
345-
deserialize it directly in its final data structure when reading them
346-
back from Redis. Aedis supports this use case by calling a user
347-
provided `from_bulk` function while parsing the response. For example
357+
deserialize responses directly in their final data structure. Aedis
358+
supports this use case by calling a user provided `from_bulk` function
359+
while parsing the response. For example
348360
349361
```cpp
350362
void from_bulk(mystruct& obj, char const* p, std::size_t size, boost::system::error_code& ec)
@@ -421,15 +433,16 @@ The `aedis::connection` is a class that provides async-only
421433
communication with a Redis server by means of three member
422434
functions
423435

424-
* `connection::async_run`: Starts read and write operations and remains suspended until the connection it is lost.
436+
* `connection::async_run`: Starts read and write operations and remains suspended until the connection is lost.
425437
* `connection::async_exec`: Executes commands.
426438
* `connection::async_receive`: Receives server-side pushes.
427439

428440
In general, these operations will be running concurrently in user
429441
application, where, for example
430442

431-
1. **Run**: One coroutine will call `async_run`, perhaps in a loop and
432-
with healthy checks.
443+
1. **Run**: One coroutine will call `async_run`, perhaps with other
444+
operations like healthy checks and in a loop to implement
445+
reconnection.
433446
2. **Execute**: Multiple coroutines will call `async_exec` independently
434447
and without coordination (e.g. queuing).
435448
3. **Receive**: One coroutine will loop on `async_receive` to receive

examples/chat_room.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ using aedis::resp3::node;
2323
// Chat over Redis pubsub. To test, run this program from different
2424
// terminals and type messages to stdin.
2525

26-
// Receives Redis server-side pushes.
26+
// Receives Redis pushes.
2727
auto receiver(std::shared_ptr<connection> conn) -> net::awaitable<void>
2828
{
2929
for (std::vector<node<std::string>> resp;;) {
@@ -55,6 +55,7 @@ auto subscriber(std::shared_ptr<connection> conn) -> net::awaitable<void>
5555
co_await conn->async_exec(req);
5656
}
5757

58+
// Called from the main function (see common.cpp)
5859
auto async_main() -> net::awaitable<void>
5960
{
6061
auto ex = co_await net::this_coro::executor;

examples/containers.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,7 @@ auto transaction(std::shared_ptr<connection> conn) -> net::awaitable<void>
9292
print(std::get<1>(std::get<4>(resp)).value());
9393
}
9494

95+
// Called from the main function (see common.cpp)
9596
net::awaitable<void> async_main()
9697
{
9798
auto conn = std::make_shared<connection>(co_await net::this_coro::executor);

examples/echo_server.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ auto echo_server_session(tcp_socket socket, std::shared_ptr<connection> conn) ->
3535
}
3636
}
3737

38+
// Listens for tcp connections.
3839
auto listener(std::shared_ptr<connection> conn) -> net::awaitable<void>
3940
{
4041
auto ex = co_await net::this_coro::executor;
@@ -43,6 +44,7 @@ auto listener(std::shared_ptr<connection> conn) -> net::awaitable<void>
4344
net::co_spawn(ex, echo_server_session(co_await acc.async_accept(), conn), net::detached);
4445
}
4546

47+
// Called from the main function (see common.cpp)
4648
auto async_main() -> net::awaitable<void>
4749
{
4850
auto ex = co_await net::this_coro::executor;

examples/intro.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ using namespace net::experimental::awaitable_operators;
1515
using aedis::adapt;
1616
using aedis::resp3::request;
1717

18+
// Called from the main function (see common.cpp)
1819
net::awaitable<void> async_main()
1920
{
2021
request req;

examples/low_level_async.cpp

Lines changed: 11 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -4,32 +4,33 @@
44
* accompanying file LICENSE.txt)
55
*/
66

7-
#include <string>
8-
#include <iostream>
9-
107
#include <boost/asio.hpp>
118
#if defined(BOOST_ASIO_HAS_CO_AWAIT)
12-
139
#include <aedis.hpp>
14-
#include <aedis/src.hpp>
10+
#include <string>
11+
#include <iostream>
1512

1613
namespace net = boost::asio;
1714
namespace resp3 = aedis::resp3;
18-
using endpoints = net::ip::tcp::resolver::results_type;
15+
using resolver = net::use_awaitable_t<>::as_default_on_t<net::ip::tcp::resolver>;
1916
using tcp_socket = net::use_awaitable_t<>::as_default_on_t<net::ip::tcp::socket>;
2017
using aedis::resp3::request;
2118
using aedis::adapter::adapt2;
2219
using net::ip::tcp;
2320

24-
net::awaitable<void> ping(endpoints const& addrs)
21+
auto async_main() -> net::awaitable<void>
2522
{
26-
tcp_socket socket{co_await net::this_coro::executor};
27-
net::connect(socket, addrs);
23+
auto ex = co_await net::this_coro::executor;
24+
25+
resolver resv{ex};
26+
auto const addrs = co_await resv.async_resolve("127.0.0.1", "6379");
27+
tcp_socket socket{ex};
28+
co_await net::async_connect(socket, addrs);
2829

2930
// Creates the request and writes to the socket.
3031
request req;
3132
req.push("HELLO", 3);
32-
req.push("PING");
33+
req.push("PING", "Hello world");
3334
req.push("QUIT");
3435
co_await resp3::async_write(socket, req);
3536

@@ -45,19 +46,4 @@ net::awaitable<void> ping(endpoints const& addrs)
4546
std::cout << "Ping: " << resp << std::endl;
4647
}
4748

48-
int main()
49-
{
50-
try {
51-
net::io_context ioc;
52-
net::ip::tcp::resolver resv{ioc};
53-
auto const addrs = resv.resolve("127.0.0.1", "6379");
54-
net::co_spawn(ioc, ping(addrs), net::detached);
55-
ioc.run();
56-
} catch (std::exception const& e) {
57-
std::cerr << "Error: " << e.what() << std::endl;
58-
}
59-
}
60-
61-
#else // defined(BOOST_ASIO_HAS_CO_AWAIT)
62-
auto main() -> int {std::cout << "Requires coroutine support." << std::endl; return 0;}
6349
#endif // defined(BOOST_ASIO_HAS_CO_AWAIT)

examples/low_level_sync.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ int main()
3131
// Creates the request and writes to the socket.
3232
request req;
3333
req.push("HELLO", 3);
34-
req.push("PING");
34+
req.push("PING", "Hello world");
3535
req.push("QUIT");
3636
resp3::write(socket, req);
3737

examples/main.cpp

Whitespace-only changes.

examples/resolve_with_sentinel.cpp

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -19,15 +19,15 @@ using aedis::resp3::request;
1919
auto redir(boost::system::error_code& ec)
2020
{ return net::redirect_error(net::use_awaitable, ec); }
2121

22-
struct endpoint {
22+
struct address {
2323
std::string host;
2424
std::string port;
2525
};
2626

2727
// For more info see
2828
// - https://redis.io/docs/manual/sentinel.
2929
// - https://redis.io/docs/reference/sentinel-clients.
30-
auto resolve_master_address(std::vector<endpoint> const& endpoints) -> net::awaitable<endpoint>
30+
auto resolve_master_address(std::vector<address> const& endpoints) -> net::awaitable<address>
3131
{
3232
request req;
3333
req.get_config().cancel_on_connection_lost = true;
@@ -43,17 +43,17 @@ auto resolve_master_address(std::vector<endpoint> const& endpoints) -> net::awai
4343
co_await (conn->async_run() && conn->async_exec(req, adapt(addr), redir(ec)));
4444
conn->reset_stream();
4545
if (std::get<0>(addr))
46-
co_return endpoint{std::get<0>(addr).value().at(0), std::get<0>(addr).value().at(1)};
46+
co_return address{std::get<0>(addr).value().at(0), std::get<0>(addr).value().at(1)};
4747
}
4848

49-
co_return endpoint{};
49+
co_return address{};
5050
}
5151

5252
auto async_main() -> net::awaitable<void>
5353
{
5454
// A list of sentinel addresses from which only one is responsive
5555
// to simulate sentinels that are down.
56-
std::vector<endpoint> const endpoints
56+
std::vector<address> const endpoints
5757
{ {"foo", "26379"}
5858
, {"bar", "26379"}
5959
, {"127.0.0.1", "26379"}

0 commit comments

Comments
 (0)