mirror of
https://github.com/boostorg/redis.git
synced 2026-01-19 04:42:09 +00:00
Some refactoring.
This commit is contained in:
11
Makefile.am
11
Makefile.am
@@ -15,12 +15,12 @@ check_PROGRAMS += intro
|
||||
check_PROGRAMS += sets
|
||||
check_PROGRAMS += hashes
|
||||
check_PROGRAMS += serialization
|
||||
check_PROGRAMS += nested_response
|
||||
check_PROGRAMS += multipurpose_response
|
||||
check_PROGRAMS += lists
|
||||
check_PROGRAMS += key_expiration
|
||||
check_PROGRAMS += response_adapter
|
||||
check_PROGRAMS += sync
|
||||
check_PROGRAMS += redis_client
|
||||
check_PROGRAMS += multipurpose_client
|
||||
check_PROGRAMS += test_offline
|
||||
check_PROGRAMS += test_online
|
||||
check_PROGRAMS += transaction
|
||||
@@ -41,12 +41,12 @@ intro_SOURCES = $(top_srcdir)/examples/intro.cpp
|
||||
sets_SOURCES = $(top_srcdir)/examples/sets.cpp
|
||||
hashes_SOURCES = $(top_srcdir)/examples/hashes.cpp
|
||||
serialization_SOURCES = $(top_srcdir)/examples/serialization.cpp
|
||||
nested_response_SOURCES = $(top_srcdir)/examples/nested_response.cpp
|
||||
multipurpose_response_SOURCES = $(top_srcdir)/examples/multipurpose_response.cpp
|
||||
lists_SOURCES = $(top_srcdir)/examples/lists.cpp
|
||||
key_expiration_SOURCES = $(top_srcdir)/examples/key_expiration.cpp
|
||||
response_adapter_SOURCES = $(top_srcdir)/examples/response_adapter.cpp
|
||||
sync_SOURCES = $(top_srcdir)/examples/sync.cpp
|
||||
redis_client_SOURCES = $(top_srcdir)/examples/redis_client.cpp
|
||||
multipurpose_client_SOURCES = $(top_srcdir)/examples/multipurpose_client.cpp
|
||||
commands_SOURCES = $(top_srcdir)/tools/commands.cpp
|
||||
test_offline_SOURCES = $(top_srcdir)/tests/offline.cpp
|
||||
test_online_SOURCES = $(top_srcdir)/tests/online.cpp
|
||||
@@ -91,7 +91,6 @@ TESTS = $(check_PROGRAMS)
|
||||
|
||||
EXTRA_DIST =
|
||||
EXTRA_DIST += $(top_srcdir)/README.md
|
||||
EXTRA_DIST += $(top_srcdir)/doc/Doxyfile
|
||||
EXTRA_DIST += $(top_srcdir)/doc/DoxygenLayout.xml
|
||||
EXTRA_DIST += $(top_srcdir)/doc/aedis.css
|
||||
EXTRA_DIST += $(top_srcdir)/doc/htmlfooter.html
|
||||
@@ -99,6 +98,6 @@ EXTRA_DIST += $(top_srcdir)/doc/htmlheader.html
|
||||
|
||||
.PHONY: doc
|
||||
doc:
|
||||
rm -rf /tmp/aedis
|
||||
rm -rf ../aedis-gh-pages/*
|
||||
doxygen doc/Doxyfile
|
||||
|
||||
|
||||
341
aedis/aedis.hpp
341
aedis/aedis.hpp
@@ -18,240 +18,187 @@
|
||||
#include <aedis/resp3/response_traits.hpp>
|
||||
#include <aedis/redis/experimental/client.hpp>
|
||||
|
||||
/** \mainpage
|
||||
*
|
||||
* \b Overview
|
||||
*
|
||||
* Aedis is low-level redis client library built on top of Boost.Asio
|
||||
* that implements communication with a Redis server over its native
|
||||
* protocol RESP3. It has first-class support for STL containers and
|
||||
* C++ built in types among other things. You will be able to
|
||||
* implement your own redis client or use a general purpose provided
|
||||
* by the library. For more information about Redis see
|
||||
* https://redis.io/
|
||||
*
|
||||
* \b Using \b Aedis
|
||||
*
|
||||
* - \subpage installation
|
||||
* - \subpage examples
|
||||
*
|
||||
* \b Reference
|
||||
*
|
||||
* - \subpage enums
|
||||
* - \subpage classes
|
||||
* - \subpage functions
|
||||
* - \subpage operators
|
||||
*/
|
||||
/** \mainpage Documentation
|
||||
\tableofcontents
|
||||
|
||||
\section Overview
|
||||
|
||||
Aedis is low-level redis client library built on top of Boost.Asio
|
||||
that implements communication with a Redis server over its native
|
||||
protocol RESP3. It has first-class support for STL containers and
|
||||
C++ built in types among other things. You will be able to
|
||||
implement your own redis client or use a general purpose provided
|
||||
by the library. For more information about Redis see
|
||||
https://redis.io/
|
||||
|
||||
\section examples Examples
|
||||
|
||||
//---------------------------------------------------------
|
||||
// Pages
|
||||
In general every feature offered by the library will be
|
||||
accompained by an example showing how to use it. We also focus in
|
||||
a more modern asynchronous programming with coroutines.
|
||||
|
||||
/** \page examples Examples
|
||||
*
|
||||
\b Basics: Focuses on small code snippets that show basic usage of
|
||||
the library, for example: how to make a request and read the
|
||||
response, how to deal with keys that may not exist, etc.
|
||||
\subsection Basics
|
||||
|
||||
- intro.cpp
|
||||
Focuses on small examples that show basic usage of
|
||||
the library, for example, how to make a request and read the
|
||||
response, how to deal with keys that may not exist, pubsub, etc.
|
||||
|
||||
Some commands are sent to the Redis server and the responses are
|
||||
printed to screen. A good starting point.
|
||||
- intro.cpp: A good starting point. Some commands are sent to the
|
||||
Redis server and the responses are printed to screen.
|
||||
|
||||
- transaction.cpp
|
||||
- transaction.cpp: Shows how to read the responses to a trasaction
|
||||
efficiently. See also https://redis.io/topics/transactions.
|
||||
|
||||
Shows how to read the responses to all commands inside a
|
||||
trasaction efficiently. At the moment this feature supports only
|
||||
transactions that contain simple types or aggregates that don't
|
||||
contain aggregates themselves (as in most cases).
|
||||
- multipurpose_response.cpp: Shows how to read any responses to
|
||||
Redis commands, including nested aggegagtes.
|
||||
|
||||
- nested_response.cpp
|
||||
|
||||
Shows how to read responses to commands that cannot be
|
||||
translated in a C++ built-in type like std::string or STL
|
||||
containers, for example all commands contained in a transaction
|
||||
will be nested by Redis in a single response. Users may have to
|
||||
convert into their own desired format.
|
||||
- subscriber.cpp: Shows how channel subscription works at a low
|
||||
level. See also https://redis.io/topics/pubsub.
|
||||
|
||||
- subscriber.cpp
|
||||
- sync.cpp: Shows hot to use the Aedis synchronous api.
|
||||
|
||||
Shows how channel subscription works at a low level.
|
||||
- key_expiration.cpp: Shows how to use \c std::optional to deal
|
||||
with keys that may have expired or do not exist.
|
||||
|
||||
- sync.cpp
|
||||
|
||||
Shows hot to use the Aedis synchronous api.
|
||||
\subsection stl-containers STL Containers
|
||||
|
||||
- key_expiration.cpp
|
||||
|
||||
Shows how to use \c std::optional to deal with keys that may
|
||||
have expired or do not exist.
|
||||
|
||||
\b STL \b Containers: Many of the Redis data structures can be
|
||||
Many of the Redis data structures can be
|
||||
directly translated in to STL containers, below you will find some
|
||||
example code. For a list of Redis data types see
|
||||
https://redis.io/topics/data-types.
|
||||
|
||||
- hashes.cpp
|
||||
|
||||
Shows how to read Redis hashes in a \c std::map, \c
|
||||
- hashes.cpp: Shows how to read Redis hashes in a \c std::map, \c
|
||||
std::unordered_map and \c std::vector.
|
||||
|
||||
- lists.cpp
|
||||
|
||||
Shows how to read Redis lists in \c std::list,
|
||||
- lists.cpp: Shows how to read Redis lists in \c std::list,
|
||||
\c std::deque, \c std::vector. It also illustrates basic serialization.
|
||||
|
||||
- sets.cpp
|
||||
- sets.cpp: Shows how to read Redis sets in a \c std::set, \c
|
||||
std::unordered_set and \c std::vector.
|
||||
|
||||
Shows how to read Redis sets in a \c std::set, \c std::unordered_set
|
||||
and \c std::vector.
|
||||
\subsection customization-points Customization points
|
||||
|
||||
\b Customization \b points: Shows how de/serialize user types
|
||||
Shows how de/serialize user types
|
||||
avoiding copies. This is particularly useful for low latency
|
||||
applications that want to avoid unneeded copies, for examples when
|
||||
storing json strings in Redis keys.
|
||||
|
||||
- serialization.cpp
|
||||
- serialization.cpp: Shows how to de/serialize your own
|
||||
non-aggregate data-structures.
|
||||
|
||||
Shows how to de/serialize your own non-aggregate data-structures.
|
||||
- response_adapter.cpp: Customization point for users that want to
|
||||
de/serialize their own data-structures like containers for example.
|
||||
|
||||
- response_adapter.cpp
|
||||
\subsection async-servers Asynchronous servers
|
||||
|
||||
Customization point for users that want to de/serialize their
|
||||
own data-structures like containers for example.
|
||||
|
||||
\b Asynchronous \b servers: Contains some non-trivial examples
|
||||
Contains some non-trivial examples
|
||||
servers that interact with users and Redis asynchronously over
|
||||
long lasting connections using a higher level API.
|
||||
|
||||
- redis_client.cpp
|
||||
- multipurpose_client.cpp: Shows how to use and experimental high
|
||||
level redis client that keeps a long lasting connections to a
|
||||
redis server. This is the starting point for the next examples.
|
||||
|
||||
Shows how to use and experimental high level redis client that
|
||||
keeps a long lasting connections to a redis server. This is the
|
||||
starting point for the next examples.
|
||||
- echo_server.cpp: Shows the basic principles behind asynchronous
|
||||
communication with a database in an asynchronous server. In this
|
||||
case, the server is a proxy between the user and Redis.
|
||||
|
||||
- echo_server.cpp
|
||||
- chat_room.cpp: Shows how to build a scalable chat room that
|
||||
scales to millions of users.
|
||||
|
||||
Shows the basic principles behind asynchronous communication
|
||||
with a database in an asynchronous server. In this case, the
|
||||
server is a proxy between the user and Redis.
|
||||
\section using-aedis Using Aedis
|
||||
|
||||
- chat_room.cpp
|
||||
This section contains instructions on how to install aedis on your machine among other things.
|
||||
|
||||
Shows how to build a scalable chat room that scales to
|
||||
millions of users.
|
||||
\subsection Requirements
|
||||
|
||||
Aedis installation requires
|
||||
|
||||
- Boost 1.78 or greater
|
||||
- Unix Shell
|
||||
- Make
|
||||
|
||||
To use Aedis and build its examples and tests you will need
|
||||
|
||||
- C++20 with coroutine support.
|
||||
- Redis server.
|
||||
|
||||
Some examples will also require interaction with
|
||||
|
||||
- Redis-client: used in on example.
|
||||
- Redis Sentinel server: used in some examples.
|
||||
|
||||
\subsection Installation
|
||||
|
||||
Start by downloading and configuring the library
|
||||
|
||||
```
|
||||
# Download the libray on github.
|
||||
$ wget github-link
|
||||
|
||||
# Uncompress the tarball and cd into the dir
|
||||
$ tar -xzvf aedis-1.0.0.tar.gz && cd aedis-1.0.0
|
||||
|
||||
# Run configure with appropriate C++ flags and your boost installation, for example
|
||||
$ CXXFLAGS="-std=c++20 -fcoroutines -g -Wall -Wno-subobject-linkage"\
|
||||
./configure --prefix=/opt/aedis-1.0.0 --with-boost=/opt/boost_1_78_0 --with-boost-libdir=/opt/boost_1_78_0/lib
|
||||
|
||||
```
|
||||
|
||||
To install the library run
|
||||
|
||||
```
|
||||
# Install Aedis in the path specified in --prefix
|
||||
$ sudo make install
|
||||
|
||||
```
|
||||
|
||||
At this point you can start using Aedis. To build the examples and
|
||||
test you can also run
|
||||
|
||||
```
|
||||
# Build aedis examples.
|
||||
$ make examples
|
||||
|
||||
# Test aedis in your machine.
|
||||
$ make check
|
||||
```
|
||||
|
||||
Finally you will have to include the following header
|
||||
|
||||
```cpp
|
||||
#include <aedis/src.hpp>
|
||||
```
|
||||
in exactly one source file in your applications.
|
||||
|
||||
\subsubsection Windows
|
||||
|
||||
Windows users can use aedis by either adding the project root
|
||||
directory to their include path or manually copying to another
|
||||
location.
|
||||
|
||||
\subsection Developers
|
||||
|
||||
To generate the build system run
|
||||
|
||||
```
|
||||
$ autoreconf -i
|
||||
```
|
||||
|
||||
After that you will have a config in the project dir that you can
|
||||
run as explained above, for example, to use a compiler other that
|
||||
the system compiler use
|
||||
|
||||
```
|
||||
CC=/opt/gcc-10.2.0/bin/gcc-10.2.0\
|
||||
CXX=/opt/gcc-10.2.0/bin/g++-10.2.0\
|
||||
CXXFLAGS="-std=c++20 -fcoroutines -g -Wall -Wno-subobject-linkage -Werror" ./configure ...
|
||||
```
|
||||
\section Referece
|
||||
|
||||
See \subpage any.
|
||||
*/
|
||||
|
||||
/** \page installation Installation
|
||||
* \tableofcontents
|
||||
*
|
||||
* \section Requirements
|
||||
*
|
||||
* Aedis installation requires
|
||||
*
|
||||
* - \b Boost \b 1.78 or greater
|
||||
* - \b Unix \b Shell
|
||||
* - \b Make
|
||||
*
|
||||
* To use Aedis and build its examples and tests you will need
|
||||
*
|
||||
* - \b C++20 with \b coroutine support.
|
||||
* - \b Redis \b server.
|
||||
*
|
||||
* Some examples will also require interaction with
|
||||
*
|
||||
* - \b Redis-client: used in on example.
|
||||
* - \b Redis \b Sentinel \b server: used in some examples.
|
||||
*
|
||||
* \section Installation
|
||||
*
|
||||
* Get the latest release and follow the steps
|
||||
*
|
||||
* ```
|
||||
* # Download the libray on github.
|
||||
* $ wget github-link
|
||||
*
|
||||
* # Uncompress the tarball and cd into the dir
|
||||
* $ tar -xzvf aedis-1.0.0.tar.gz && cd aedis-1.0.0
|
||||
*
|
||||
* # Run configure with appropriate C++ flags and your boost installation, for example
|
||||
* $ CXXFLAGS="-std=c++20 -fcoroutines -g -Wall -Wno-subobject-linkage"\
|
||||
* ./configure --prefix=/opt/aedis-1.0.0 --with-boost=/opt/boost_1_78_0 --with-boost-libdir=/opt/boost_1_78_0/lib
|
||||
*
|
||||
* ```
|
||||
*
|
||||
* To install the library run
|
||||
*
|
||||
* ```
|
||||
* # Install Aedis in the path specified in --prefix
|
||||
* $ sudo make install
|
||||
*
|
||||
* ```
|
||||
*
|
||||
* To build the examples and test the library in your machine
|
||||
*
|
||||
* ```
|
||||
* # Build aedis examples.
|
||||
* $ make examples
|
||||
*
|
||||
* # Test aedis in your machine.
|
||||
* $ make check
|
||||
* ```
|
||||
*
|
||||
* \subsection Windows
|
||||
*
|
||||
* Windows users can use aedis by either adding the project root
|
||||
* directory to their include path or manually copying to another
|
||||
* location.
|
||||
*
|
||||
* \section using Using Aedis
|
||||
*
|
||||
* This library in not header-only. You have to include the following
|
||||
* header
|
||||
*
|
||||
* ```cpp
|
||||
* #include <aedis/src.hpp>
|
||||
* ```
|
||||
*
|
||||
* in exactly one source file in your applications.
|
||||
*
|
||||
* \section Developers
|
||||
*
|
||||
* Aedis uses Autotools for its build system. To generate the build
|
||||
* system run
|
||||
*
|
||||
* ```
|
||||
* $ autoreconf -i
|
||||
* ```
|
||||
*
|
||||
* After that you will have a config in the project dir that you can
|
||||
* run as explained above, for example, to use a compiler other that
|
||||
* the system compiler use
|
||||
*
|
||||
* ```
|
||||
* CC=/opt/gcc-10.2.0/bin/gcc-10.2.0\
|
||||
* CXX=/opt/gcc-10.2.0/bin/g++-10.2.0\
|
||||
* CXXFLAGS="-std=c++20 -fcoroutines -g -Wall -Wno-subobject-linkage -Werror" ./configure ...
|
||||
* ```
|
||||
/** \defgroup any Reference
|
||||
*/
|
||||
|
||||
//---------------------------------------------------------
|
||||
// Groups
|
||||
|
||||
/** \defgroup enums Enumerations
|
||||
* \brief Enumerations defined by this library.
|
||||
*/
|
||||
|
||||
|
||||
/** \defgroup classes Classes
|
||||
* \brief Classes defined by this library.
|
||||
*/
|
||||
|
||||
|
||||
/** \defgroup functions Free functions
|
||||
* \brief All functions defined by this library.
|
||||
*/
|
||||
|
||||
|
||||
/** \defgroup operators Operators
|
||||
* \brief Operators defined in Aedis
|
||||
*/
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
// TODO: Remove this.
|
||||
#include <boost/asio.hpp>
|
||||
|
||||
namespace aedis {
|
||||
|
||||
@@ -16,7 +16,7 @@ namespace aedis {
|
||||
namespace redis {
|
||||
|
||||
/** \brief Redis commands.
|
||||
* \ingroup enums
|
||||
* \ingroup any
|
||||
*
|
||||
* For a full list of commands see https://redis.io/commands.
|
||||
*
|
||||
@@ -437,7 +437,7 @@ enum class command {
|
||||
};
|
||||
|
||||
/** \brief Converts a command to a string
|
||||
* \ingroup functions
|
||||
* \ingroup any
|
||||
*
|
||||
* \param c The command to convert.
|
||||
*/
|
||||
@@ -452,12 +452,12 @@ char const* to_string(command c);
|
||||
std::ostream& operator<<(std::ostream& os, command c);
|
||||
|
||||
/** \brief Returns true for commands with push response.
|
||||
* \ingroup functions
|
||||
* \ingroup any
|
||||
*/
|
||||
bool has_push_response(command cmd);
|
||||
|
||||
/** \brief Creates a serializer for a \c std::string.
|
||||
* \ingroup functions
|
||||
* \ingroup any
|
||||
* \param storage The string.
|
||||
*/
|
||||
template <class CharT, class Traits, class Allocator>
|
||||
|
||||
@@ -18,22 +18,33 @@ namespace resp3 {
|
||||
namespace experimental {
|
||||
|
||||
/** \brief A high level redis client.
|
||||
* \ingroup classes
|
||||
* \ingroup any
|
||||
*
|
||||
* This Redis client keeps a connection to the database open and
|
||||
* manage reconnections.
|
||||
* uses it for all communication with Redis. For examples on how to
|
||||
* use see the examples chat_room.cpp, echo_server.cpp and redis_client.cpp.
|
||||
*
|
||||
* \remarks This class reuses its internal buffers for requests and
|
||||
* for reading Redis responses. With time it will allocate less and
|
||||
* less.
|
||||
*/
|
||||
class client : public std::enable_shared_from_this<client> {
|
||||
public:
|
||||
/// The response adapter type.
|
||||
using adapter_type = std::function<void(redis::command, type, std::size_t, std::size_t, char const*, std::size_t, std::error_code&)>;
|
||||
/** \brief The extended response adapter type.
|
||||
*
|
||||
* The difference between the adapter and extended_adapter
|
||||
* concepts is that the extended get a command redis::parameter.
|
||||
*/
|
||||
using extented_adapter_type = std::function<void(redis::command, type, std::size_t, std::size_t, char const*, std::size_t, std::error_code&)>;
|
||||
|
||||
/// The type of the message callback.
|
||||
using on_message_type = std::function<void(std::error_code ec, redis::command)>;
|
||||
|
||||
private:
|
||||
using tcp_socket = net::use_awaitable_t<>::as_default_on_t<net::ip::tcp::socket>;
|
||||
/// The type of the socket used by the client.
|
||||
//using socket_type = net::use_awaitable_t<>::as_default_on_t<net::ip::tcp::socket>;
|
||||
using socket_type = net::ip::tcp::socket;
|
||||
|
||||
private:
|
||||
struct request_info {
|
||||
// Request size in bytes.
|
||||
std::size_t size = 0;
|
||||
@@ -53,18 +64,21 @@ private:
|
||||
std::queue<request_info> req_info_;
|
||||
|
||||
// The stream.
|
||||
tcp_socket socket_;
|
||||
socket_type socket_;
|
||||
|
||||
// Timer used to inform the write coroutine that it can write the
|
||||
// next message in the output queue.
|
||||
net::steady_timer timer_;
|
||||
|
||||
// Response adapter.
|
||||
adapter_type adapter_ = [](redis::command, type, std::size_t, std::size_t, char const*, std::size_t, std::error_code&) {};
|
||||
extented_adapter_type extended_adapter_ = [](redis::command, type, std::size_t, std::size_t, char const*, std::size_t, std::error_code&) {};
|
||||
|
||||
// Message callback.
|
||||
on_message_type on_msg_ = [](std::error_code ec, redis::command) {};
|
||||
|
||||
// Set when the writer coroutine should stop.
|
||||
bool stop_writer_ = false;
|
||||
|
||||
// A coroutine that keeps reading the socket. When a message
|
||||
// arrives it calls on_message.
|
||||
net::awaitable<void> reader();
|
||||
@@ -73,12 +87,6 @@ private:
|
||||
// to be sent.
|
||||
net::awaitable<void> writer();
|
||||
|
||||
net::awaitable<void> say_hello();
|
||||
|
||||
// The connection manager. It keeps trying the reconnect to the
|
||||
// server when the connection is lost.
|
||||
net::awaitable<void> connection_manager();
|
||||
|
||||
/* Prepares the back of the queue to receive further commands.
|
||||
*
|
||||
* If true is returned the request in the front of the queue can be
|
||||
@@ -95,9 +103,21 @@ public:
|
||||
*/
|
||||
client(net::any_io_executor ex);
|
||||
|
||||
/** \brief Prepares the client for execution.
|
||||
/// Returns the executor used for I/O with Redis.
|
||||
auto get_executor() {return socket_.get_executor();}
|
||||
|
||||
/** \brief Starts communication with Redis.
|
||||
*
|
||||
* This functions will send the hello command to Redis and spawn
|
||||
* the read and write coroutines.
|
||||
*
|
||||
* \param socket A socket that is connected to redis.
|
||||
*
|
||||
* \returns This function returns an awaitable on which users should \c
|
||||
* co_await. When the communication with Redis is lost the
|
||||
* coroutine will finally co_return.
|
||||
*/
|
||||
void prepare();
|
||||
net::awaitable<void> engage(socket_type socket);
|
||||
|
||||
/** \brief Adds a command to the command queue.
|
||||
*
|
||||
@@ -106,8 +126,8 @@ public:
|
||||
template <class... Ts>
|
||||
void send(redis::command cmd, Ts const&... args);
|
||||
|
||||
/// Sets the response adapter.
|
||||
void set_adapter(adapter_type adapter);
|
||||
/// Sets an extended response adapter.
|
||||
void set_extended_adapter(extented_adapter_type adapter);
|
||||
|
||||
/// Sets the message callback;
|
||||
void set_msg_callback(on_message_type on_msg);
|
||||
|
||||
@@ -18,16 +18,27 @@ namespace experimental {
|
||||
client::client(net::any_io_executor ex)
|
||||
: socket_{ex}
|
||||
, timer_{ex}
|
||||
{ }
|
||||
{
|
||||
timer_.expires_at(std::chrono::steady_clock::time_point::max());
|
||||
}
|
||||
|
||||
net::awaitable<void> client::reader()
|
||||
{
|
||||
// Writes and reads continuosly from the socket.
|
||||
for (std::string buffer;;) {
|
||||
while (!std::empty(req_info_)) {
|
||||
co_await net::async_write(socket_, net::buffer(requests_.data(), req_info_.front().size));
|
||||
// Notice this coro can get scheduled while the write operation
|
||||
// in the writer is ongoing. so we have to check.
|
||||
while (!std::empty(req_info_) && req_info_.front().size != 0) {
|
||||
assert(!std::empty(requests_));
|
||||
boost::system::error_code ec;
|
||||
co_await
|
||||
net::async_write(
|
||||
socket_,
|
||||
net::buffer(requests_.data(), req_info_.front().size),
|
||||
net::redirect_error(net::use_awaitable, ec));
|
||||
|
||||
requests_.erase(0, req_info_.front().size);
|
||||
req_info_.front().size = 0;
|
||||
|
||||
if (req_info_.front().cmds != 0)
|
||||
break; // We must await the responses.
|
||||
@@ -37,21 +48,41 @@ net::awaitable<void> client::reader()
|
||||
|
||||
do { // Keeps reading while there are no messages queued waiting to be sent.
|
||||
do { // Consumes the responses to all commands in the request.
|
||||
auto const t = co_await async_read_type(socket_, net::dynamic_buffer(buffer));
|
||||
boost::system::error_code ec;
|
||||
auto const t =
|
||||
co_await async_read_type(socket_, net::dynamic_buffer(buffer),
|
||||
net::redirect_error(net::use_awaitable, ec));
|
||||
if (ec) {
|
||||
stop_writer_ = true;
|
||||
timer_.cancel();
|
||||
co_return;
|
||||
}
|
||||
|
||||
if (t == type::push) {
|
||||
auto adapter = [this](type t, std::size_t aggregate_size, std::size_t depth, char const* data, std::size_t size, std::error_code& ec)
|
||||
{adapter_(redis::command::unknown, t, aggregate_size, depth, data, size, ec);};
|
||||
{extended_adapter_(redis::command::unknown, t, aggregate_size, depth, data, size, ec);};
|
||||
|
||||
boost::system::error_code ec;
|
||||
co_await resp3::async_read(socket_, net::dynamic_buffer(buffer), adapter, net::redirect_error(net::use_awaitable, ec));
|
||||
on_msg_(ec, redis::command::unknown);
|
||||
if (ec) { // TODO: Return only on non aedis errors.
|
||||
stop_writer_ = true;
|
||||
timer_.cancel();
|
||||
co_return;
|
||||
}
|
||||
|
||||
} else {
|
||||
auto adapter = [this](type t, std::size_t aggregate_size, std::size_t depth, char const* data, std::size_t size, std::error_code& ec)
|
||||
{adapter_(commands_.front(), t, aggregate_size, depth, data, size, ec);};
|
||||
{extended_adapter_(commands_.front(), t, aggregate_size, depth, data, size, ec);};
|
||||
|
||||
boost::system::error_code ec;
|
||||
co_await resp3::async_read(socket_, net::dynamic_buffer(buffer), adapter, net::redirect_error(net::use_awaitable, ec));
|
||||
on_msg_(ec, commands_.front());
|
||||
if (ec) { // TODO: Return only on non aedis errors.
|
||||
stop_writer_ = true;
|
||||
timer_.cancel();
|
||||
co_return;
|
||||
}
|
||||
|
||||
commands_.pop();
|
||||
--req_info_.front().cmds;
|
||||
}
|
||||
@@ -71,14 +102,33 @@ net::awaitable<void> client::reader()
|
||||
|
||||
net::awaitable<void> client::writer()
|
||||
{
|
||||
boost::system::error_code ec;
|
||||
while (socket_.is_open()) {
|
||||
boost::system::error_code ec;
|
||||
ec = {};
|
||||
co_await timer_.async_wait(net::redirect_error(net::use_awaitable, ec));
|
||||
while (!std::empty(req_info_)) {
|
||||
co_await net::async_write(socket_, net::buffer(requests_.data(), req_info_.front().size));
|
||||
if (stop_writer_)
|
||||
co_return;
|
||||
|
||||
// Notice this coro can get scheduled while the write operation
|
||||
// in the reader is ongoing. so we have to check.
|
||||
while (!std::empty(req_info_) && req_info_.front().size != 0) {
|
||||
assert(!std::empty(requests_));
|
||||
ec = {};
|
||||
co_await net::async_write(
|
||||
socket_, net::buffer(requests_.data(), req_info_.front().size),
|
||||
net::redirect_error(net::use_awaitable, ec));
|
||||
if (ec) {
|
||||
// What should we do here exactly? Closing the socket will
|
||||
// cause the reader coroutine to return so that the engage
|
||||
// coroutine returns to the user.
|
||||
socket_.close();
|
||||
co_return;
|
||||
}
|
||||
|
||||
requests_.erase(0, req_info_.front().size);
|
||||
req_info_.front().size = 0;
|
||||
|
||||
if (req_info_.front().cmds != 0)
|
||||
if (req_info_.front().cmds != 0)
|
||||
break;
|
||||
|
||||
req_info_.pop();
|
||||
@@ -86,44 +136,6 @@ net::awaitable<void> client::writer()
|
||||
}
|
||||
}
|
||||
|
||||
net::awaitable<void> client::say_hello()
|
||||
{
|
||||
std::string request;
|
||||
auto sr = redis::make_serializer(request);
|
||||
sr.push(redis::command::hello, 3);
|
||||
co_await net::async_write(socket_, net::buffer(request));
|
||||
|
||||
std::string buffer;
|
||||
auto adapter = [this](type t, std::size_t aggregate_size, std::size_t depth, char const* data, std::size_t size, std::error_code& ec)
|
||||
{adapter_(redis::command::hello, t, aggregate_size, depth, data, size, ec);};
|
||||
co_await resp3::async_read(socket_, net::dynamic_buffer(buffer), adapter);
|
||||
}
|
||||
|
||||
net::awaitable<void>
|
||||
client::connection_manager()
|
||||
{
|
||||
using namespace aedis::net::experimental::awaitable_operators;
|
||||
using tcp_resolver = aedis::net::use_awaitable_t<>::as_default_on_t<aedis::net::ip::tcp::resolver>;
|
||||
|
||||
for (;;) {
|
||||
tcp_resolver resolver{socket_.get_executor()};
|
||||
auto const res = co_await resolver.async_resolve("127.0.0.1", "6379");
|
||||
co_await net::async_connect(socket_, res);
|
||||
|
||||
co_await say_hello();
|
||||
|
||||
timer_.expires_at(std::chrono::steady_clock::time_point::max());
|
||||
co_await (reader() && writer());
|
||||
|
||||
socket_.close();
|
||||
timer_.cancel();
|
||||
|
||||
timer_.expires_after(std::chrono::seconds{1});
|
||||
boost::system::error_code ec;
|
||||
co_await timer_.async_wait(net::redirect_error(net::use_awaitable, ec));
|
||||
}
|
||||
}
|
||||
|
||||
bool client::prepare_next()
|
||||
{
|
||||
if (std::empty(req_info_)) {
|
||||
@@ -131,7 +143,9 @@ bool client::prepare_next()
|
||||
return true;
|
||||
}
|
||||
|
||||
if (std::size(req_info_) == 1) {
|
||||
if (req_info_.front().size == 0) {
|
||||
// It has already been written and we are waiting for the
|
||||
// responses.
|
||||
req_info_.push({});
|
||||
return false;
|
||||
}
|
||||
@@ -139,16 +153,9 @@ bool client::prepare_next()
|
||||
return false;
|
||||
}
|
||||
|
||||
void client::prepare()
|
||||
void client::set_extended_adapter(extented_adapter_type adapter)
|
||||
{
|
||||
net::co_spawn(socket_.get_executor(),
|
||||
[self = this->shared_from_this()]{ return self->connection_manager(); },
|
||||
net::detached);
|
||||
}
|
||||
|
||||
void client::set_adapter(adapter_type adapter)
|
||||
{
|
||||
adapter_ = adapter;
|
||||
extended_adapter_ = adapter;
|
||||
}
|
||||
|
||||
void client::set_msg_callback(on_message_type on_msg)
|
||||
@@ -156,6 +163,37 @@ void client::set_msg_callback(on_message_type on_msg)
|
||||
on_msg_ = on_msg;
|
||||
}
|
||||
|
||||
net::awaitable<void> client::engage(socket_type socket)
|
||||
{
|
||||
using namespace aedis::net::experimental::awaitable_operators;
|
||||
|
||||
socket_ = std::move(socket);
|
||||
|
||||
std::string request;
|
||||
auto sr = redis::make_serializer(request);
|
||||
sr.push(redis::command::hello, 3);
|
||||
|
||||
boost::system::error_code ec;
|
||||
co_await net::async_write(socket_, net::buffer(request), net::redirect_error(net::use_awaitable, ec));
|
||||
if (ec)
|
||||
co_return;
|
||||
|
||||
std::string buffer;
|
||||
auto adapter = [this](type t, std::size_t aggregate_size, std::size_t depth, char const* data, std::size_t size, std::error_code& ec)
|
||||
{extended_adapter_(redis::command::hello, t, aggregate_size, depth, data, size, ec);};
|
||||
|
||||
co_await
|
||||
resp3::async_read(
|
||||
socket_, net::dynamic_buffer(buffer), adapter, net::redirect_error(net::use_awaitable, ec));
|
||||
|
||||
on_msg_(ec, redis::command::hello);
|
||||
|
||||
if (ec)
|
||||
co_return;
|
||||
|
||||
co_await (reader() && writer());
|
||||
}
|
||||
|
||||
} // experimental
|
||||
} // resp3
|
||||
} // aedis
|
||||
|
||||
@@ -13,7 +13,7 @@ namespace aedis {
|
||||
namespace resp3 {
|
||||
|
||||
/** \brief Creates a void response adapter.
|
||||
\ingroup functions
|
||||
\ingroup any
|
||||
|
||||
The adapter returned by this function ignores responses and is
|
||||
useful to avoid wasting time with responses which the user is
|
||||
@@ -30,32 +30,36 @@ auto adapt() noexcept
|
||||
{ return response_traits<void>::adapt(); }
|
||||
|
||||
/** \brief Adapts user data to read operations.
|
||||
* \ingroup functions
|
||||
* \ingroup any
|
||||
*
|
||||
* For example
|
||||
* The following types are supported.
|
||||
*
|
||||
* 1. Integer data types e.g. `int`, `unsigned`, etc.
|
||||
* - Integer data types e.g. `int`, `unsigned`, etc.
|
||||
*
|
||||
* 1. `std::string`
|
||||
* - `std::string`
|
||||
*
|
||||
* We also support the following C++ containers
|
||||
*
|
||||
* 1. `std::vector<T>`. Can be used with any RESP3 aggregate type.
|
||||
* - `std::vector<T>`. Can be used with any RESP3 aggregate type.
|
||||
*
|
||||
* 1. `std::deque<T>`. Can be used with any RESP3 aggregate type.
|
||||
* - `std::deque<T>`. Can be used with any RESP3 aggregate type.
|
||||
*
|
||||
* 1. `std::list<T>`. Can be used with any RESP3 aggregate type.
|
||||
* - `std::list<T>`. Can be used with any RESP3 aggregate type.
|
||||
*
|
||||
* 1. `std::set<T>`. Can be used with RESP3 set type.
|
||||
* - `std::set<T>`. Can be used with RESP3 set type.
|
||||
*
|
||||
* 1. `std::unordered_set<T>`. Can be used with RESP3 set type.
|
||||
* - `std::unordered_set<T>`. Can be used with RESP3 set type.
|
||||
*
|
||||
* 1. `std::map<T>`. Can be used with RESP3 hash type.
|
||||
* - `std::map<T>`. Can be used with RESP3 hash type.
|
||||
*
|
||||
* 1. `std::unordered_map<T>`. Can be used with RESP3 hash type.
|
||||
* - `std::unordered_map<T>`. Can be used with RESP3 hash type.
|
||||
*
|
||||
* All these types can be wrapped in an `std::optional<T>`.
|
||||
* All these types can be wrapped in an `std::optional<T>`. This
|
||||
* function also support \c std::tuple to read the response to
|
||||
* tuples. At the moment this feature supports only transactions that
|
||||
* contain simple types or aggregates that don't contain aggregates
|
||||
* themselves (as in most cases).
|
||||
*
|
||||
* Example usage:
|
||||
*
|
||||
@@ -63,6 +67,24 @@ auto adapt() noexcept
|
||||
* std::unordered_map<std::string, std::string> cont;
|
||||
* co_await async_read(socket, buffer, adapt(cont));
|
||||
* @endcode
|
||||
*
|
||||
* For a transaction
|
||||
*
|
||||
* @code
|
||||
sr.push(command::multi);
|
||||
sr.push(command::ping, ...);
|
||||
sr.push(command::incr, ...);
|
||||
sr.push_range(command::rpush, ...);
|
||||
sr.push(command::lrange, ...);
|
||||
sr.push(command::incr, ...);
|
||||
sr.push(command::exec);
|
||||
|
||||
co_await async_write(socket, buffer(request));
|
||||
|
||||
// Reads the response to a transaction
|
||||
std::tuple<std::string, int, int, std::vector<std::string>, int> execs;
|
||||
co_await resp3::async_read(socket, dynamic_buffer(buffer), adapt(execs));
|
||||
* @endcode
|
||||
*/
|
||||
template<class T>
|
||||
auto adapt(T& t) noexcept
|
||||
|
||||
@@ -7,7 +7,7 @@ namespace resp3 {
|
||||
namespace adapter {
|
||||
|
||||
/** \brief Errors that may occurr when reading a response.
|
||||
* \ingroup enums
|
||||
* \ingroup any
|
||||
*/
|
||||
enum class error
|
||||
{
|
||||
|
||||
@@ -8,7 +8,7 @@ namespace aedis {
|
||||
namespace resp3 {
|
||||
|
||||
/** \brief RESP3 parsing errors.
|
||||
* \ingroup enums
|
||||
* \ingroup any
|
||||
*/
|
||||
enum class error
|
||||
{
|
||||
@@ -54,7 +54,7 @@ std::error_category const& category()
|
||||
} // detail
|
||||
|
||||
/** \brief Converts an error in an std::error_code object.
|
||||
* \ingroup functions
|
||||
* \ingroup any
|
||||
*/
|
||||
inline
|
||||
std::error_code make_error_code(error e)
|
||||
|
||||
@@ -16,7 +16,7 @@ namespace aedis {
|
||||
namespace resp3 {
|
||||
|
||||
/** \brief A node in the response tree.
|
||||
* \ingroup classes
|
||||
* \ingroup any
|
||||
*
|
||||
* Redis responses are the pre-order view of the response tree (see
|
||||
* https://en.wikipedia.org/wiki/Tree_traversal#Pre-order,_NLR).
|
||||
@@ -36,31 +36,31 @@ struct node {
|
||||
};
|
||||
|
||||
/** \brief Converts the node to a string.
|
||||
* \ingroup functions
|
||||
* \ingroup any
|
||||
*
|
||||
* \param obj The node object.
|
||||
*/
|
||||
std::string to_string(node const& obj);
|
||||
|
||||
/** \brief Compares a node for equality.
|
||||
* \ingroup operators
|
||||
* \ingroup any
|
||||
*/
|
||||
bool operator==(node const& a, node const& b);
|
||||
|
||||
/** \brief Writes the node to the stream.
|
||||
* \ingroup operators
|
||||
* \ingroup any
|
||||
*
|
||||
* NOTE: Binary data is not converted to text.
|
||||
*/
|
||||
std::ostream& operator<<(std::ostream& os, node const& o);
|
||||
|
||||
/** \brief Writes the response to the output stream
|
||||
* \ingroup functions
|
||||
* \ingroup any
|
||||
*/
|
||||
std::string to_string(std::vector<node> const& vec);
|
||||
|
||||
/** \brief Writes the response to the output stream
|
||||
* \ingroup operators
|
||||
* \ingroup any
|
||||
*/
|
||||
std::ostream& operator<<(std::ostream& os, std::vector<node> const& r);
|
||||
|
||||
|
||||
@@ -27,7 +27,7 @@ namespace aedis {
|
||||
namespace resp3 {
|
||||
|
||||
/** \brief Traits class for response objects.
|
||||
* \ingroup classes
|
||||
* \ingroup any
|
||||
*/
|
||||
template <class T>
|
||||
struct response_traits
|
||||
|
||||
@@ -13,7 +13,7 @@ namespace aedis {
|
||||
namespace resp3 {
|
||||
|
||||
/** @brief Creates a Redis request from user data.
|
||||
* \ingroup classes
|
||||
* \ingroup any
|
||||
*
|
||||
* A request is composed of one or more redis commands and is
|
||||
* referred to in the redis documentation as a pipeline, see
|
||||
|
||||
@@ -15,7 +15,7 @@ namespace aedis {
|
||||
namespace resp3 {
|
||||
|
||||
/** \brief RESP3 types
|
||||
\ingroup enums
|
||||
\ingroup any
|
||||
|
||||
The RESP3 full specification can be found at https://github.com/antirez/RESP3/blob/74adea588783e463c7e84793b325b088fe6edd1c/spec.md
|
||||
*/
|
||||
@@ -57,7 +57,7 @@ enum class type
|
||||
};
|
||||
|
||||
/** \brief Returns the string representation of the type.
|
||||
* \ingroup functions
|
||||
* \ingroup any
|
||||
* \param t RESP3 type.
|
||||
*/
|
||||
char const* to_string(type t);
|
||||
@@ -70,13 +70,13 @@ char const* to_string(type t);
|
||||
std::ostream& operator<<(std::ostream& os, type t);
|
||||
|
||||
/** \brief Returns true if the data type is an aggregate.
|
||||
* \ingroup functions
|
||||
* \ingroup any
|
||||
* \param t RESP3 type.
|
||||
*/
|
||||
bool is_aggregate(type t);
|
||||
|
||||
/** @brief Returns the element multilicity.
|
||||
* \ingroup functions
|
||||
* \ingroup any
|
||||
* \param t RESP3 type.
|
||||
*
|
||||
* For type map and attribute this value is 2, all other types have
|
||||
|
||||
@@ -13,7 +13,7 @@ namespace aedis {
|
||||
namespace sentinel {
|
||||
|
||||
/** \brief Sentinel commands.
|
||||
* \ingroup enums
|
||||
* \ingroup any
|
||||
*
|
||||
* For a full list of commands see https://redis.io/topics/sentinel
|
||||
*
|
||||
@@ -56,7 +56,7 @@ enum class command {
|
||||
};
|
||||
|
||||
/** \brief Converts a sentinel command to a string
|
||||
* \ingroup functions
|
||||
* \ingroup any
|
||||
*
|
||||
* \param c The command to convert.
|
||||
*/
|
||||
@@ -71,7 +71,7 @@ char const* to_string(command c);
|
||||
std::ostream& operator<<(std::ostream& os, command c);
|
||||
|
||||
/** \brief Returns true for sentinel commands with push response.
|
||||
* \ingroup functions
|
||||
* \ingroup any
|
||||
*/
|
||||
bool has_push_response(command cmd);
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
AC_PREREQ([2.69])
|
||||
AC_INIT([aedis], [1.0.0], [mzimbres@gmail.com])
|
||||
AC_INIT([Aedis], [0.0.1], [mzimbres@gmail.com])
|
||||
AC_CONFIG_MACRO_DIR([m4])
|
||||
#AC_CONFIG_SRCDIR([src/aedis.cpp])
|
||||
AC_CONFIG_HEADERS([config.h])
|
||||
@@ -18,5 +18,5 @@ AC_CHECK_HEADER_STDBOOL
|
||||
AC_TYPE_UINT64_T
|
||||
AC_CHECK_TYPES([ptrdiff_t])
|
||||
|
||||
AC_CONFIG_FILES([Makefile])
|
||||
AC_CONFIG_FILES([Makefile doc/Doxyfile])
|
||||
AC_OUTPUT
|
||||
|
||||
@@ -38,13 +38,13 @@ PROJECT_NAME = "Aedis"
|
||||
# could be handy for archiving the generated documentation or if some version
|
||||
# control system is used.
|
||||
|
||||
PROJECT_NUMBER = 1.0.0
|
||||
PROJECT_NUMBER = "0.0.1"
|
||||
|
||||
# Using the PROJECT_BRIEF tag one can provide an optional one line description
|
||||
# for a project that appears at the top of each page and should give viewer a
|
||||
# quick idea about the purpose of the project. Keep the description short.
|
||||
|
||||
PROJECT_BRIEF = "A low level Redis client library"
|
||||
PROJECT_BRIEF = "Low level Redis client library"
|
||||
|
||||
# With the PROJECT_LOGO tag one can specify a logo or an icon that is included
|
||||
# in the documentation. The maximum height of the logo should not exceed 55
|
||||
@@ -58,7 +58,7 @@ PROJECT_LOGO =
|
||||
# entered, it will be relative to the location where doxygen was started. If
|
||||
# left blank the current directory will be used.
|
||||
|
||||
OUTPUT_DIRECTORY = /home/marcelo/my/aedis-gh-pages
|
||||
OUTPUT_DIRECTORY = ../aedis-gh-pages
|
||||
|
||||
# If the CREATE_SUBDIRS tag is set to YES then doxygen will create 4096 sub-
|
||||
# directories (in 2 levels) under the output directory of each output format and
|
||||
|
||||
@@ -24,7 +24,7 @@ $extrastylesheet
|
||||
<table bgcolor="#346295" cellspacing="0" cellpadding="6">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td valign="middle" style="color: #FFFFFF" nowrap="nowrap"><font size="6">$projectname</font>   <br> $projectbrief </td>
|
||||
<td valign="middle" style="color: #FFFFFF" nowrap="nowrap"><font size="6">$projectname $projectnumber</font>   <br> $projectbrief </td>
|
||||
<td style="width:100%"> $searchbox </td>
|
||||
</tr>
|
||||
</tbody>
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
#include <aedis/src.hpp>
|
||||
|
||||
#include "lib/user_session.hpp"
|
||||
#include "lib/net_utils.hpp"
|
||||
|
||||
namespace net = aedis::net;
|
||||
using aedis::redis::command;
|
||||
@@ -59,7 +60,7 @@ public:
|
||||
resps_.clear();
|
||||
}
|
||||
|
||||
auto get_adapter()
|
||||
auto get_extended_adapter()
|
||||
{
|
||||
return [adapter = adapt(resps_)](command, type t, std::size_t aggregate_size, std::size_t depth, char const* data, std::size_t size, std::error_code& ec) mutable
|
||||
{ return adapter(t, aggregate_size, depth, data, size, ec); };
|
||||
@@ -69,6 +70,16 @@ public:
|
||||
{ sessions_.push_back(session); }
|
||||
};
|
||||
|
||||
net::awaitable<void> connection_manager(std::shared_ptr<client> db)
|
||||
{
|
||||
try {
|
||||
auto socket = co_await connect();
|
||||
co_await db->engage(std::move(socket));
|
||||
} catch (std::exception const& e) {
|
||||
std::cerr << "Error: " << e.what() << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
net::awaitable<void> listener()
|
||||
{
|
||||
auto ex = co_await net::this_coro::executor;
|
||||
@@ -79,10 +90,10 @@ net::awaitable<void> listener()
|
||||
{ recv->on_message(ec, cmd); };
|
||||
|
||||
auto db = std::make_shared<client>(ex);
|
||||
db->set_adapter(recv->get_adapter());
|
||||
db->set_extended_adapter(recv->get_extended_adapter());
|
||||
db->set_msg_callback(on_db_msg);
|
||||
net::co_spawn(ex, connection_manager(db), net::detached);
|
||||
db->send(command::subscribe, "channel");
|
||||
db->prepare();
|
||||
|
||||
auto on_user_msg = [db](std::string const& msg)
|
||||
{
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
#include <aedis/src.hpp>
|
||||
|
||||
#include "lib/user_session.hpp"
|
||||
#include "lib/net_utils.hpp"
|
||||
|
||||
namespace net = aedis::net;
|
||||
using aedis::redis::command;
|
||||
@@ -51,13 +52,13 @@ public:
|
||||
{
|
||||
std::cout << "Echos so far: " << resps_.front().data << std::endl;
|
||||
} break;
|
||||
default: { assert(false); }
|
||||
default: { /* Ignore */; }
|
||||
}
|
||||
|
||||
resps_.clear();
|
||||
}
|
||||
|
||||
auto get_adapter()
|
||||
auto get_extended_adapter()
|
||||
{
|
||||
return [adapter = adapt(resps_)](command, type t, std::size_t aggregate_size, std::size_t depth, char const* data, std::size_t size, std::error_code& ec) mutable
|
||||
{ return adapter(t, aggregate_size, depth, data, size, ec); };
|
||||
@@ -67,6 +68,16 @@ public:
|
||||
{ sessions_.push(session); }
|
||||
};
|
||||
|
||||
net::awaitable<void> connection_manager(std::shared_ptr<client> db)
|
||||
{
|
||||
try {
|
||||
auto socket = co_await connect();
|
||||
co_await db->engage(std::move(socket));
|
||||
} catch (std::exception const& e) {
|
||||
std::cerr << "Error: " << e.what() << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
net::awaitable<void> listener()
|
||||
{
|
||||
auto ex = co_await net::this_coro::executor;
|
||||
@@ -77,9 +88,9 @@ net::awaitable<void> listener()
|
||||
{ recv->on_message(ec, cmd); };
|
||||
|
||||
auto db = std::make_shared<client>(ex);
|
||||
db->set_adapter(recv->get_adapter());
|
||||
db->set_extended_adapter(recv->get_extended_adapter());
|
||||
db->set_msg_callback(on_db_msg);
|
||||
db->prepare();
|
||||
net::co_spawn(ex, connection_manager(db), net::detached);
|
||||
|
||||
for (;;) {
|
||||
auto socket = co_await acceptor.async_accept(net::use_awaitable);
|
||||
|
||||
@@ -51,8 +51,8 @@ net::awaitable<void> ping()
|
||||
|
||||
// Print the responses.
|
||||
std::cout
|
||||
<< "ping: " << ping << "\n"
|
||||
<< "incr: " << incr << "\n";
|
||||
<< "ping: " << ping << "\n"
|
||||
<< "incr: " << incr << "\n";
|
||||
|
||||
} catch (std::exception const& e) {
|
||||
std::cerr << e.what() << std::endl;
|
||||
|
||||
@@ -24,3 +24,28 @@ connect(
|
||||
co_return std::move(socket);
|
||||
}
|
||||
|
||||
//net::awaitable<void>
|
||||
//client::connection_manager()
|
||||
//{
|
||||
// using namespace aedis::net::experimental::awaitable_operators;
|
||||
// using tcp_resolver = aedis::net::use_awaitable_t<>::as_default_on_t<aedis::net::ip::tcp::resolver>;
|
||||
//
|
||||
// for (;;) {
|
||||
// tcp_resolver resolver{socket_.get_executor()};
|
||||
// auto const res = co_await resolver.async_resolve("127.0.0.1", "6379");
|
||||
// co_await net::async_connect(socket_, res);
|
||||
//
|
||||
// co_await say_hello();
|
||||
//
|
||||
// timer_.expires_at(std::chrono::steady_clock::time_point::max());
|
||||
// co_await (reader() && writer());
|
||||
//
|
||||
// socket_.close();
|
||||
// timer_.cancel();
|
||||
//
|
||||
// timer_.expires_after(std::chrono::seconds{1});
|
||||
// boost::system::error_code ec;
|
||||
// co_await timer_.async_wait(net::redirect_error(net::use_awaitable, ec));
|
||||
// }
|
||||
//}
|
||||
|
||||
|
||||
@@ -1,79 +0,0 @@
|
||||
/* Copyright (c) 2019 Marcelo Zimbres Silva (mzimbres at gmail dot com)
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
#include <iostream>
|
||||
#include <vector>
|
||||
#include <tuple>
|
||||
#include <array>
|
||||
|
||||
#include <aedis/aedis.hpp>
|
||||
#include <aedis/src.hpp>
|
||||
|
||||
#include "lib/net_utils.hpp"
|
||||
|
||||
namespace resp3 = aedis::resp3;
|
||||
using aedis::redis::command;
|
||||
using aedis::redis::make_serializer;
|
||||
using resp3::adapt;
|
||||
using resp3::node;
|
||||
|
||||
namespace net = aedis::net;
|
||||
using net::async_write;
|
||||
using net::buffer;
|
||||
using net::dynamic_buffer;
|
||||
|
||||
net::awaitable<void> nested_response()
|
||||
{
|
||||
try {
|
||||
auto socket = co_await connect();
|
||||
|
||||
auto list = {"one", "two", "three"};
|
||||
|
||||
std::string request;
|
||||
auto sr = make_serializer(request);
|
||||
sr.push(command::hello, 3);
|
||||
sr.push(command::flushall);
|
||||
sr.push(command::multi);
|
||||
sr.push(command::ping, "Some message");
|
||||
sr.push(command::incr, "incr-key");
|
||||
sr.push_range(command::rpush, "list-key", std::cbegin(list), std::cend(list));
|
||||
sr.push(command::lrange, "list-key", 0, -1);
|
||||
sr.push(command::exec);
|
||||
sr.push(command::quit);
|
||||
co_await async_write(socket, buffer(request));
|
||||
|
||||
// Expected responses.
|
||||
std::vector<node> exec;
|
||||
|
||||
// Reads the response.
|
||||
std::string buffer;
|
||||
co_await resp3::async_read(socket, dynamic_buffer(buffer)); // hello
|
||||
co_await resp3::async_read(socket, dynamic_buffer(buffer)); // flushall
|
||||
co_await resp3::async_read(socket, dynamic_buffer(buffer)); // multi
|
||||
co_await resp3::async_read(socket, dynamic_buffer(buffer)); // ping
|
||||
co_await resp3::async_read(socket, dynamic_buffer(buffer)); // incr
|
||||
co_await resp3::async_read(socket, dynamic_buffer(buffer)); // rpush
|
||||
co_await resp3::async_read(socket, dynamic_buffer(buffer)); // lrange
|
||||
co_await resp3::async_read(socket, dynamic_buffer(buffer), adapt(exec));
|
||||
co_await resp3::async_read(socket, dynamic_buffer(buffer)); // quit
|
||||
|
||||
// Prints the response.
|
||||
std::cout << "General format:\n";
|
||||
for (auto const& e: exec) std::cout << e << "\n";
|
||||
|
||||
} catch (std::exception const& e) {
|
||||
std::cerr << e.what() << std::endl;
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
net::io_context ioc;
|
||||
co_spawn(ioc, nested_response(), net::detached);
|
||||
ioc.run();
|
||||
}
|
||||
@@ -1,55 +0,0 @@
|
||||
/* Copyright (c) 2019 Marcelo Zimbres Silva (mzimbres@gmail.com)
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
#include <iostream>
|
||||
|
||||
#include <aedis/aedis.hpp>
|
||||
#include <aedis/src.hpp>
|
||||
|
||||
namespace net = aedis::net;
|
||||
using aedis::redis::command;
|
||||
using aedis::resp3::experimental::client;
|
||||
using aedis::resp3::node;
|
||||
using aedis::resp3::type;
|
||||
|
||||
int main()
|
||||
{
|
||||
try {
|
||||
std::vector<node> resps;
|
||||
|
||||
auto on_msg = [&resps](std::error_code ec, command cmd)
|
||||
{
|
||||
if (ec) {
|
||||
std::cerr << "Error: " << ec.message() << std::endl;
|
||||
return;
|
||||
}
|
||||
|
||||
std::cout << cmd << ": " << resps.front().data << std::endl;
|
||||
resps.clear();
|
||||
};
|
||||
|
||||
net::io_context ioc{1};
|
||||
|
||||
// This adapter uses the general response that is suitable for
|
||||
// all commands, so the command parameter will be ignored.
|
||||
auto adapter = [adapter = adapt(resps)](command, type t, std::size_t aggregate_size, std::size_t depth, char const* data, std::size_t size, std::error_code& ec) mutable
|
||||
{ return adapter(t, aggregate_size, depth, data, size, ec); };
|
||||
|
||||
auto db = std::make_shared<client>(ioc.get_executor());
|
||||
db->set_adapter(adapter);
|
||||
db->set_msg_callback(on_msg);
|
||||
db->send(command::ping, "O rato roeu a roupa do rei de Roma");
|
||||
db->send(command::incr, "redis-client-counter");
|
||||
db->send(command::quit);
|
||||
db->prepare();
|
||||
|
||||
ioc.run();
|
||||
} catch (std::exception const& e) {
|
||||
std::cerr << e.what() << std::endl;
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user