2
0
mirror of https://github.com/boostorg/redis.git synced 2026-01-19 04:42:09 +00:00

Simplifications.

This commit is contained in:
Marcelo Zimbres
2022-01-30 15:36:01 +01:00
parent 62cea27e4e
commit 983e844be5
10 changed files with 70 additions and 186 deletions

View File

@@ -83,7 +83,6 @@ nobase_include_HEADERS =\
nobase_noinst_HEADERS =\
$(top_srcdir)/examples/lib/net_utils.hpp\
$(top_srcdir)/examples/lib/responses.hpp\
$(top_srcdir)/examples/lib/user_session.hpp\
$(top_srcdir)/tests/check.hpp\
$(top_srcdir)/tests/test_stream.hpp

View File

@@ -1,53 +1 @@
# Aedis
Aedis is a redis client designed for scalability and performance while
providing an easy and intuitive interface.
## Example
Lets us see some examples with increasing order of complexity.
See the `examples` directory for more examples.
### Ping
A very simple example with illustrative purposes. The example above
will start writing the `hello` command and proceed reading its
response. After that users can add further commands to the queue. See
the example directory for a complete example.
```cpp
net::awaitable<void> ping2()
{
auto socket = co_await make_connection();
resp3::stream<tcp_socket> stream{std::move(socket)};
resp3::request req;
req.push(command::hello, 3);
req.push(command::ping);
req.push(command::quit);
co_await stream.async_write(req);
while (!std::empty(req.commands)) {
resp3::response resp;
co_await stream.async_read(resp);
req.commands.pop();
std::cout << resp << std::endl;
}
}
```
## Installation
This library is header only. To install it run
```cpp
$ sudo make install
```
or copy the include folder to where you want. You will also need to include
the following header in one of your source files e.g. `aedis.cpp`
```cpp
#include <aedis/impl/src.hpp>
```
See https://mzimbres.github.io/aedis/

View File

@@ -162,7 +162,7 @@
* - \b Redis-client: used in on example.
* - \b Redis \b Sentinel \b server: used in some examples.
*
* \section Installing
* \section Installation
*
* Get the latest release and follow the steps
*
@@ -197,6 +197,12 @@
* $ 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

View File

@@ -59,11 +59,11 @@ private:
// next message in the output queue.
net::steady_timer timer_;
// Response adapter. TODO: Set a default adapter.
adapter_type adapter_;
// Response adapter.
adapter_type adapter_ = [](redis::command, type, std::size_t, std::size_t, char const*, std::size_t, std::error_code&) {};
// Message callback. TODO: Set a default callback.
on_message_type on_msg_;
// Message callback.
on_message_type on_msg_ = [](std::error_code ec, redis::command) {};
// A coroutine that keeps reading the socket. When a message
// arrives it calls on_message.

View File

@@ -39,7 +39,13 @@ namespace resp3 {
* to_string, which must be made available over ADL.
*/
// TODO: Add an overload for containers.
// NOTE: Consider adding an overload for containers.
//
// TODO: Should we detect any std::pair or tuple in the type in the parameter
// pack to calculate the header size correctly?
//
// NOTE: For some commands like hset it would be a good idea to assert
// the value type is a pair.
template <class Storage, class Command>
class serializer {
private:
@@ -73,9 +79,6 @@ public:
template <class... Ts>
void push(Command cmd, Ts const&... args)
{
// TODO: Should we detect any std::pair in the type in the pack
// to calculate the header size correctly?
auto constexpr pack_size = sizeof...(Ts);
detail::add_header(*request_, 1 + pack_size);
@@ -106,9 +109,6 @@ public:
template <class Key, class ForwardIterator>
void push_range(Command cmd, Key const& key, ForwardIterator begin, ForwardIterator end)
{
// Note: For some commands like hset it would helpful to users
// to assert the value type is a pair.
using value_type = typename std::iterator_traits<ForwardIterator>::value_type;
auto constexpr size = detail::value_type_size<value_type>::size;
@@ -141,9 +141,6 @@ public:
template <class ForwardIterator>
void push_range(Command cmd, ForwardIterator begin, ForwardIterator end)
{
// Note: For some commands like hset it would be a good idea to assert
// the value type is a pair.
using value_type = typename std::iterator_traits<ForwardIterator>::value_type;
auto constexpr size = detail::value_type_size<value_type>::size;

View File

@@ -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 = /tmp/aedis
OUTPUT_DIRECTORY = /home/marcelo/my/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
@@ -1170,7 +1170,7 @@ GENERATE_HTML = YES
# The default directory is: html.
# This tag requires that the tag GENERATE_HTML is set to YES.
HTML_OUTPUT = html
HTML_OUTPUT = .
# The HTML_FILE_EXTENSION tag can be used to specify the file extension for each
# generated HTML page (for example: .htm, .php, .asp).

View File

@@ -12,26 +12,24 @@
#include <aedis/src.hpp>
#include "lib/user_session.hpp"
#include "lib/responses.hpp"
namespace net = aedis::net;
using aedis::redis::command;
using aedis::resp3::adapt;
using aedis::resp3::experimental::client;
using aedis::resp3::node;
using aedis::resp3::type;
using aedis::user_session;
using aedis::user_session_base;
using aedis::resp3::experimental::client;
// TODO: Delete sessions that have expired.
class receiver : public std::enable_shared_from_this<receiver> {
public:
private:
responses resps_;
std::vector<node> resps_;
std::vector<std::weak_ptr<user_session_base>> sessions_;
public:
auto get_adapter()
{ return adapter_wrapper{resps_}; }
auto add(std::shared_ptr<user_session_base> session)
{ sessions_.push_back(session); }
void on_message(std::error_code ec, command cmd)
{
if (ec) {
@@ -42,24 +40,33 @@ public:
switch (cmd) {
case command::incr:
{
std::cout << "Message so far: " << resps_.number << std::endl;
std::cout << "Message so far: " << resps_.front().data << std::endl;
} break;
case command::unknown: // Push
{
// TODO: Delete sessions lazily on traversal.
for (auto& weak: sessions_) {
if (auto session = weak.lock()) {
session->deliver(resps_.general.at(3).data);
session->deliver(resps_.at(3).data);
} else {
std::cout << "Session expired." << std::endl;
}
}
resps_.general.clear();
} break;
default: { /* Ignore */ }
}
resps_.clear();
}
auto get_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); };
}
auto add(std::shared_ptr<user_session_base> session)
{ sessions_.push_back(session); }
};
net::awaitable<void> listener()

View File

@@ -7,31 +7,28 @@
#include <iostream>
#include <queue>
#include <vector>
#include <aedis/aedis.hpp>
#include <aedis/src.hpp>
#include "lib/user_session.hpp"
#include "lib/responses.hpp"
namespace net = aedis::net;
using aedis::redis::command;
using aedis::user_session;
using aedis::user_session_base;
using aedis::resp3::node;
using aedis::resp3::adapt;
using aedis::resp3::experimental::client;
using aedis::resp3::type;
class receiver : public std::enable_shared_from_this<receiver> {
private:
responses resps_;
std::vector<node> resps_;
std::queue<std::weak_ptr<user_session_base>> sessions_;
public:
auto get_adapter()
{ return adapter_wrapper{resps_}; }
void add_user_session(std::shared_ptr<user_session_base> session)
{ sessions_.push(session); }
void on_message(std::error_code ec, command cmd)
{
if (ec) {
@@ -43,21 +40,31 @@ public:
case command::ping:
{
if (auto session = sessions_.front().lock()) {
session->deliver(resps_.simple_string);
session->deliver(resps_.front().data);
} else {
std::cout << "Session expired." << std::endl;
}
sessions_.pop();
resps_.simple_string.clear();
} break;
case command::incr:
{
std::cout << "Echos so far: " << resps_.number << std::endl;
std::cout << "Echos so far: " << resps_.front().data << std::endl;
} break;
default: { assert(false); }
}
resps_.clear();
}
auto get_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); };
}
void add_user_session(std::shared_ptr<user_session_base> session)
{ sessions_.push(session); }
};
net::awaitable<void> listener()

View File

@@ -1,70 +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/.
*/
#pragma once
#include <aedis/aedis.hpp>
namespace net = aedis::net;
using aedis::redis::command;
using aedis::resp3::adapt;
using aedis::resp3::response_traits;
using aedis::resp3::type;
using aedis::resp3::node;
// Groups the responses used in the examples.
struct responses {
int number;
std::string simple_string;
std::vector<node> general;
};
// Adpter as required by experimental::client.
class adapter_wrapper {
private:
response_traits<int>::adapter_type number_adapter_;
response_traits<std::string>::adapter_type str_adapter_;
response_traits<std::vector<node>>::adapter_type general_adapter_;
public:
adapter_wrapper(responses& resps)
: number_adapter_{adapt(resps.number)}
, str_adapter_{adapt(resps.simple_string)}
, general_adapter_{adapt(resps.general)}
{}
void
operator()(
command cmd,
type t,
std::size_t aggregate_size,
std::size_t depth,
char const* data,
std::size_t size,
std::error_code& ec)
{
// Handles only the commands we are interested in the examples and
// ignores the rest.
switch (cmd) {
case command::quit:
case command::ping:
str_adapter_(t, aggregate_size, depth, data, size, ec);
return;
case command::incr:
number_adapter_(t, aggregate_size, depth, data, size, ec);
return;
case command::unknown:
general_adapter_(t, aggregate_size, depth, data, size, ec);
return;
default: {} // Ignore.
}
}
};

View File

@@ -10,16 +10,16 @@
#include <aedis/aedis.hpp>
#include <aedis/src.hpp>
#include "lib/responses.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 {
responses resps;
std::vector<node> resps;
auto on_msg = [&resps](std::error_code ec, command cmd)
{
@@ -28,29 +28,19 @@ int main()
return;
}
switch (cmd) {
case command::ping:
{
std::cout << "ping: " << resps.simple_string << std::endl;
resps.simple_string.clear();
} break;
case command::quit:
{
std::cout << "quit: " << resps.simple_string << std::endl;
resps.simple_string.clear();
} break;
case command::incr:
{
std::cout << "incr: " << resps.number << std::endl;
} break;
default: { assert(false); }
}
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_wrapper{resps});
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");