Skip to content

Commit afa5cb9

Browse files
authored
Merge pull request #24 from basiliscos/issue-22
Let drop_result policy be useable
2 parents 90402c6 + e69578c commit afa5cb9

33 files changed

+427
-185
lines changed

CMakeLists.txt

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ set(CMAKE_CXX_STANDARD_REQUIRED ON)
77
include (CTest)
88
enable_testing()
99

10+
11+
1012
add_definitions(-DBOOST_ERROR_CODE_HEADER_ONLY)
1113
add_definitions(-DBOOST_COROUTINES_NO_DEPRECATION_WARNING)
1214
#add_definitions(-DBREDIS_DEBUG)
@@ -70,7 +72,9 @@ if(WIN32)
7072
set(LINK_DEPENDENCIES ${Boost_LIBRARIES} catch_lib)
7173
add_definitions(-DBOOST_ALL_DYN_LINK -DBOOST_THREAD_USE_LIB -D_WIN32_WINNT=0x0501)
7274
else()
73-
set(LINK_DEPENDENCIES pthread ${Boost_LIBRARIES} catch_lib)
75+
add_definitions(-Wall -Wextra -pedantic -Werror)
76+
#add_definitions(-fsanitize=undefined -fsanitize=address -fno-omit-frame-pointer)
77+
set(LINK_DEPENDENCIES pthread ${Boost_LIBRARIES} catch_lib) # asan ubsan
7478
endif()
7579

7680
add_subdirectory(examples)
@@ -141,6 +145,10 @@ add_executable(t-21-coroutine t/21-coroutine.cpp)
141145
target_link_libraries(t-21-coroutine ${LINK_DEPENDENCIES})
142146
add_test("t-21-coroutine" t-21-coroutine)
143147

148+
add_executable(t-22-ping_drop-policy t/22-ping_drop-policy.cpp)
149+
target_link_libraries(t-22-ping_drop-policy ${LINK_DEPENDENCIES})
150+
add_test("t-22-ping_drop-policy" t-22-ping_drop-policy)
151+
144152
add_executable(t-23-stream t/23-stream.cpp)
145153
target_link_libraries(t-23-stream ${LINK_DEPENDENCIES})
146154
add_test("t-23-stream" t-23-stream)

README.md

Lines changed: 39 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -57,15 +57,19 @@ can be done on the socket object outside of the connector)
5757

5858
Results achieved with `examples/speed_test_async_multi.cpp` for 1 thread, Intel Core i7-4800MQ, gentoo linux
5959

60-
| bredis (commands/s) | redox (commands/s) |
61-
|----------------------------|------------------------------|
62-
| 1.30257e+06 | 1.19214e+06 |
60+
| bredis (commands/s) | bredis(*) (commands/s) | redox (commands/s)
61+
|---------------------+------------------------+--------------------
62+
| 1.59325e+06 | 2.50826e+06 | 0.999375+06
6363

6464
These results are not completely fair, because of the usage of different semantics in the
6565
APIs; however they are still interesting, as they are using different
6666
underlying event libraries ([Boost::ASIO](http://www.boost.org/doc/libs/release/libs/asio/) vs [libev](http://software.schmorp.de/pkg/libev.html)) as well as redis protocol
6767
parsing libraries (written from scratch vs [hiredis](https://github.com/redis/hiredis))
6868

69+
`(*)` bredis with drop_result policy, i.e. replies from redis server are
70+
scanned only for formal correctness and never delivered to the caller.
71+
72+
6973
## Work with the result
7074

7175
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).
@@ -386,7 +390,7 @@ boost::asio::spawn(
386390
## Steams
387391
388392
There is no specific support for streams (appeared in redis 5.0) in bredis,
389-
they are just usual `XADD`, `XRANGE` etc. commands and corresponding replies.
393+
they are just usual `XADD`, `XRANGE` etc. commands and corresponding replies.
390394
391395
```cpp
392396
...
@@ -410,7 +414,7 @@ rx_buff.consume(parse_result3.consumed);
410414
auto& outer_arr = boost::get<r::extracts::array_holder_t>(extract3);
411415
auto& inner_arr1 = boost::get<r::extracts::array_holder_t>(outer_arr.elements[0]);
412416
auto& inner_arr2 = boost::get<r::extracts::array_holder_t>(outer_arr.elements[1]);
413-
...
417+
...
414418
415419
```
416420

@@ -441,6 +445,35 @@ r::Connection<next_layer_t> c(socket);
441445
socket.cancel();
442446
```
443447
448+
## Thread-safety
449+
450+
`bredis` itself is thread-agnostic, however the underlying socket (`next_layer_t`)
451+
and used buffers are usually not thread-safe. To handle that in multi-thead
452+
environment the access to those objects should be sequenced via
453+
`asio::io_context::strand` . See the `examples/multi-threads-1.cpp`.
454+
455+
456+
## parsing_policy::drop_result
457+
The performance still can be boosted if it is known beforehand that the response from
458+
redis server is not needed at all. For example, the only possible response to `PING`
459+
command is `PONG` reply, usually there is no sense it validating that `PONG` reply,
460+
as soon as it is known, that redis-server alredy delivered us **some** reply
461+
(in practice it is `PONG`). Another example is `SET` command, when redis-server
462+
**usually** replies with `OK`.
463+
464+
With `parsing_policy::drop_result` the reply result is just verified with formal
465+
compliance to redis protocol, and then it is discarded.
466+
467+
It should be noted, that redis can reply back with error, which aslo correct
468+
reply, but the caller side isn't able to see it when `parsing_policy::drop_result`
469+
is applied. So, it should be used with care, when you know what your are doing. You have
470+
been warned.
471+
472+
It is safe, however, to mix different parsing policies on the same connection,
473+
i.e. write `SET` command and read it's reply with `parsing_policy::drop_result` and
474+
then write `GET` command and read it's reply with `parsing_policy::keep_result`.
475+
See the `examples/speed_test_async_multi.cpp`.
476+
444477
## API
445478
446479
There's a convenience header include/bredis.hpp, doing `#include "bredis.hpp"` will include
@@ -675,7 +708,7 @@ The asynchronous read has the following signature:
675708
```cpp
676709
void-or-deduced
677710
async_read(DynamicBuffer &rx_buff, ReadCallback read_callback,
678-
std::size_t replies_count = 1);
711+
std::size_t replies_count = 1, Policy = bredis::parsing_policy::keep_result{});
679712
```
680713

681714
It reads `replies_count` replies from the *next_layer* stream, which will be

examples/CMakeLists.txt

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,15 @@
11
find_package(Threads REQUIRED)
22

3+
set(LINK_DEPENDENCIES ${Boost_LIBRARIES} Threads::Threads)
4+
35
add_executable(stream-parse stream-parse.cpp)
4-
target_link_libraries(stream-parse ${Boost_LIBRARIES} Threads::Threads)
6+
target_link_libraries(stream-parse ${LINK_DEPENDENCIES})
57

68
add_executable(synch-subscription synch-subscription.cpp)
7-
target_link_libraries(synch-subscription ${Boost_LIBRARIES} Threads::Threads)
9+
target_link_libraries(synch-subscription ${LINK_DEPENDENCIES})
810

911
add_executable(speed_test_async_multi speed_test_async_multi.cpp)
10-
target_link_libraries(speed_test_async_multi ${Boost_LIBRARIES} Threads::Threads)
12+
target_link_libraries(speed_test_async_multi ${LINK_DEPENDENCIES})
1113

1214
add_executable(multi-threads-1 multi-threads-1.cpp)
13-
target_link_libraries(multi-threads-1 ${Boost_LIBRARIES} Threads::Threads)
15+
target_link_libraries(multi-threads-1 ${LINK_DEPENDENCIES})

examples/speed_test_async_multi.cpp

Lines changed: 32 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,26 @@
11
//
22
//
3-
// Copyright (c) 2017 Ivan Baidakou (basiliscos) (the dot dmol at gmail dot com)
3+
// Copyright (c) 2017-2019 Ivan Baidakou (basiliscos) (the dot dmol at gmail dot com)
44
//
55
// Distributed under the MIT Software License
66
//
77
// mimics performance measurements from
88
// https://github.com/hmartiro/redox/blob/master/examples/speed_test_async_multi.cpp
99
//
10-
// Results (1 thread, Intel Core i7-4800MQ, linux)
10+
// Results (1 thread, Intel Core i7-8550U, void-linux, gcc 8.3.0)
1111
//
12-
// bredis (commands/s) | redox (commands/s)
13-
// -----------------------+-----------------------
14-
// 1.30257e+06 | 1.19214e+06
12+
// bredis (commands/s) | bredis(*) (commands/s) | redox (commands/s)
13+
// ---------------------+------------------------+---------------------
14+
// 1.59325e+06 | 2.50826e+06 | 0.999375+06
1515
//
1616
// Results are not completely fair, because of usage of different semantics in
1717
// APIs; however they are still interesting, as there are used different
1818
// underlying event libraries (Boost::ASIO vs libev) as well redis protocol
1919
// parsing library (written from scratch vs hiredis)
20+
//
21+
// (*) bredis with drop_result policy, i.e. replies from redis server are
22+
// scanned only for formal correctness and never delivered to the caller
23+
2024

2125
#include <algorithm>
2226
#include <atomic>
@@ -43,15 +47,18 @@ double time_s() {
4347
// alias namespaces
4448
namespace r = bredis;
4549
namespace asio = boost::asio;
50+
using boost::get;
4651

4752
int main(int argc, char **argv) {
4853
// common setup
4954
using socket_t = asio::ip::tcp::socket;
5055
using next_layer_t = socket_t;
5156
using Buffer = boost::asio::streambuf;
5257
using Iterator = typename r::to_iterator<Buffer>::iterator_t;
58+
using policy_t = r::parsing_policy::drop_result;
59+
//using policy_t = r::parsing_policy::keep_result;
5360

54-
if (argc < 1) {
61+
if (argc < 2) {
5562
std::cout << "Usage : " << argv[0] << " ip:port \n";
5663
return 1;
5764
}
@@ -70,12 +77,10 @@ int main(int argc, char **argv) {
7077

7178
// write subscribe cmd
7279
r::single_command_t cmd_incr{"INCR", "simple_loop:count"};
73-
r::single_command_t cmd_get{"GET", "simple_loop:count"};
7480
r::command_container_t cmd_container;
75-
for (auto i = 0; i < cmds_count; ++i) {
81+
for (size_t i = 0; i < cmds_count; ++i) {
7682
cmd_container.push_back(cmd_incr);
7783
}
78-
cmd_container.push_back(cmd_get);
7984

8085
r::command_wrapper_t cmd_wpapper{std::move(cmd_container)};
8186

@@ -92,29 +97,27 @@ int main(int argc, char **argv) {
9297
r::Connection<next_layer_t> c(std::move(socket));
9398

9499
Buffer tx_buff, rx_buff;
95-
std::promise<std::string> completion_promise;
96-
std::future<std::string> completion_future =
97-
completion_promise.get_future();
100+
std::promise<void> completion_promise;
101+
auto completion_future = completion_promise.get_future();
98102

99103
c.async_read(
100104
rx_buff,
101105
[&](const boost::system::error_code &ec, auto &&r) {
102106
assert(!ec);
103-
auto &replies =
104-
boost::get<r::markers::array_holder_t<Iterator>>(r.result);
105-
auto &last_reply = replies.elements.at(replies.elements.size() - 1);
106-
auto &str_reply =
107-
boost::get<r::markers::string_t<Iterator>>(last_reply);
108-
std::string value{str_reply.from, str_reply.to};
107+
(void)ec;
109108
rx_buff.consume(r.consumed);
110-
count += replies.elements.size() - 1;
111-
completion_promise.set_value(value);
109+
// cannot be done with drop_result
110+
//auto &replies = get<r::markers::array_holder_t<Iterator>>(r.result);
111+
//count += replies.elements.size() - 1;
112+
count = cmds_count;
113+
completion_promise.set_value();
112114
std::cout << "done reading...\n";
113115
},
114-
cmds_count + 1);
116+
cmds_count, policy_t{});
115117

116118
c.async_write(tx_buff, cmd_wpapper, [&](const boost::system::error_code &ec,
117119
auto bytes_transferred) {
120+
(void)ec;
118121
assert(!ec);
119122
tx_buff.consume(bytes_transferred);
120123
std::cout << "done writing...\n";
@@ -130,13 +133,20 @@ int main(int argc, char **argv) {
130133

131134
io_service.run();
132135
std::cout << "done...\n";
136+
completion_future.get();
137+
138+
c.write(r::single_command_t{"GET", "simple_loop:count"});
139+
auto r = c.read(rx_buff);
140+
auto &str_reply = get<r::markers::string_t<Iterator>>(r.result);
141+
142+
std::string counter_value {str_reply.from, str_reply.to};
133143

134144
double actual_freq = (double)count / t_elapsed;
135145
std::cout << "Sent " << cmds_count << " commands in " << t_elapsed << "s, "
136146
<< "that's " << actual_freq << " commands/s."
137147
<< "\n";
138148

139-
std::cout << "Final value of counter: " << completion_future.get() << "\n";
149+
std::cout << "Final value of counter: " << counter_value << "\n";
140150

141151
std::cout << "exiting...\n";
142152
return 0;

examples/stream-parse.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ using option_t = boost::optional<json_payload>;
5656

5757
template <typename Iterator>
5858
struct json_extractor : public boost::static_visitor<option_t> {
59-
template <typename T> option_t operator()(const T &value) const {
59+
template <typename T> option_t operator()(const T & /*value*/) const {
6060
return option_t{};
6161
}
6262

examples/synch-subscription.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ using optional_string_t =
5757
boost::optional<std::pair<string_wrapper_t, string_wrapper_t>>;
5858

5959
struct payload_extractor : public boost::static_visitor<optional_string_t> {
60-
template <typename T> optional_string_t operator()(const T &value) const {
60+
template <typename T> optional_string_t operator()(const T & /* value */) const {
6161
return optional_string_t{};
6262
}
6363

include/bredis.hpp

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,7 @@
44
//
55
// Distributed under the MIT Software License
66
//
7-
#ifndef BREDIS_HPP
8-
#define BREDIS_HPP
7+
#pragma once
98

109
#include <bredis/Command.hpp>
1110
#include <bredis/Connection.hpp>
@@ -15,5 +14,3 @@
1514
#include <bredis/Markers.hpp>
1615
#include <bredis/Protocol.hpp>
1716
#include <bredis/Result.hpp>
18-
19-
#endif // BREDIS_HPP

include/bredis/Connection.hpp

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
//
22
//
3-
// Copyright (c) 2017 Ivan Baidakou (basiliscos) (the dot dmol at gmail dot com)
3+
// Copyright (c) 2017, 2019 Ivan Baidakou (basiliscos) (the dot dmol at gmail
4+
// dot com)
45
//
56
// Distributed under the MIT Software License
67
//
@@ -27,9 +28,8 @@
2728

2829
namespace bredis {
2930

30-
#define BREDIS_PARSE_RESULT(B) \
31-
positive_parse_result_t<typename to_iterator<B>::iterator_t, \
32-
bredis::parsing_policy::keep_result>
31+
#define BREDIS_PARSE_RESULT(B, P) \
32+
positive_parse_result_t<typename to_iterator<B>::iterator_t, P>
3333

3434
template <typename NextLayer> class Connection {
3535

@@ -51,23 +51,25 @@ template <typename NextLayer> class Connection {
5151
async_write(DynamicBuffer &tx_buff, const command_wrapper_t &command,
5252
WriteCallback &&write_callback);
5353

54-
template <typename DynamicBuffer, typename ReadCallback>
54+
template <typename DynamicBuffer, typename ReadCallback,
55+
typename Policy = bredis::parsing_policy::keep_result>
5556
BOOST_ASIO_INITFN_RESULT_TYPE(ReadCallback,
5657
void(boost::system::error_code,
57-
BREDIS_PARSE_RESULT(DynamicBuffer)))
58+
BREDIS_PARSE_RESULT(DynamicBuffer,
59+
Policy)))
5860
async_read(DynamicBuffer &rx_buff, ReadCallback &&read_callback,
59-
std::size_t replies_count = 1);
61+
std::size_t replies_count = 1, Policy policy = Policy{});
6062

6163
/* synchronous interface */
6264
void write(const command_wrapper_t &command);
6365
void write(const command_wrapper_t &command, boost::system::error_code &ec);
6466

6567
template <typename DynamicBuffer>
66-
BREDIS_PARSE_RESULT(DynamicBuffer)
68+
BREDIS_PARSE_RESULT(DynamicBuffer, bredis::parsing_policy::keep_result)
6769
read(DynamicBuffer &rx_buff);
6870

6971
template <typename DynamicBuffer>
70-
BREDIS_PARSE_RESULT(DynamicBuffer)
72+
BREDIS_PARSE_RESULT(DynamicBuffer, bredis::parsing_policy::keep_result)
7173
read(DynamicBuffer &rx_buff, boost::system::error_code &ec);
7274
};
7375

include/bredis/Extract.hpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ struct extractor : public boost::static_visitor<extracts::extraction_result_t> {
7878
}
7979

8080
extracts::extraction_result_t
81-
operator()(const markers::nil_t<Iterator> &value) const {
81+
operator()(const markers::nil_t<Iterator> & /*value*/) const {
8282
return extracts::nil_t{};
8383
}
8484

0 commit comments

Comments
 (0)