Skip to content

Commit d8cf431

Browse files
authored
Merge pull request #149 from boostorg/144-implement-connection-usage-information
Adds connection usage information.
2 parents 509635f + 401dd24 commit d8cf431

File tree

7 files changed

+136
-35
lines changed

7 files changed

+136
-35
lines changed

README.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -686,6 +686,10 @@ https://lists.boost.org/Archives/boost/2023/01/253944.php.
686686
performance improvement where one of my benchmark programs passed
687687
from 190k/s to 473k/s.
688688

689+
* The connection has a new member `get_usage()` that returns the
690+
connection usage information, such as number of bytes writen,
691+
received etc.
692+
689693
### v1.4.2 (incorporates changes to conform the boost review and more)
690694

691695
* Adds `boost::redis::config::database_index` to make it possible to

include/boost/redis/connection.hpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -292,6 +292,10 @@ class basic_connection {
292292
void set_receive_response(Response& response)
293293
{ impl_.set_receive_response(response); }
294294

295+
/// Returns connection usage information.
296+
usage get_usage() const noexcept
297+
{ return impl_.get_usage(); }
298+
295299
private:
296300
using timer_type =
297301
asio::basic_waitable_timer<
@@ -394,6 +398,10 @@ class connection {
394398
void set_receive_response(Response& response)
395399
{ impl_.set_receive_response(response); }
396400

401+
/// Returns connection usage information.
402+
usage get_usage() const noexcept
403+
{ return impl_.get_usage(); }
404+
397405
private:
398406
void
399407
async_run_impl(

include/boost/redis/detail/connection_base.hpp

Lines changed: 45 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
#include <boost/redis/resp3/type.hpp>
1717
#include <boost/redis/config.hpp>
1818
#include <boost/redis/detail/runner.hpp>
19+
#include <boost/redis/usage.hpp>
1920

2021
#include <boost/system.hpp>
2122
#include <boost/asio/basic_stream_socket.hpp>
@@ -40,16 +41,15 @@
4041
#include <type_traits>
4142
#include <functional>
4243

43-
namespace boost::redis::detail {
44+
namespace boost::redis::detail
45+
{
4446

4547
template <class Conn>
4648
struct exec_op {
4749
using req_info_type = typename Conn::req_info;
4850
using adapter_type = typename Conn::adapter_type;
4951

5052
Conn* conn_ = nullptr;
51-
request const* req_ = nullptr;
52-
adapter_type adapter{};
5353
std::shared_ptr<req_info_type> info_ = nullptr;
5454
asio::coroutine coro{};
5555

@@ -60,14 +60,12 @@ struct exec_op {
6060
{
6161
// Check whether the user wants to wait for the connection to
6262
// be stablished.
63-
if (req_->get_config().cancel_if_not_connected && !conn_->is_open()) {
63+
if (info_->req_->get_config().cancel_if_not_connected && !conn_->is_open()) {
6464
BOOST_ASIO_CORO_YIELD
6565
asio::post(std::move(self));
6666
return self.complete(error::not_connected, 0);
6767
}
6868

69-
info_ = std::allocate_shared<req_info_type>(asio::get_associated_allocator(self), *req_, adapter, conn_->get_executor());
70-
7169
conn_->add_request_info(info_);
7270

7371
EXEC_OP_WAIT:
@@ -329,6 +327,10 @@ class connection_base {
329327
/// Type of the next layer
330328
using next_layer_type = asio::ssl::stream<asio::basic_stream_socket<asio::ip::tcp, Executor>>;
331329

330+
using clock_type = std::chrono::steady_clock;
331+
using clock_traits_type = asio::wait_traits<clock_type>;
332+
using timer_type = asio::basic_waitable_timer<clock_type, clock_traits_type, executor_type>;
333+
332334
using receiver_adapter_type = std::function<void(resp3::basic_node<std::string_view> const&, system::error_code&)>;
333335

334336
using this_type = connection_base<Executor>;
@@ -391,12 +393,14 @@ class connection_base {
391393
{
392394
using namespace boost::redis::adapter;
393395
auto f = boost_redis_adapt(resp);
394-
BOOST_ASSERT_MSG(req.size() <= f.get_supported_response_size(), "Request and response have incompatible sizes.");
396+
BOOST_ASSERT_MSG(req.get_expected_responses() <= f.get_supported_response_size(), "Request and response have incompatible sizes.");
397+
398+
auto info = std::make_shared<req_info>(req, f, get_executor());
395399

396400
return asio::async_compose
397401
< CompletionToken
398402
, void(system::error_code, std::size_t)
399-
>(redis::detail::exec_op<this_type>{this, &req, f}, token, writer_timer_);
403+
>(exec_op<this_type>{this, info}, token, writer_timer_);
400404
}
401405

402406
template <class Response, class CompletionToken>
@@ -427,12 +431,12 @@ class connection_base {
427431
receive_adapter_ = adapter::detail::make_adapter_wrapper(g);
428432
}
429433

434+
usage get_usage() const noexcept
435+
{ return usage_; }
436+
430437
private:
431-
using clock_type = std::chrono::steady_clock;
432-
using clock_traits_type = asio::wait_traits<clock_type>;
433-
using timer_type = asio::basic_waitable_timer<clock_type, clock_traits_type, executor_type>;
434438
using receive_channel_type = asio::experimental::channel<executor_type, void(system::error_code, std::size_t)>;
435-
using runner_type = redis::detail::runner<executor_type>;
439+
using runner_type = runner<executor_type>;
436440
using adapter_type = std::function<void(std::size_t, resp3::basic_node<std::string_view> const&, system::error_code&)>;
437441

438442
auto use_ssl() const noexcept
@@ -545,7 +549,7 @@ class connection_base {
545549
, action_{action::none}
546550
, req_{&req}
547551
, adapter_{}
548-
, cmds_{std::size(req)}
552+
, expected_responses_{req.get_expected_responses()}
549553
, status_{status::none}
550554
, ec_{{}}
551555
, read_size_{0}
@@ -554,7 +558,7 @@ class connection_base {
554558

555559
adapter_ = [this, adapter](node_type const& nd, system::error_code& ec)
556560
{
557-
auto const i = std::size(*req_) - cmds_;
561+
auto const i = req_->get_expected_responses() - expected_responses_;
558562
adapter(i, nd, ec);
559563
};
560564
}
@@ -611,7 +615,7 @@ class connection_base {
611615
wrapped_adapter_type adapter_;
612616

613617
// Contains the number of commands that haven't been read yet.
614-
std::size_t cmds_;
618+
std::size_t expected_responses_;
615619
status status_;
616620

617621
system::error_code ec_;
@@ -625,16 +629,16 @@ class connection_base {
625629

626630
using reqs_type = std::deque<std::shared_ptr<req_info>>;
627631

628-
template <class, class> friend struct redis::detail::reader_op;
629-
template <class, class> friend struct redis::detail::writer_op;
630-
template <class, class> friend struct redis::detail::run_op;
631-
template <class> friend struct redis::detail::exec_op;
632-
template <class, class, class> friend struct redis::detail::run_all_op;
632+
template <class, class> friend struct reader_op;
633+
template <class, class> friend struct writer_op;
634+
template <class, class> friend struct run_op;
635+
template <class> friend struct exec_op;
636+
template <class, class, class> friend struct run_all_op;
633637

634638
void cancel_push_requests()
635639
{
636640
auto point = std::stable_partition(std::begin(reqs_), std::end(reqs_), [](auto const& ptr) {
637-
return !(ptr->is_staged() && ptr->req_->size() == 0);
641+
return !(ptr->is_staged() && ptr->req_->get_expected_responses() == 0);
638642
});
639643

640644
std::for_each(point, std::end(reqs_), [](auto const& ptr) {
@@ -671,7 +675,7 @@ class connection_base {
671675
return asio::async_compose
672676
< CompletionToken
673677
, void(system::error_code)
674-
>(redis::detail::reader_op<this_type, Logger>{this, l}, token, writer_timer_);
678+
>(reader_op<this_type, Logger>{this, l}, token, writer_timer_);
675679
}
676680

677681
template <class CompletionToken, class Logger>
@@ -680,7 +684,7 @@ class connection_base {
680684
return asio::async_compose
681685
< CompletionToken
682686
, void(system::error_code)
683-
>(redis::detail::writer_op<this_type, Logger>{this, l}, token, writer_timer_);
687+
>(writer_op<this_type, Logger>{this, l}, token, writer_timer_);
684688
}
685689

686690
template <class Logger, class CompletionToken>
@@ -691,7 +695,7 @@ class connection_base {
691695
return asio::async_compose
692696
< CompletionToken
693697
, void(system::error_code)
694-
>(redis::detail::run_op<this_type, Logger>{this, l}, token, writer_timer_);
698+
>(run_op<this_type, Logger>{this, l}, token, writer_timer_);
695699
}
696700

697701
[[nodiscard]] bool coalesce_requests()
@@ -706,8 +710,11 @@ class connection_base {
706710
// Stage the request.
707711
write_buffer_ += ri->req_->payload();
708712
ri->mark_staged();
713+
usage_.commands_sent += ri->expected_responses_;
709714
});
710715

716+
usage_.bytes_sent += std::size(write_buffer_);
717+
711718
return point != std::cend(reqs_);
712719
}
713720

@@ -758,13 +765,13 @@ class connection_base {
758765
return
759766
(resp3::to_type(read_buffer_.front()) == resp3::type::push)
760767
|| reqs_.empty()
761-
|| (!reqs_.empty() && reqs_.front()->cmds_ == 0)
768+
|| (!reqs_.empty() && reqs_.front()->expected_responses_ == 0)
762769
|| !is_waiting_response(); // Added to deal with MONITOR.
763770
}
764771

765772
auto get_suggested_buffer_growth() const noexcept
766773
{
767-
return parser_.get_suggested_buffer_growth(1024);
774+
return parser_.get_suggested_buffer_growth(4096);
768775
}
769776

770777
enum class parse_result { needs_more, push, resp };
@@ -773,6 +780,14 @@ class connection_base {
773780

774781
parse_ret_type on_finish_parsing(parse_result t)
775782
{
783+
if (t == parse_result::push) {
784+
usage_.pushes_received += 1;
785+
usage_.push_bytes_received += parser_.get_consumed();
786+
} else {
787+
usage_.responses_received += 1;
788+
usage_.response_bytes_received += parser_.get_consumed();
789+
}
790+
776791
on_push_ = false;
777792
dbuf_.consume(parser_.get_consumed());
778793
auto const res = std::make_pair(t, parser_.get_consumed());
@@ -808,7 +823,7 @@ class connection_base {
808823
BOOST_ASSERT_MSG(is_waiting_response(), "Not waiting for a response (using MONITOR command perhaps?)");
809824
BOOST_ASSERT(!reqs_.empty());
810825
BOOST_ASSERT(reqs_.front() != nullptr);
811-
BOOST_ASSERT(reqs_.front()->cmds_ != 0);
826+
BOOST_ASSERT(reqs_.front()->expected_responses_ != 0);
812827

813828
if (!resp3::parse(parser_, data, reqs_.front()->adapter_, ec))
814829
return std::make_pair(parse_result::needs_more, 0);
@@ -821,7 +836,7 @@ class connection_base {
821836

822837
reqs_.front()->read_size_ += parser_.get_consumed();
823838

824-
if (--reqs_.front()->cmds_ == 0) {
839+
if (--reqs_.front()->expected_responses_ == 0) {
825840
// Done with this request.
826841
reqs_.front()->proceed();
827842
reqs_.pop_front();
@@ -849,6 +864,8 @@ class connection_base {
849864
reqs_type reqs_;
850865
resp3::parser parser_{};
851866
bool on_push_ = false;
867+
868+
usage usage_;
852869
};
853870

854871
} // boost::redis::detail

include/boost/redis/logger.hpp

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,11 @@ class logger {
3030
* @ingroup high-level-api
3131
*/
3232
enum class level
33-
{ /// Emergency
33+
{
34+
/// Disabled
35+
disabled,
36+
37+
/// Emergency
3438
emerg,
3539

3640
/// Alert
@@ -60,7 +64,7 @@ class logger {
6064
*
6165
* @param l Log level.
6266
*/
63-
logger(level l = level::info)
67+
logger(level l = level::disabled)
6468
: level_{l}
6569
{}
6670

include/boost/redis/request.hpp

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -84,8 +84,12 @@ class request {
8484
request(config cfg = config{true, false, true, true})
8585
: cfg_{cfg} {}
8686

87+
//// Returns the number of responses expected for this request.
88+
[[nodiscard]] auto get_expected_responses() const noexcept -> std::size_t
89+
{ return expected_responses_;};
90+
8791
//// Returns the number of commands contained in this request.
88-
[[nodiscard]] auto size() const noexcept -> std::size_t
92+
[[nodiscard]] auto get_commands() const noexcept -> std::size_t
8993
{ return commands_;};
9094

9195
[[nodiscard]] auto payload() const noexcept -> std::string_view
@@ -99,6 +103,7 @@ class request {
99103
{
100104
payload_.clear();
101105
commands_ = 0;
106+
expected_responses_ = 0;
102107
has_hello_priority_ = false;
103108
}
104109

@@ -303,8 +308,10 @@ class request {
303308
private:
304309
void check_cmd(std::string_view cmd)
305310
{
311+
++commands_;
312+
306313
if (!detail::has_response(cmd))
307-
++commands_;
314+
++expected_responses_;
308315

309316
if (cmd == "HELLO")
310317
has_hello_priority_ = cfg_.hello_with_priority;
@@ -313,6 +320,7 @@ class request {
313320
config cfg_;
314321
std::string payload_;
315322
std::size_t commands_ = 0;
323+
std::size_t expected_responses_ = 0;
316324
bool has_hello_priority_ = false;
317325
};
318326

include/boost/redis/usage.hpp

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
/* Copyright (c) 2018-2022 Marcelo Zimbres Silva ([email protected])
2+
*
3+
* Distributed under the Boost Software License, Version 1.0. (See
4+
* accompanying file LICENSE.txt)
5+
*/
6+
7+
#ifndef BOOST_REDIS_USAGE_HPP
8+
#define BOOST_REDIS_USAGE_HPP
9+
10+
namespace boost::redis
11+
{
12+
13+
/** @brief Connection usage information.
14+
* @ingroup high-level-api
15+
*
16+
* @note: To simplify the implementation, the commands_sent and
17+
* bytes_sent in the struct below are computed just before writing to
18+
* the socket, which means on error they might not represent exaclty
19+
* what has been received by the Redis server.
20+
*/
21+
struct usage {
22+
/// Number of commands sent.
23+
std::size_t commands_sent = 0;
24+
25+
/// Number of bytes sent.
26+
std::size_t bytes_sent = 0;
27+
28+
/// Number of responses received.
29+
std::size_t responses_received = 0;
30+
31+
/// Number of pushes received.
32+
std::size_t pushes_received = 0;
33+
34+
/// Number of response-bytes received.
35+
std::size_t response_bytes_received = 0;
36+
37+
/// Number of push-bytes received.
38+
std::size_t push_bytes_received = 0;
39+
};
40+
41+
} // boost::redis
42+
43+
#endif // BOOST_REDIS_USAGE_HPP

0 commit comments

Comments
 (0)