Boost::ASIO low-level redis client (connector)

Overview

bredis

Boost::ASIO low-level redis client (connector), github gitee

Travis Build status license codecov

Features

  • header only
  • zero-copy (currently only for received replies from Redis)
  • low-level controls (i.e. you can cancel, or do manual DNS-resolving before a connection)
  • unix domain sockets support
  • works on linux (clang, gcc) and windows (msvc)
  • synchronous & asynchronous interface
  • inspired by beast
  • requirements: boost v1.69 minimum

Changelog

0.11

  • [feature, possible breakage] add BOOST_ASIO_NO_DEPRECATED definition for better support boost v1.74 and modernize boost API usage

0.10

  • [bugfix] avoid access to protected destructor (c++17 compatibility)

0.09

  • [bugfix] critical bug in protcol serialization on empty values

0.08

  • relaxed c++ compiler requirements: c++11 can be used instead of c++14

0.07

  • minor parsing speed improvements (upto 10% in synthetic tests)
  • fix compilation issues on boost::asio 1.70
  • make it possible to use DynamicBuffer_v2 (dynamic_string_buffer, dynamic_vector_buffer) from boost::asio 1.70 in addition to streambuf. DynamicBuffer_v1 was actually never supported by bredis
  • [API breakage] boos::asio::use_future cannot be used with bredis and boost::asio prior v1.70 (see issue). If you need use_future then either upgrade boost::asio or use previous bredis version.

0.06

  • the parsing_policy::drop_result was documented and made applicable in client code
  • updated preformance results
  • fixed compliation warnings (-Wall -Wextra -pedantic -Werror)
  • added shortcut header include/bredis.hpp
  • added redis-streams usage example
  • added multi-thread example

0.05

  • fixed level 4 warning in MSVC
  • fixed compilation issues on archlinux
  • improved documentation (numerous typos etc.)

0.04

  • [bugfix] removed unneeded tx_buff.commit() on async_write which corrupted buffer

0.03

  • improved protocol parser (no memory allocations during input stream validity check)
  • more detailed information in protocol_error_t
  • added async incr speed test example
  • [small API breakage] positive_parse_result_t was enriched with parcing policy; now instead of positive_parse_result_t<Iterator> should be written:
using Policy = r::parsing_policy::keep_result;
using result_t = r::parse_result_mapper_t<Iterator, Policy>;
  • [small API breakage] protocol_error_t instead of std::string what member now contains boost::system::error_code code

0.02

  • added windows support
  • added coroutines & futures support
  • generalised (templated) buffer support
  • changed return type: instead of result of parsing just result markers are returned, extraction of result can be done as separate step
  • dropped queing support (queuing policy should be implemented at more higher levels)
  • dropped subscription support (can be implemented at higher levels)
  • dropped internal buffers (can be implemented at higher levels)
  • dropped explicit cancellation (socket reference can be passed to connector, and cancellation can be done on the socket object outside of the connector)

0.01

  • initial version

Performance

Results achieved with examples/speed_test_async_multi.cpp for 1 thread, Intel Core i7-8550U, void-linux, gcc 8.3.0

bredis (commands/s) bredis(*) (commands/s) redox (commands/s)
1.80845e+06 2.503e+06 0.999375+06

These results are not completely fair, because of the usage of different semantics in the APIs; however they are still interesting, as they are using different underlying event libraries (Boost::ASIO vs libev) as well as redis protocol parsing libraries (written from scratch vs hiredis)

(*) bredis with drop_result policy, i.e. replies from redis server are scanned only for formal correctness and never delivered to the caller.

Work with the result

The general idea is that the result of trying to parse a redis reply can be either: not enough data, protocol error (in an extreme case) or some positive parse result. The last one is just markers of the result, which is actually stored in the receive buffer (i.e. outside of markers, and outside of the bredis-connection).

The further work with markers depends on your needs: it is possible to either scan the result for the expected results (e.g. for a PONG reply on a PING command, or for OK/QUEUED replies on MULTI/EXEC commands) or to extract the results (the common redis types: nil, string, error, int or a (recursive) array of them).

When the data in the receive buffer is no longer required, it should be consumed.

Scan example:

#include "bredis/MarkerHelpers.hpp"
...
namespace r = bredis;
...
using Buffer = boost::asio::streambuf;
...
Buffer rx_buff;
auto result_markers = c.read(rx_buff);
/* check for the response */
auto eq_pong = r::marker_helpers::equality<Iterator>("PONG");
/* print true or false */
std::cout << boost::apply_visitor(eq_pong, result_markers.result) << "\n";
/* consume the buffers, after finishing work with the markers */
rx_buff.consume(result_markers.consumed);

For extraction of results it is possible to use either one of the shipped extractors or to write a custom one. Shipped extractors detach (copy / convert) the extraction results from the receive buffer.

#include "bredis/Extract.hpp"
...
auto result_markers = c.read(rx_buff);
auto extract = boost::apply_visitor(r::extractor<Iterator>(), result_markers.result);
/* safe to consume buffers now */
rx_buff.consume(result_markers.consumed);
/* we know what the type is, safe to unpack to string */
auto &reply_str = boost::get<r::extracts::string_t>(extract);
/* print "PONG" */
std::cout << reply_str.str << "\n";

Custom extractors (visitors) might be useful for performance-sensitive cases, e.g. when JSON is re-constructed in-place by using string reply markers without re-allocating the whole JSON-string reply.

The underlying reason for the decision to retrieve the final results in two steps (get markers and then scan/extract results) is that the receive buffer might be scattered (fragmented). In such cases scan and extraction can be performed without gathering receive buffers (i.e. without flattening / linearizing it) if they are separate steps.

In other words, markers have reference semantics (they refer to memory regions in the buffer, but do not own it), while extracted results have value semantics (they take ownership).

Synchronous TCP-connection example

#include "bredis/Connection.hpp"
#include "bredis/MarkerHelpers.hpp"

#include <boost/variant.hpp>
...
namespace r = bredis;
namespace asio = boost::asio;
...
/* define used types */
using socket_t = asio::ip::tcp::socket;
using Buffer = boost::asio::streambuf;
using Iterator = typename r::to_iterator<Buffer>::iterator_t;
...
/* establishing connection to redis is outside of bredis */
asio::ip::tcp::endpoint end_point(
    asio::ip::make_address("127.0.0.1"), port);
socket_t socket(io_service, end_point.protocol());
socket.connect(end_point);

/* wrap socket to bredis connection */
r::Connection<socket_t> c(std::move(socket));

/* synchronously write command */
c.write("ping");

/* buffer is allocated outside of bredis connection*/
Buffer rx_buff;
/* get the result markers */
auto result_markers = c.read(rx_buff);
/* check for the response */
auto eq_pong = r::marker_helpers::equality<Iterator>("PONG");
/* print true */
std::cout << boost::apply_visitor(eq_pong, result_markers.result) << "\n";
/* consume the buffers, after finishing work with the markers */
rx_buff.consume(result_markers.consumed);

In the ping example above the PONG reply string from redis is not (re)allocated, but directly scanned from the rx_buff using a result markers. This can be useful for performance-sensitive cases, e.g. when JSON is re-constructed in-place by using string reply markers without re-allocating the whole JSON-string reply.

In cases where you need to extract the reply (i.e. to detach it from rx_buff), the following can be done:

#include "bredis/Extract.hpp"
...
auto result_markers = c.read(rx_buff);
/* extract the results */
auto extract = boost::apply_visitor(r::extractor<Iterator>(), result_markers.result);
/* safe to consume buffers now */
rx_buff.consume(result_markers.consumed);
/* we know what the type is, safe to unpack to string */
auto &reply_str = boost::get<r::extracts::string_t>(extract);
/* print "PONG" */
std::cout << reply_str.str << "\n";

The examples above throw an exception in case of I/O or protocol errors. Another way to use the API is

boost::system::error_code ec;
c.write("ping", ec);
...
parse_result = c.read(rx_buff, ec);

in case you don't want the throw-exception behaviour.

Asynchronous TCP-connection example

#include "bredis/Connection.hpp"
#include "bredis/MarkerHelpers.hpp"
...
namespace r = bredis;
namespace asio = boost::asio;
namespace sys = boost::system;
...
using socket_t = asio::ip::tcp::socket;
using Buffer = boost::asio::streambuf;
using Iterator = typename r::to_iterator<Buffer>::iterator_t;
using Policy = r::parsing_policy::keep_result;
using result_t = r::parse_result_mapper_t<Iterator, Policy>;

...
/* establishing the connection to redis is outside of bredis */
asio::ip::tcp::endpoint end_point(
    asio::ip::make_address("127.0.0.1"), port);
socket_t socket(io_service, end_point.protocol());
socket.connect(end_point);
...
Buffer tx_buff, rx_buff;
c.async_write(
    tx_buff, r::single_command_t{"llen", "my-queue"}, [&](const sys::error_code &ec, std::size_t bytes_transferred) {
        /* tx_buff must be consumed when it is no longer needed */
        tx_buff.consume(bytes_transferred);
        c.async_read(rx_buff, [&](const sys::error_code &ec, result_t &&r) {
            /* see above how to work with the result */
            auto extract = boost::apply_visitor(r::extractor<Iterator>(), r.result);
            auto &queue_size = boost::get<r::extracts::int_t>(extract);
            std::cout << "queue size: " << queue_size << "\n";
            ...
            /* consume rx_buff when it is no longer needed */
            rx_buff.consume(r.consumed);
        });
    });

In the example above separate receive and transfer buffers are used. In theory you can use only one buffer for both operations, but you must ensure that it will not be used simultaneously for reading and writing, in other words you cannot use the pipelining redis feature.

Asynchronous unix domain socket connections

The same as above, except the underlying socket type must be changed:

using socket_t = asio::local::stream_protocol::socket;

Subscriptions

There is no specific support for subscriptions, but you can easily build your own like

synchronous subscription

r::single_command_t subscribe_cmd{"subscribe", "some-channel1", "some-channel2"};
c.write(subscribe_cmd);
Buffer rx_buff;

/* get the 2 confirmations, as we subscribed to 2 channels */
r::marker_helpers::check_subscription<Iterator> check_subscription{std::move(subscribe_cmd)};
for (auto i = 0; i < 2; ++i){
  auto result_markers = c.read(rx_buff);
  bool confirmed =  boost::apply_visitor(check_subscription, result_markers.result);
  if (!confirmed) {
    // do something!
    ...;
  }
  rx_buff.consume(result_markers.consumed);
}

while(true) {
  auto result_markers = c.read(rx_buff);
  auto extract = boost::apply_visitor(r::extractor<Iterator>(), result_markers.result);
  rx_buff.consume(result_markers.consumed);

  /* process the result  */
  auto& array_reply = boost::get<r::extracts::array_holder_t>(extract);
  auto* type_reply = boost::get<r::extracts::string_t>(&array_reply.elements[0]);
  if (type_reply && type_reply->str == "message") {
      auto& channel = boost::get<r::extracts::string_t>(array_reply.elements[1]);
      auto& payload = boost::get<r::extracts::string_t>(array_reply.elements[2]);
      ...
  }
}

See examples/synch-subscription.cpp for the full example.

asynchronous subscription

These work similarly to the synchronous approach. However you have to provide a read callback initially and again after each successfull read

using Policy = r::parsing_policy::keep_result;
using ParseResult = r::parse_result_mapper_t<Iterator, Policy>;
using read_callback_t = std::function<void(const boost::system::error_code &error_code, ParseResult &&r)>;
using Extractor = r::extractor<Iterator>;
...
/* we can execute the subscription command synchronously, as it is easier */
c.command("subscribe", "channel-1", "channel-2");
...
Buffer rx_buff;
read_callback_t notification_callback = [&](const boost::system::error_code,
                                            ParseResult &&r) {
    auto extract = boost::apply_visitor(Extractor(), r.result);
    rx_buff.consume(r.consumed);
    /* process the result, see above */
    ...
    /* re-trigger new message processing */
    c.async_read(rx_buff, notification_callback);
};

/* initialise listening subscriptions */
c.async_read(rx_buff, notification_callback);

See examples/stream-parse.cpp for the full example.

Transactions

There is no specific support for transactions in bredis, but you can easily build your own for your needs.

First, wrap your commands into a transaction:

r::command_container_t tx_commands = {
    r::single_command_t("MULTI"),
    r::single_command_t("INCR", "foo"),
    r::single_command_t("GET", "bar"),
    r::single_command_t("EXEC"),
};
r::command_wrapper_t cmd(tx_commands);
c.write(cmd);

Then, as above there were 4 redis commands, there we should receive 4 redis replies: OK, QUEUED, QUEUED followed by the array of results of the execution of the commands in the transaction (i.e. results for INCR and GET above)

Buffer rx_buff;
c.async_read(rx_buff, [&](const sys::error_code &ec, result_t&& r){
    auto &replies = boost::get<r::markers::array_holder_t<Iterator>>(r.result);
    /* scan stream for OK, QUEUED, QUEUED */
    ...
    assert(replies.elements.size() == 4);
    auto eq_OK = r::marker_helpers::equality<Iterator>("OK");
    auto eq_QUEUED = r::marker_helpers::equality<Iterator>("QUEUED");
    assert(boost::apply_visitor(eq_OK, replies.elements[0]));
    assert(boost::apply_visitor(eq_QUEUED, replies.elements[1]));
    assert(boost::apply_visitor(eq_QUEUED, replies.elements[2]));

    /* get tx replies */
    auto &tx_replies = boost::get<r::markers::array_holder_t<Iterator>>(replies.elements[3]);
    ...;
    rx_buff.consume(r.consumed);
},
4); /* pay attention here */

Futures & Coroutines

Done in a similiar way as in Boost::ASIO (special thanks to Vinnie Falko for the suggestion)

Futures

#include <boost/asio/use_future.hpp>
...
Buffer rx_buff, tx_buff;
auto f_tx_consumed = c.async_write(tx_buff, "ping", asio::use_future);
auto f_result_markers = c.async_read(rx_buff, asio::use_future);
...
tx_buff.consume(f_tx_consumed.get());
auto result_markers = f_result_markers.get();
/* scan/extract result, and consume rx_buff as usual */

Coroutines

#include <boost/asio/spawn.hpp>
Buffer rx_buff, tx_buff;

boost::asio::spawn(
    io_service, [&](boost::asio::yield_context yield) mutable {
        boost::system::error_code error_code;
        auto consumed = c.async_write(tx_buff, "ping", yield[error_code]);
        tx_buff.consume(consumed);
        ...
        auto parse_result = c.async_read(rx_buff, yield[error_code], 1);
        /* scan/extract result */
        rx_buff.consume(parse_result.consumed);
    });

Steams

There is no specific support for streams (appeared in redis 5.0) in bredis, they are just usual XADD, XRANGE etc. commands and corresponding replies.

...
Buffer rx_buff;
c.write(r::single_command_t{ "XADD", "mystream", "*", "cpu-temp", "23.4", "load", "2.3" });
auto parse_result1 = c.read(rx_buff);
auto extract1 = boost::apply_visitor(Extractor(), parse_result1.result);
auto id1 = boost::get<r::extracts::string_t>(extract1);

c.write(r::single_command_t{ "XADD", "mystream", "*", "cpu-temp", "23.2", "load", "2.1" });
auto parse_result2 = c.read(rx_buff);
auto extract2 = boost::apply_visitor(Extractor(), parse_result2.result);
auto id2 = boost::get<r::extracts::string_t>(extract2);
rx_buff.consume(parse_result2.consumed);

c.write(r::single_command_t{ "XRANGE" , "mystream",  id1.str, id2.str});
auto parse_result3 = c.read(rx_buff);
auto extract3 = boost::apply_visitor(Extractor(), parse_result3.result);
rx_buff.consume(parse_result3.consumed);

auto& outer_arr = boost::get<r::extracts::array_holder_t>(extract3);
auto& inner_arr1 = boost::get<r::extracts::array_holder_t>(outer_arr.elements[0]);
auto& inner_arr2 = boost::get<r::extracts::array_holder_t>(outer_arr.elements[1]);
...

Inspecting network traffic

See t/SocketWithLogging.hpp for an example. The main idea is quite simple: Instead of providing a real socket implementation supplied by Boost::ASIO, provide a wrapper (proxy) which will spy on the traffic before delegating it to/from a Boost::ASIO socket.

Cancellation & other socket operations

There is nothing specific to this in bredis. If you need low-level socket operations, instead of moving socket into bredis connection, you can simply move a reference to it and keep (own) the socket somewhere outside of the bredis connection.

using socket_t = asio::ip::tcp::socket;
using next_layer_t = socket_t &;
...
asio::ip::tcp::endpoint end_point(asio::ip::make_address("127.0.0.1"), port);
socket_t socket(io_service, end_point.protocol());
socket.connect(end_point);
r::Connection<next_layer_t> c(socket);
...
socket.cancel();

Thread-safety

bredis itself is thread-agnostic, however the underlying socket (next_layer_t) and used buffers are usually not thread-safe. To handle that in multi-thead environment the access to those objects should be sequenced via asio::io_context::strand . See the examples/multi-threads-1.cpp.

parsing_policy::drop_result

The performance still can be boosted if it is known beforehand that the response from redis server is not needed at all. For example, the only possible response to PING command is PONG reply, usually there is no sense it validating that PONG reply, as soon as it is known, that redis-server alredy delivered us some reply (in practice it is PONG). Another example is SET command, when redis-server usually replies with OK.

With parsing_policy::drop_result the reply result is just verified with formal compliance to redis protocol, and then it is discarded.

It should be noted, that redis can reply back with error, which aslo correct reply, but the caller side isn't able to see it when parsing_policy::drop_result is applied. So, it should be used with care, when you know what your are doing. You have been warned.

It is safe, however, to mix different parsing policies on the same connection, i.e. write SET command and read it's reply with parsing_policy::drop_result and then write GET command and read it's reply with parsing_policy::keep_result. See the examples/speed_test_async_multi.cpp.

API

There's a convenience header include/bredis.hpp, doing #include "bredis.hpp" will include every header under include/bredis/ .

Iterator template

The underlying iterator type used for the dynamic buffer type (e.g. boost::asio::streambuf)

redis_result_t<Iterator>

Header: include/bredis/Markers.hpp

Namespace: bredis::markers

boost::variant for the basic types in the redis protocol , i.e. the following marker types :

  • nil_t<Iterator>
  • int_t<Iterator>
  • string_t<Iterator> (simple string and bulk strings)
  • error_t<Iterator>
  • array_holder_t<Iterator>

The basic type is string_t<Iterator>, which contains from and to members (Iterator) to where the string is held. String does not contain the special redis-protocol symbols or any other metadata, i.e. it can be used to extract/flatten the whole string.

nil_t<Iterator>, int_t<Iterator>, error_t<Iterator> just have a string member to point to the underlying string in the redis protocol.

array_holder_t is recursive wrapper for the redis_result_t<Iterator>, it contains a elements member of std::array of redis_result_t<Iterator> type.

parse_result_t<Iterator, Policy>

Header: include/bredis/Result.hpp

Namespace: bredis

Represents the results of a parse attempt. It is a boost::variant of the following types:

  • not_enough_data_t
  • protocol_error_t
  • positive_parse_result_t<Iterator, Policy>

not_enough_data_t is a empty struct. It means that buffer just does not contain enough information to completely parse it.

protocol_error_t has a boost::system::error_code code member. It describes the error in the protocol (e.g. when the type in the stream is specified as an integer, but it cannot be converted to an integer). This error should never occur in production code, meaning that no (logical) errors are expected in the redis-server nor in the bredis parser. The error might occur if the buffer is corrupted.

Policy (namespace bredis::parsing_policy) specifies what to do with the result: Either drop it (bredis::parsing_policy::drop_result) or keep it (bredis::parsing_policy::keep_result). The helper parse_result_mapper_t<Iterator, Policy> helps to get the proper positive_parse_result_t<Iterator, Policy> type.

positive_parse_result_t<Iterator, Policy> contains members:

  • markers::redis_result_t<Iterator> result - the result of mark-up buffer; can be used either for scanning for particular results or for extraction of results. Valid only for keep_result policy.
  • size_t consumed - how many bytes of receive buffer must be consumed after using the result field.

marker helpers

Header: include/bredis/MarkerHelpers.hpp

Namespace: bredis::marker_helpers

stringizer<Iterator>

Apply this boost::static_visitor<std::string>s to stringize the result (can be useful for debugging).

equality<Iterator>

Apply this boost::static_visitor<bool> to find a string in the parsed results (the markup can point to integer types, but as it is transferred as a string anyway, it still can be founded as string too).

Constructor: equality<Iterator>(std::string str)

check_subscription<Iterator>

This boost::static_visitor<bool> helper is used to check whether the redis reply confirms one of the requested channels. Hence, the constructor is check_subscription(single_command_t).

Usually, the redis subscription reply is in the form:

[array] {
    [string] "subcribe",
    [string] channel_name,
    [int] reference
}

So it checks that:

  1. The redis reply is a 3-element array
  2. The 1st reply element is a string, and it case-insensitively matches the command, i.e. it is assumed, that command will be subscribe or psubscribe depending on the original command
  3. That the 3rd reply element is a reference, and it is present among the command arguments.

It is possible to reuse the same check_subscription<Iterator> on multiple redis replies to a single subsription command for multiple channels.

Example:

bredis::single_command_t subscribe_cmd{
    "subscribe", "channel-1", "channel-2"
};
...
// write the command, so the subscribe_cmd
// will be no longer be required
...;
bredis::marker_helpers::check_subscription<Iterator>
    check_subscription{std::move(subscribe_cmd)};
...;
// get the 1st reply
auto parse_result = ...;
bool channel_1_ok = boost::apply_visitor(check_subscription, parse_result.result);
...;
// get the 2nd reply
parse_result = ...;
bool channel_2_ok = boost::apply_visitor(check_subscription, parse_result.result);

command_wrapper_t

Header: include/bredis/Command.hpp

Namespace: bredis

boost::variant for the basic commands:

  • single_command_t
  • command_container_t

single_command_t represents a single redis command with all its arguments, e.g.:

// compile-time version
r::single_command_t cmd_ping {"ping"};
r::single_command_t cmd_get {"get", "queu-name"};
...
// or runtime-version
std::vector<std::string> subscription_items { "subscribe", "channel-a", "channel-b"};
r::single_command_t cmd_subscribe {
    subscription_items.cbegin(),
    subscription_items.cend()
};

The arguments must be conversible to boost::string_ref.

command_container_t is a std::vector of single_command_t. It is useful for transactions or bulk message creation.

Connection<NextLayer>

Header: include/bredis/Connection.hpp

Namespace: bredis

A thin wrapper around NextLayer; represents a connection to redis. NextLayer can be either asio::ip::tcp::socket or asio::ip::tcp::socket& or a custom wrapper, which follows the specification of asio::ip::tcp::socket.

The constructor template <typename... Args> Connection(Args &&... args) is used for the construction of NextLayer (stream interface).

Stream interface accessors:

  • NextLayer &next_layer()
  • const NextLayer &next_layer() const

return the underlying stream object.

Synchronous interface

Performs a synchonous write of a redis command:

  • void write(const command_wrapper_t &command)
  • void write(const command_wrapper_t &command, boost::system::error_code &ec)

Performs a synchronous read of a redis result until the buffer is parsed or some error (protocol or I/O) occurs:

  • template <typename DynamicBuffer> positive_parse_result_t<Iterator, Policy = bredis::parsing_policy::keep_result> read(DynamicBuffer &rx_buff)
  • template <typename DynamicBuffer> positive_parse_result_t<Iterator, Policy = bredis::parsing_policy::keep_result> read(DynamicBuffer &rx_buff, boost::system::error_code &ec);

DynamicBuffer must conform to the boost::asio::streambuf interface.

Asynchronous interface

async_write

The WriteCallback template should be a callable object with the signature:

void (const boost::system::error_code&, std::size_t bytes_transferred)

The asynchronous write has the following signature:

void-or-deduced
async_write(DynamicBuffer &tx_buff, const command_wrapper_t &command,
                WriteCallback write_callback)

It writes the redis command (or commands) into a transfer buffer, sends them to the next_layer stream and invokes write_callback after completion.

tx_buff must consume bytes_transferred upon write_callback invocation.

The client must guarantee that async_write is not invoked until the previous invocation is finished.

async_read

ReadCallback template should be a callable object with the signature:

void(boost::system::error_code, r::positive_parse_result_t<Iterator, Policy = bredis::parsing_policy::keep_result>&& result)

The asynchronous read has the following signature:

void-or-deduced
async_read(DynamicBuffer &rx_buff, ReadCallback read_callback,
               std::size_t replies_count = 1, Policy = bredis::parsing_policy::keep_result{});

It reads replies_count replies from the next_layer stream, which will be stored in rx_buff, or until an error (I/O or protocol) is encountered; then read_callback will be invoked.

If replies_count is greater than 1, the result type will always be bredis::array_wrapper_t; if the replies_count is 1 then the result type depends on redis answer type.

On read_callback invocation with a successful parse result it is expected, that rx_buff will consume the amount of bytes specified in the result.

The client must guarantee that async_read is not invoked until the previous invocation is finished. If you invoke async_read from read_callback don't forget to consume rx_buff first, otherwise it leads to subtle bugs.

License

MIT

Contributors

See also

Comments
  • Clarification regarding multiple messages

    Clarification regarding multiple messages

    Hi,

    There is some issue that I can't figure out from the documentation: when multiple messages are streamed in and the buffer contains more then one message, what's the correct way of using async_read to ensure that all messages are handled and none is lost. Is it handled within bredis or should I adapt on my side?

    Thanks, Silviu

    opened by silviuzil 5
  • using as cpp-bredis

    using as cpp-bredis

    Hello, we are going to use cpp-bredis in our redis graph solution we have the following code:

    
    executor_context_ptr_->conn.async_write(
    						executor_context_ptr_->tx_buff,
    						cmd,
    						asio::bind_executor(executor_context_ptr_->strand, [this, &result_promise](const sys::error_code& ec, std::size_t bytes_transferred)
    							{
    							if (!ec) {
    								auto self = this;
    								self->executor_context_ptr_->tx_buff.consume(bytes_transferred);
    								executor_context_ptr_->conn.async_read(
    									executor_context_ptr_->rx_buff,
    									asio::bind_executor(executor_context_ptr_->strand, [this, &result_promise](const sys::error_code& ec, result_t&& r) {
    										if (!ec) {
    											auto self = this;
    											auto result = r.result;
    											auto& replies = boost::get<r::markers::array_holder_t<Iterator>>(r.result);
    											// here how to parse the reply?
                                                
    											self->executor_context_ptr_->rx_buff.consume(r.consumed);
    											// parse and set result.
    										}
    										else
    										{
    											redisgraph::result_view read_error_view;
    											result_promise.set_value(read_error_view);
    										}
    										}));
                                }
    

    We woud like to know how to parse and debug the list of arrays. Best Regards, Giorgio.

    opened by giorgiozoppi 5
  • Wrapping sockets with Bredis::Connection

    Wrapping sockets with Bredis::Connection

    Hi, I was initially using boost version 1.73. Wrapping the underlying type boost::asio::ip::tcp::socket as a bredis connection works. However, doing the same for the unix sockets with type boost::asio::local::stream_protocol::socket does not.

    This is the code I am running at the moment, which fails during building.

    boost::asio::io_context ioserv;
    using socket_t = boost::asio::local::stream_protocol::socket;
    using next_layer_t = socket_t;
    
    boost::asio::local::stream_protocol::endpoint end_point("/shared/redis.sock");
    socket_t sock(ioserv); 
    sock.connect(end_point);
    bredis::Connection<next_layer_t> bredis_con(std::move(socket));
    

    And here is the error I get.

    In file included from /usr/local/include/boost/asio/executor.hpp:338,
                     from /usr/local/include/boost/asio/basic_socket.hpp:27,
                     from /usr/local/include/boost/asio/basic_datagram_socket.hpp:20,
                     from /usr/local/include/boost/asio.hpp:24,
                     from /atom/languages/cpp/third-party/cpp-bredis/include/bredis/Connection.hpp:20,
                     from ./clients/bredis_client.cc:7:
    /usr/local/include/boost/asio/impl/executor.hpp: In instantiation of 'boost::asio::execution_context& boost::asio::executor::impl< <template-parameter-1-1>, <template-parameter-1-2> >::context() [with Executor = int (*)(int, int, int); Allocator = std::allocator<void>]':
    /usr/local/include/boost/asio/impl/executor.hpp:177:22:   required from here
    /usr/local/include/boost/asio/impl/executor.hpp:179:22: error: request for member 'context' in '((boost::asio::executor::impl<int (*)(int, int, int), std::allocator<void> >*)this)->boost::asio::executor::impl<int (*)(int, int, int), std::allocator<void> >::executor_', which is of non-class type 'int (*)(int, int, int)'
         return executor_.context();
                ~~~~~~~~~~^~~~~~~
    /usr/local/include/boost/asio/impl/executor.hpp: In instantiation of 'void boost::asio::executor::impl< <template-parameter-1-1>, <template-parameter-1-2> >::on_work_started() [with Executor = int (*)(int, int, int); Allocator = std::allocator<void>]':
    /usr/local/include/boost/asio/impl/executor.hpp:167:8:   required from here
    /usr/local/include/boost/asio/impl/executor.hpp:169:15: error: request for member 'on_work_started' in '((boost::asio::executor::impl<int (*)(int, int, int), std::allocator<void> >*)this)->boost::asio::executor::impl<int (*)(int, int, int), std::allocator<void> >::executor_', which is of non-class type 'int (*)(int, int, int)'
         executor_.on_work_started();
         ~~~~~~~~~~^~~~~~~~~~~~~~~
    /usr/local/include/boost/asio/impl/executor.hpp: In instantiation of 'void boost::asio::executor::impl< <template-parameter-1-1>, <template-parameter-1-2> >::on_work_finished() [with Executor = int (*)(int, int, int); Allocator = std::allocator<void>]':
    /usr/local/include/boost/asio/impl/executor.hpp:172:8:   required from here
    /usr/local/include/boost/asio/impl/executor.hpp:174:15: error: request for member 'on_work_finished' in '((boost::asio::executor::impl<int (*)(int, int, int), std::allocator<void> >*)this)->boost::asio::executor::impl<int (*)(int, int, int), std::allocator<void> >::executor_', which is of non-class type 'int (*)(int, int, int)'
         executor_.on_work_finished();
         ~~~~~~~~~~^~~~~~~~~~~~~~~~
    /usr/local/include/boost/asio/impl/executor.hpp: In instantiation of 'void boost::asio::executor::impl< <template-parameter-1-1>, <template-parameter-1-2> >::dispatch(boost::asio::executor::function&&) [with Executor = int (*)(int, int, int); Allocator = std::allocator<void>]':
    /usr/local/include/boost/asio/impl/executor.hpp:182:8:   required from here
    /usr/local/include/boost/asio/impl/executor.hpp:184:15: error: request for member 'dispatch' in '((boost::asio::executor::impl<int (*)(int, int, int), std::allocator<void> >*)this)->boost::asio::executor::impl<int (*)(int, int, int), std::allocator<void> >::executor_', which is of non-class type 'int (*)(int, int, int)'
         executor_.dispatch(BOOST_ASIO_MOVE_CAST(function)(f), allocator_);
         ~~~~~~~~~~^~~~~~~~
    /usr/local/include/boost/asio/impl/executor.hpp: In instantiation of 'void boost::asio::executor::impl< <template-parameter-1-1>, <template-parameter-1-2> >::post(boost::asio::executor::function&&) [with Executor = int (*)(int, int, int); Allocator = std::allocator<void>]':
    /usr/local/include/boost/asio/impl/executor.hpp:187:8:   required from here
    /usr/local/include/boost/asio/impl/executor.hpp:189:15: error: request for member 'post' in '((boost::asio::executor::impl<int (*)(int, int, int), std::allocator<void> >*)this)->boost::asio::executor::impl<int (*)(int, int, int), std::allocator<void> >::executor_', which is of non-class type 'int (*)(int, int, int)'
         executor_.post(BOOST_ASIO_MOVE_CAST(function)(f), allocator_);
         ~~~~~~~~~~^~~~
    /usr/local/include/boost/asio/impl/executor.hpp: In instantiation of 'void boost::asio::executor::impl< <template-parameter-1-1>, <template-parameter-1-2> >::defer(boost::asio::executor::function&&) [with Executor = int (*)(int, int, int); Allocator = std::allocator<void>]':
    /usr/local/include/boost/asio/impl/executor.hpp:192:8:   required from here
    /usr/local/include/boost/asio/impl/executor.hpp:194:15: error: request for member 'defer' in '((boost::asio::executor::impl<int (*)(int, int, int), std::allocator<void> >*)this)->boost::asio::executor::impl<int (*)(int, int, int), std::allocator<void> >::executor_', which is of non-class type 'int (*)(int, int, int)'
         executor_.defer(BOOST_ASIO_MOVE_CAST(function)(f), allocator_);
         ~~~~~~~~~~^~~~~
    

    I tried downgrading to boost 1.69, but got the following error:

    /atom/languages/cpp/third-party/cpp-bredis/include/bredis/Connection.hpp: In instantiation of 'bredis::Connection<NextLayer>::Connection(Args&& ...) [with Args = {int (&)(int, int, int)}; NextLayer = boost::asio::basic_stream_socket<boost::asio::local::stream_protocol>]':
    ./clients/bredis_client.cc:61:66:   required from here
    /atom/languages/cpp/third-party/cpp-bredis/include/bredis/Connection.hpp:41:46: error: no matching function for call to 'boost::asio::basic_stream_socket<boost::asio::local::stream_protocol>::basic_stream_socket(int (&)(int, int, int))'
             : stream_(std::forward<Args>(args)...) {}
                                                  ^
    In file included from /usr/local/include/boost/asio/basic_socket_streambuf.hpp:25,
                     from /usr/local/include/boost/asio/basic_socket_iostream.hpp:24,
                     from /usr/local/include/boost/asio.hpp:31,
                     from /atom/languages/cpp/third-party/cpp-bredis/include/bredis/Connection.hpp:20,
                     from ./clients/bredis_client.cc:7:
    /usr/local/include/boost/asio/basic_stream_socket.hpp:185:3: note: candidate: 'template<class Protocol1> boost::asio::basic_stream_socket<Protocol>::basic_stream_socket(boost::asio::basic_stream_socket<Protocol1>&&, typename std::enable_if<std::is_convertible<Protocol1, Protocol>::value>::type*)'
       basic_stream_socket(
       ^~~~~~~~~~~~~~~~~~~
    /usr/local/include/boost/asio/basic_stream_socket.hpp:185:3: note:   template argument deduction/substitution failed:
    In file included from ./clients/bredis_client.cc:7:
    /atom/languages/cpp/third-party/cpp-bredis/include/bredis/Connection.hpp:41:46: note:   mismatched types 'boost::asio::basic_stream_socket<Protocol>' and 'int(int, int, int)'
             : stream_(std::forward<Args>(args)...) {}
                                                  ^
    In file included from /usr/local/include/boost/asio/basic_socket_streambuf.hpp:25,
                     from /usr/local/include/boost/asio/basic_socket_iostream.hpp:24,
                     from /usr/local/include/boost/asio.hpp:31,
                     from /atom/languages/cpp/third-party/cpp-bredis/include/bredis/Connection.hpp:20,
                     from ./clients/bredis_client.cc:7:
    /usr/local/include/boost/asio/basic_stream_socket.hpp:152:3: note: candidate: 'boost::asio::basic_stream_socket<Protocol>::basic_stream_socket(boost::asio::basic_stream_socket<Protocol>&&) [with Protocol = boost::asio::local::stream_protocol]'
       basic_stream_socket(basic_stream_socket&& other)
       ^~~~~~~~~~~~~~~~~~~
    /usr/local/include/boost/asio/basic_stream_socket.hpp:152:3: note:   no known conversion for argument 1 from 'int(int, int, int)' to 'boost::asio::basic_stream_socket<boost::asio::local::stream_protocol>&&'
    /usr/local/include/boost/asio/basic_stream_socket.hpp:134:3: note: candidate: 'boost::asio::basic_stream_socket<Protocol>::basic_stream_socket(boost::asio::io_context&, const protocol_type&, const native_handle_type&) [with Protocol = boost::asio::local::stream_protocol; boost::asio::basic_stream_socket<Protocol>::protocol_type = boost::asio::local::stream_protocol; boost::asio::basic_stream_socket<Protocol>::native_handle_type = int]'
       basic_stream_socket(boost::asio::io_context& io_context,
       ^~~~~~~~~~~~~~~~~~~
    /usr/local/include/boost/asio/basic_stream_socket.hpp:134:3: note:   candidate expects 3 arguments, 1 provided
    /usr/local/include/boost/asio/basic_stream_socket.hpp:114:3: note: candidate: 'boost::asio::basic_stream_socket<Protocol>::basic_stream_socket(boost::asio::io_context&, const endpoint_type&) [with Protocol = boost::asio::local::stream_protocol; boost::asio::basic_stream_socket<Protocol>::endpoint_type = boost::asio::local::basic_endpoint<boost::asio::local::stream_protocol>]'
       basic_stream_socket(boost::asio::io_context& io_context,
       ^~~~~~~~~~~~~~~~~~~
    /usr/local/include/boost/asio/basic_stream_socket.hpp:114:3: note:   candidate expects 2 arguments, 1 provided
    /usr/local/include/boost/asio/basic_stream_socket.hpp:93:3: note: candidate: 'boost::asio::basic_stream_socket<Protocol>::basic_stream_socket(boost::asio::io_context&, const protocol_type&) [with Protocol = boost::asio::local::stream_protocol; boost::asio::basic_stream_socket<Protocol>::protocol_type = boost::asio::local::stream_protocol]'
       basic_stream_socket(boost::asio::io_context& io_context,
       ^~~~~~~~~~~~~~~~~~~
    /usr/local/include/boost/asio/basic_stream_socket.hpp:93:3: note:   candidate expects 2 arguments, 1 provided
    /usr/local/include/boost/asio/basic_stream_socket.hpp:76:12: note: candidate: 'boost::asio::basic_stream_socket<Protocol>::basic_stream_socket(boost::asio::io_context&) [with Protocol = boost::asio::local::stream_protocol]'
       explicit basic_stream_socket(boost::asio::io_context& io_context)
                ^~~~~~~~~~~~~~~~~~~
    /usr/local/include/boost/asio/basic_stream_socket.hpp:76:12: note:   no known conversion for argument 1 from 'int(int, int, int)' to 'boost::asio::io_context&'
    make: *** [Makefile:102: clients/build/bredis_client] Error 1
    

    I'm running inside of a Docker container with Debian GNU/Linux 10 (buster).

    opened by BarbBoyaji 4
  • Consider (minimal) C++11 support

    Consider (minimal) C++11 support

    At the moment we (@Icinga) have to use this workaround to compile this lib:

    #if __cplusplus < 201402L
    
    #include <boost/type_traits/decay.hpp>
    #include <boost/core/enable_if.hpp>
    
    namespace std
    {
    using boost::decay_t;
    using boost::enable_if_t;
    }
    
    #endif /* < C++14 */
    

    It would be nice if we wouldn't have to.

    opened by Al2Klimov 4
  • subscriptions to multiple channels issue

    subscriptions to multiple channels issue

    Hi,

    Thanks for a great library, very useful.

    I have an issue with multiple subscriptions. One way of subscribing is (following the documentation): bredis::single_command_t subscribe_cmd{ "subscribe", "channel-1", "channel-2" }; Then one can validate the subscription using: bredis::marker_helpers::check_subscription<Iterator> check_subscription{std::move(subscribe_cmd)}; ...; // get the 1st reply auto parse_result = ...; bool channel_1_ok = boost::apply_visitor(check_subscription, parse_result.result); ...; // get the 2nd reply parse_result = ...; bool channel_2_ok = boost::apply_visitor(check_subscription, parse_result.result);

    However, if subscriptions are not simultaneous, that is we subscribe to channel-1 and later subscribe to channel-2, the validation fails on channel-2: // get the 2nd reply parse_result = ...; bool channel_2_ok = boost::apply_visitor(check_subscription, parse_result.result);

    The reason is that redis response has only 3 fields and the value in the 3rd field is 2, which doesn't comply with the validation tests in check_subscription.

    Any work around?

    Thanks, Silviu

    opened by silviuzil 2
  • Boost 1.74 and BOOST_ASIO_NO_DEPRECATED

    Boost 1.74 and BOOST_ASIO_NO_DEPRECATED

    asio_handler_invoke has been deprecated for a while, but in 1.74, they added compile-time detection for it if BOOST_ASIO_NO_DEPRECATED is defined.

    This makes it impossible to use bredis with that flag, unfortunately, which is a must-have for certain Windows builds (it prevents a mis-detection of a specific constructor overload that leads to a crash).

    opened by amensel 2
  • clang >=8 w/C++17 detects trying to access protected dtor

    clang >=8 w/C++17 detects trying to access protected dtor

    We encountered an issue when compiling with clang-tidy >= 8.0 and C++17, constructing the boost::static_visitor, in this case count_unwrapper_t, with the initializer-list, the compiler complains about trying to access a protected destructor:

    /usr/local/include/bredis/impl/protocol.ipp:292:52: error: temporary of type 'boost::static_visitor<count_variant_t<buffers_iterator<const_buffer
    s_1, char>, drop_result> >' (aka 'static_visitor<variant<bredis::details::count_value_t, variant<bredis::not_enough_data_t, positive_parse_result
    _t<boost::asio::buffers_iterator<boost::asio::const_buffers_1, char>, bredis::parsing_policy::drop_result>, bredis::protocol_error_t> > >') has p
    rotected destructor [clang-diagnostic-error]
                boost::apply_visitor(count_unwrapper_t{}, count_result);
                                                       ^
    /usr/local/include/bredis/impl/protocol.ipp:386:24: note: in instantiation of member function 'bredis::details::bulk_string_parser_t<boost::asio:
    :buffers_iterator<boost::asio::const_buffers_1, char>, bredis::parsing_policy::drop_result>::apply' requested here
            return Parser::apply(next_from, to_, 1);
                           ^
    ...
    /usr/local/include/boost/variant/static_visitor.hpp:53:5: note: declared protected here
        ~static_visitor() = default;
    

    gcc 10.1, boost 1.69, clang 10.0, bredis 395830c1b68d47ff54b93724ac22e2255765015d

    The following explanation describes how {} vs () differs in how each of them access ctor's and in our case dtor's of the parent classes: https://stackoverflow.com/questions/56367480/should-this-code-fail-to-compile-in-c17/56367566#56367566

    opened by sbchisholm 2
  • Fix CMake warning while using CMake > 2.6.0

    Fix CMake warning while using CMake > 2.6.0

    Include cmake_minimum_required check in examples/CMakeLists.txt to fix CMP0000 policy.

    CMake Warning (dev) in CMakeLists.txt:
      No cmake_minimum_required command is present.  A line of code such as
    
        cmake_minimum_required(VERSION 3.14)
    
      should be added at the top of the file.  The version specified may be lower
      if you wish to support older CMake versions for this project.  For more
      information run "cmake --help-policy CMP0000".
    This warning is for project developers.  Use -Wno-dev to suppress it.
    
    opened by dhruvkakadiya 2
  • Fix typos and grammar in readme

    Fix typos and grammar in readme

    I'm currently looking at cpp-bredis and noticed various typos, missing articles and other minor issues w.r.t to the English grammar in the readme that made it a bit hard to read.

    I tried to correct as many of them as I could find without changing the meaning or touching too much. I didn't try to do many changes to punctuation. English isn't my first language so I'm never 100% sure when to place them though I'm pretty sure one could remove a lot of the ","s.

    opened by hacst 2
  • Different arrangement of directories

    Different arrangement of directories

    To prevent conflicts with includes, it might be better if the library header files were located in a directory called include/bredis instead of bredis/.

    This way, anyone who uses the library can simply clone the repository, and add -I{root}include/bredis without worrying that they can pick up other includes (for example, trying to include t/catch.hpp).

    opened by vinniefalco 2
  • cmake boost on windows

    cmake boost on windows

    Good day I started learning cmake quite recently And there there problems with conne sos cting boost to cmake,how do I specify the desired directory? I work on windows

    opened by pasjkeee 1
  • performance improvements ideas

    performance improvements ideas

    1. Introduce prepared_command, which already holds serialized form, and yous it as item in ConstBufferSequence

    in other words, the idea is to avoid output (tx) buffer linearization. Currently this technique is used with rx buffer on (and it used completely on the experimental branch).

    Avoiding output linearization will really make bredis zero cost, as the user's payload will not be copied by bredis, and will be passed into OS-kernel directly.

    question 
    opened by basiliscos 1
  • Unclear how to use drop_result.

    Unclear how to use drop_result.

    Documentation states, that it's possible to use drop_result as part of the parsing policy: https://github.com/basiliscos/cpp-bredis#parse_result_titerator-policy

    However, it's pretty unclear how to use it with the Connection object.

    I read through the source code and Connection seems to be have hard-coded keep_result as policy type. How would I use drop_result with Connection object?

    Do I understand the intention of drop_result properly, that it'd cause the parser to verify that the response isn't an error, but the payload itself is dropped.

    opened by ovanes 13
  • Mac build failure

    Mac build failure

    After I build with Cmake and invoke the make command, these errors occur. What should I do to solve this?

    /Users/Stephen/documents/cpp-bredis/include/bredis/impl/connection.ipp:61:40: error: no matching constructor for initialization of 'asio::async_result<real_handler_t>' (aka 'async_result<coro_handler<boost::asio::executor_binder<void ()(), boost::asio::executor>, bredis::positive_parse_result_t<boost::asio::buffers_iterator<boost::asio::const_buffers_1, char>, bredis::parsing_policy::keep_result> > >') asio::async_result<real_handler_t> async_result(real_handler); ^ ~~~~~~~~~~~~ /Users/Stephen/documents/cpp-bredis/t/21-coroutine.cpp:53:35: note: in instantiation of function template specialization 'bredis::Connection<boost::asio::basic_stream_socketboost::asio::ip::tcp >::async_read<boost::asio::basic_streambuf<std::__1::allocator >, boost::asio::basic_yield_context<boost::asio::executor_binder<void ()(), boost::asio::executor> > >' requested here auto parse_result = c.async_read(rx_buff, yield[error_code], 1); ^ /usr/local/include/boost/asio/async_result.hpp:50:7: note: candidate constructor (the implicit copy constructor) not viable: no known conversion from 'real_handler_t' (aka 'coro_handler<boost::asio::executor_binder<void ()(), boost::asio::executor>, bredis::positive_parse_result_t<boost::asio::buffers_iterator<boost::asio::const_buffers_1, char>, bredis::parsing_policy::keep_result> >') to 'const boost::asio::async_result<boost::asio::detail::coro_handler<boost::asio::executor_binder<void ()(), boost::asio::executor>, bredis::positive_parse_result_t<boost::asio::buffers_iterator<boost::asio::const_buffers_1, char>, bredis::parsing_policy::keep_result> >, void>' for 1st argument class async_result ^ /usr/local/include/boost/asio/impl/spawn.hpp:316:12: note: candidate constructor not viable: no known conversion from 'real_handler_t' (aka 'coro_handler<boost::asio::executor_binder<void ()(), boost::asio::executor>, bredis::positive_parse_result_t<boost::asio::buffers_iterator<boost::asio::const_buffers_1, char>, bredis::parsing_policy::keep_result> >') to 'typename detail::coro_async_result<executor_binder<void ()(), executor>, void>::completion_handler_type &' (aka 'coro_handler<boost::asio::executor_binder<void (*)(), boost::asio::executor>, void> &') for 1st argument explicit async_result( ^ 3 warnings and 2 errors generated. make[2]: *** [CMakeFiles/t-21-coroutine.dir/t/21-coroutine.cpp.o] Error 1 make[1]: *** [CMakeFiles/t-21-coroutine.dir/all] Error 2 make: *** [all] Error 2

    opened by stephendliang 4
  • Build instructions for linux

    Build instructions for linux

    I'm trying to build the examples, are there any build instructions for linux?

    I tried ccmake . make

    but I get

    $ make
    CMake Warning (dev) in CMakeLists.txt:
      No cmake_minimum_required command is present.  A line of code such as
    
        cmake_minimum_required(VERSION 3.5)
    
      should be added at the top of the file.  The version specified may be lower
      if you wish to support older CMake versions for this project.  For more
      information run "cmake --help-policy CMP0000".
    This warning is for project developers.  Use -Wno-dev to suppress it.
    
    -- Configuring done
    -- Generating done
    -- Build files have been written to: /home/derek/Downloads/cpp-bredis/examples
    Scanning dependencies of target stream-parse
    [ 16%] Building CXX object CMakeFiles/stream-parse.dir/stream-parse.o
    /home/derek/Downloads/cpp-bredis/examples/stream-parse.cpp:30:33: fatal error: bredis/Connection.hpp: No such file or directory
    compilation terminated.
    CMakeFiles/stream-parse.dir/build.make:62: recipe for target 'CMakeFiles/stream-parse.dir/stream-parse.o' failed
    make[2]: *** [CMakeFiles/stream-parse.dir/stream-parse.o] Error 1
    CMakeFiles/Makefile2:67: recipe for target 'CMakeFiles/stream-parse.dir/all' failed
    make[1]: *** [CMakeFiles/stream-parse.dir/all] Error 2
    Makefile:85: recipe for target 'all' failed
    make: *** [all] Error 2
    

    I can find the include files, but I don't know how to config the make process to see them...

    opened by dcolley 1
Releases(v0.12)
  • v0.12(Dec 22, 2022)

    • [feature, breakging] modernize API to use completion token instead of using completion handler, which makes it possible to use bredis with coroutines. Thanks to Usevalad Sauta. Successfully tested with boost v1.77, lower versions might not work
    Source code(tar.gz)
    Source code(zip)
  • v0.11(Sep 4, 2020)

  • v0.10(May 13, 2020)

  • v0.09(Dec 7, 2019)

  • v0.08(Aug 10, 2019)

  • v0.07(Apr 27, 2019)

    • minor parsing speed improvements (upto 10% in synthetic tests)
    • fix compilation issues on boost::asio 1.70
    • make it possible to use DynamicBuffer_v2 (dynamic_string_buffer, dynamic_vector_buffer) from boost::asio 1.70 in addition to streambuf. DynamicBuffer_v1 was actually never supported by bredis
    • [API breakage] boos::asio::use_future cannot be used with bredis and boost::asio prior v1.70 (see issue). If you need use_future then either upgrade boost::asio or use previous bredis version.
    Source code(tar.gz)
    Source code(zip)
  • v0.06(Apr 14, 2019)

    • the parsing_policy::drop_result was documented and made applicable in client code
    • updated preformance results
    • fixed compliation warnings (-Wall -Wextra -pedantic -Werror)
    • added shortcut header include/bredis.hpp
    • added redis-streams usage example
    • added multi-thread example
    Source code(tar.gz)
    Source code(zip)
  • v0.05(Jan 28, 2019)

  • v0.04(Jan 25, 2018)

  • v0.03(Jul 30, 2017)

    • improved protocol parser (no memory allocations during input stream validity check)
    • more detailed information in protocol_error_t
    • added async incr speed test example
    • [small API breakage] positive_parse_result_t was enriched with parcing policy; now instead of positive_parse_result_t<Iterator> should be written:
    using Policy = r::parsing_policy::keep_result;
    using result_t = r::parse_result_mapper_t<Iterator, Policy>;
    
    • [small API breakage] protocol_error_t instead of std::string what member now contains boost::system::error_code code
    Source code(tar.gz)
    Source code(zip)
  • v0.02(Jun 24, 2017)

    • added windows support
    • added coroutines & futures support
    • generalised (templated) buffer support
    • changed return type: instead of result of parsing just result markers are returned, extraction of result can be done as separate step
    • dropped queing support (queuing policy should be implemented at more higher levels)
    • dropped subscription support (can be implemented at higher levels)
    • dropped internal buffers (can be implemented at higher levels)
    • dropped explicit cancellation (socket reference can be passed to connector, and cancellation can be done on the socket object outside of the connector)
    Source code(tar.gz)
    Source code(zip)
Owner
Ivan Baidakou
Ash nazg durbatulûk, ash nazg gimbatul, Ash nazg thrakatulûk agh burzum-ishi krimpatul https://gitee.com/basiliscos
Ivan Baidakou
Ole Christian Eidheim 741 Dec 27, 2022
A very simple, fast, multithreaded, platform independent HTTP and HTTPS server and client library implemented using C++11 and Boost.Asio.

A very simple, fast, multithreaded, platform independent HTTP and HTTPS server and client library implemented using C++11 and Boost.Asio. Created to be an easy way to make REST resources available from C++ applications.

Ole Christian Eidheim 2.4k Dec 23, 2022
HTTP and WebSocket built on Boost.Asio in C++11

HTTP and WebSocket built on Boost.Asio in C++11 Branch Linux/OSX Windows Coverage Documentation Matrix master develop Contents Introduction Appearance

Boost.org 3.6k Jan 4, 2023
Asynchronous gRPC with Boost.Asio executors

asio-grpc This library provides an implementation of boost::asio::execution_context that dispatches work to a grpc::CompletionQueue. Making it possibl

Dennis 180 Dec 31, 2022
Packio - An asynchronous msgpack-RPC and JSON-RPC library built on top of Boost.Asio.

Header-only | JSON-RPC | msgpack-RPC | asio | coroutines This library requires C++17 and is designed as an extension to boost.asio. It will let you bu

Quentin Chateau 58 Dec 26, 2022
Boost.GIL - Generic Image Library | Requires C++11 since Boost 1.68

Documentation GitHub Actions AppVeyor Azure Pipelines CircleCI Regression Codecov Boost.GIL Introduction Documentation Requirements Branches Community

Boost.org 154 Nov 24, 2022
Build a redis client by cpp:)

Redix A light redis client implement by c++. Develop Environment cmake:3.16.3 g++:9.3.0 os:ubuntu 20.04.01 reids:5.0.7 Welcome to see my talk. Benchma

null 3 Apr 15, 2022
Level up your Beat Saber experience on Quest! AnyTweaks provides various tweaks to help boost your experience on Quest, such as Bloom, FPS Counter and more.

Need help/support? Ask in one of BSMG's support channels for Quest, or join my Discord server! AnyTweaks Level up your Beat Saber experience on Quest!

kaitlyn~ 19 Nov 20, 2022
Perf-ninja - This is an online course where you can learn and master the skill of low-level performance analysis and tuning.

Performance Ninja Class This is an online course where you can learn to find and fix low-level performance issues, for example CPU cache misses and br

Denis Bakhvalov 1.2k Dec 29, 2022
Ultra fast and low latency asynchronous socket server & client C++ library with support TCP, SSL, UDP, HTTP, HTTPS, WebSocket protocols and 10K connections problem solution

CppServer Ultra fast and low latency asynchronous socket server & client C++ library with support TCP, SSL, UDP, HTTP, HTTPS, WebSocket protocols and

Ivan Shynkarenka 958 Jan 3, 2023
C++ peer to peer library, built on the top of boost

Breep What is Breep? Breep is a c++ bridged peer to peer library. What does that mean? It means that even though the network is constructed as a peer

Lucas Lazare 110 Nov 24, 2022
requests-like networking library using boost for C++

cq == C++ Requests cq == C++ Requests is a "Python Requests"-like C++ header-only library for sending HTTP requests. The library is inspired a lot by

null 11 Dec 15, 2021
Lightweight, header-only, Boost-based socket pool library

Stream-client This is a lightweight, header-only, Boost-based library providing client-side network primitives to easily organize and implement data t

Tinkoff.ru 12 Aug 5, 2022
Boost headers

About This repository contains a set of header files from Boost. Can be useful when using header only libraries. How to use You can easily include the

null 2 Oct 16, 2021
Boost.org signals2 module

Signals2, part of collection of the Boost C++ Libraries, is an implementation of a managed signals and slots system. License Distributed under the Boo

Boost.org 52 Dec 1, 2022
Boost.org property_tree module

Maintainer This library is currently maintained by Richard Hodges with generous support from the C++ Alliance. Build Status Branch Status develop mast

Boost.org 36 Dec 6, 2022
Super-project for modularized Boost

Boost C++ Libraries The Boost project provides free peer-reviewed portable C++ source libraries. We emphasize libraries that work well with the C++ St

Boost.org 5.4k Jan 8, 2023
Triton Python and C++ client libraries and example, and client examples for go, java and scala.

Triton Client Libraries and Examples To simplify communication with Triton, the Triton project provides several client libraries and examples of how t

Triton Inference Server 228 Jan 5, 2023
VEngine-Client - vEngine: Official Client Module

━ S Y N O P S I S ━ Maintainer(s): Aviril, Tron vEngine is Next-Gen Sandbox-Engine being crafted in C++. In contrast to UE/Unity/ReverseEngineered-Mod

ᴠ : ꜱᴛᴜᴅɪᴏ 15 Sep 7, 2022