|
28 | 28 | \li First class support for STL containers and C++ built-in types.
|
29 | 29 | \li Serialization and deserialization of your own data types.
|
30 | 30 | \li Healthy checks, back pressure and low latency.
|
| 31 | + \li Hides most of the low level asynchronous operations away from the user. |
31 | 32 |
|
32 |
| - Aedis API hides most of the low level asynchronous operations away |
33 |
| - from the user, for example, the code below sends a ping command to |
34 |
| - Redis (see intro.cpp) |
| 33 | + Let us start with an overview of asynchronous code. |
| 34 | +
|
| 35 | + @subsection Async |
| 36 | +
|
| 37 | + The code below sends a ping command to Redis (see intro.cpp) |
35 | 38 |
|
36 | 39 | @code
|
37 | 40 | int main()
|
38 | 41 | {
|
| 42 | + net::io_context ioc; |
| 43 | + connection db{ioc}; |
| 44 | +
|
39 | 45 | request req;
|
40 |
| - req.push("HELLO", 3); |
41 | 46 | req.push("PING");
|
42 | 47 | req.push("QUIT");
|
43 | 48 |
|
44 |
| - std::tuple<aedis::ignore, std::string, aedis::ignore> resp; |
45 |
| -
|
46 |
| - net::io_context ioc; |
47 |
| -
|
48 |
| - connection db{ioc}; |
49 |
| -
|
50 |
| - db.async_exec("127.0.0.1", "6379", req, adapt(resp), handler); |
| 49 | + std::tuple<std::string, aedis::ignore> resp; |
| 50 | + db.async_run(req, adapt(resp), net::detached); |
51 | 51 |
|
52 | 52 | ioc.run();
|
53 | 53 |
|
54 |
| - // Print the ping message. |
55 |
| - std::cout << std::get<1>(resp) << std::endl; |
| 54 | + std::cout << std::get<0>(resp) << std::endl; |
56 | 55 | }
|
57 | 56 | @endcode
|
58 | 57 |
|
59 |
| - The connection class keeps a long lasting connection to the Redis |
60 |
| - server over which users can execute commands, without any need of |
61 |
| - queuing, for example, to execute more than one command |
| 58 | + The connection class maintains a healthy connection with |
| 59 | + Redis over which users can execute their commands, without any |
| 60 | + need of queuing. For example, to execute more than one command |
62 | 61 |
|
63 | 62 | @code
|
64 | 63 | int main()
|
|
71 | 70 | db.async_exec(req2, adapt(resp2), handler2);
|
72 | 71 | db.async_exec(req3, adapt(resp3), handler3);
|
73 | 72 |
|
74 |
| - db.async_run("127.0.0.1", "6379", handler4); |
| 73 | + db.async_run(net::detached); |
75 | 74 |
|
76 | 75 | ioc.run();
|
77 | 76 | ...
|
78 | 77 | }
|
79 | 78 | @endcode
|
80 | 79 |
|
81 |
| - See echo_server.cpp for a more complex example. Server-side |
82 |
| - pushes are supported on the same connection where commands are |
83 |
| - executed, a typical subscriber will look like |
| 80 | + The `async_exec` functions above can be called from different |
| 81 | + places in the code without knowing about each other, see for |
| 82 | + example echo_server.cpp. Server-side pushes are supported on the |
| 83 | + same connection where commands are executed, a typical subscriber |
| 84 | + will look like |
| 85 | + (see subscriber.cpp) |
84 | 86 |
|
85 | 87 | @code
|
86 | 88 | net::awaitable<void> reader(std::shared_ptr<connection> db)
|
87 | 89 | {
|
88 |
| - ... |
89 |
| - for (std::vector<node<std::string>> resp;;) { |
90 |
| - co_await db->async_receive(adapt(resp)); |
| 90 | + request req; |
| 91 | + req.push("SUBSCRIBE", "channel"); |
| 92 | +
|
| 93 | + for (std::vector<node_type> resp;;) { |
| 94 | + auto ev = co_await db->async_receive_event(aedis::adapt(resp)); |
91 | 95 |
|
92 |
| - // Handle message |
| 96 | + switch (ev) { |
| 97 | + case connection::event::push: |
| 98 | + // Use resp. |
| 99 | + resp.clear(); |
| 100 | + break; |
93 | 101 |
|
94 |
| - resp.clear(); |
| 102 | + case connection::event::hello: |
| 103 | + // Subscribes to channels when a new connection is |
| 104 | + // stablished. |
| 105 | + co_await db->async_exec(req); |
| 106 | + break; |
| 107 | +
|
| 108 | + default:; |
| 109 | + } |
95 | 110 | }
|
96 | 111 | }
|
97 | 112 | @endcode
|
98 | 113 |
|
99 |
| - See subscriber.cpp for a complete example. |
| 114 | + @subsection Sync |
| 115 | +
|
| 116 | + The `connection` class is async-only, many users however need to |
| 117 | + interact with it synchronously, this is also supported by Aedis as long |
| 118 | + as this interaction occurs across threads, for example (see |
| 119 | + intro_sync.cpp) |
| 120 | +
|
| 121 | + @code |
| 122 | + int main() |
| 123 | + { |
| 124 | + try { |
| 125 | + net::io_context ioc{1}; |
| 126 | + connection conn{ioc}; |
| 127 | + |
| 128 | + std::thread thread{[&]() { |
| 129 | + conn.async_run(net::detached); |
| 130 | + ioc.run(); |
| 131 | + }}; |
| 132 | + |
| 133 | + request req; |
| 134 | + req.push("PING"); |
| 135 | + req.push("QUIT"); |
| 136 | + |
| 137 | + std::tuple<std::string, aedis::ignore> resp; |
| 138 | + exec(conn, req, adapt(resp)); |
| 139 | + thread.join(); |
| 140 | + |
| 141 | + std::cout << "Response: " << std::get<0>(resp) << std::endl; |
| 142 | + } catch (std::exception const& e) { |
| 143 | + std::cerr << e.what() << std::endl; |
| 144 | + } |
| 145 | + } |
| 146 | + @endcode |
100 | 147 |
|
101 | 148 | \subsection using-aedis Installation
|
102 | 149 |
|
|
110 | 157 |
|
111 | 158 | ```
|
112 | 159 | # Clone the repository and checkout the lastest release tag.
|
113 |
| - $ git clone --branch v0.2.1 https://github.com/mzimbres/aedis.git |
| 160 | + $ git clone --branch v0.3.0 https://github.com/mzimbres/aedis.git |
114 | 161 | $ cd aedis
|
115 | 162 |
|
116 | 163 | # Build an example
|
|
121 | 168 |
|
122 | 169 | ```
|
123 | 170 | # Download and unpack the latest release
|
124 |
| - $ wget https://github.com/mzimbres/aedis/releases/download/v0.2.1/aedis-0.2.1.tar.gz |
| 171 | + $ wget https://github.com/mzimbres/aedis/releases/download/v0.3.0/aedis-0.2.1.tar.gz |
125 | 172 | $ tar -xzvf aedis-0.2.1.tar.gz
|
126 | 173 |
|
127 | 174 | # Configure, build and install
|
|
451 | 498 | The examples listed below cover most use cases presented in the documentation above.
|
452 | 499 |
|
453 | 500 | @li intro.cpp: Basic steps with Aedis.
|
| 501 | + @li intro_sync.cpp: Synchronous version of intro.cpp. |
454 | 502 | @li containers.cpp: Shows how to send and receive stl containers.
|
455 | 503 | @li serialization.cpp: Shows the \c request support to serialization of user types.
|
456 | 504 | @li subscriber.cpp: Shows how to subscribe to a channel and how to reconnect when connection is lost.
|
457 |
| - @li echo_server.cpp: A simple TCP echo server that users coroutines. |
| 505 | + @li subscriber_sync.cpp: Synchronous version of subscriber.cpp. |
| 506 | + @li echo_server.cpp: A simple TCP echo server that uses coroutines. |
458 | 507 | @li chat_room.cpp: A simple chat room that uses coroutines.
|
459 | 508 |
|
460 | 509 | \section why-aedis Why Aedis
|
|
476 | 525 | not support
|
477 | 526 |
|
478 | 527 | @li RESP3. Without RESP3 is impossible to support some important Redis features like client side caching, among other things.
|
479 |
| - @li The Asio asynchronous model. |
480 |
| - @li Reading response diretly in user data structures avoiding temporaries. |
| 528 | + @li Coroutines. |
| 529 | + @li Reading responses directly in user data structures avoiding temporaries. |
481 | 530 | @li Error handling with error-code and exception overloads.
|
482 | 531 | @li Healthy checks.
|
483 | 532 |
|
|
523 | 572 |
|
524 | 573 | Some of the problems with this API are
|
525 | 574 |
|
526 |
| - @li Heterogeneous treatment of commands, pipelines and transaction. |
| 575 | + @li Heterogeneous treatment of commands, pipelines and transaction. This makes auto-pipelining impossible. |
527 | 576 | @li Any Api that sends individual commands has a very restricted scope of usability and should be avoided for performance reasons.
|
528 | 577 | @li The API imposes exceptions on users, no error-code overload is provided.
|
529 | 578 | @li No way to reuse the buffer for new calls to e.g. \c redis.get in order to avoid further dynamic memory allocations.
|
530 |
| - @li Error handling of resolve and connection no clear. |
| 579 | + @li Error handling of resolve and connection not clear. |
531 | 580 |
|
532 | 581 | According to the documentation, pipelines in redis-plus-plus have
|
533 | 582 | the following characteristics
|
|
538 | 587 | This is clearly a downside of the API as pipelines should be the
|
539 | 588 | default way of communicating and not an exception, paying such a
|
540 | 589 | high price for each pipeline imposes a severe cost in performance.
|
541 |
| - Transactions also suffer from the very same problem |
| 590 | + Transactions also suffer from the very same problem. |
542 | 591 |
|
543 | 592 | > NOTE: Creating a Transaction object is NOT cheap, since it
|
544 | 593 | > creates a new connection.
|
|
0 commit comments