Skip to content

Commit d2055a0

Browse files
authored
chore: add hdr_histogram to the project (#5330)
* chore: add hdr_histogram to the project Mostly a boilerplate PR, should not change functionality. 1. reduce number of absl::GetCurrentTimeNanos() calls by one during the command execution. 2. add hdr_histogram object to CommandId but do not yet record anything into it. First part to address #5092 --------- Signed-off-by: Roman Gershman <[email protected]>
1 parent b6b2247 commit d2055a0

File tree

8 files changed

+78
-16
lines changed

8 files changed

+78
-16
lines changed

src/CMakeLists.txt

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,17 @@ add_third_party(
125125
-DFLATBUFFERS_BUILD_FLATC=OFF"
126126
)
127127

128+
add_third_party(
129+
hdr_histogram
130+
GIT_REPOSITORY https://github.com/HdrHistogram/HdrHistogram_c/
131+
GIT_TAG 652d51bcc36744fd1a6debfeb1a8a5f58b14022c
132+
GIT_SHALLOW 1
133+
CMAKE_PASS_FLAGS "-DHDR_LOG_REQUIRED=OFF -DHDR_HISTOGRAM_BUILD_PROGRAMS=OFF
134+
-DHDR_HISTOGRAM_INSTALL_SHARED=OFF"
135+
LIB libhdr_histogram_static.a
136+
)
137+
138+
128139
add_library(TRDP::jsoncons INTERFACE IMPORTED)
129140
add_dependencies(TRDP::jsoncons jsoncons_project)
130141
set_target_properties(TRDP::jsoncons PROPERTIES

src/server/CMakeLists.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@ if (WITH_AWS)
8282
add_definitions(-DWITH_AWS)
8383
endif()
8484

85-
cxx_link(dfly_transaction dfly_core strings_lib TRDP::fast_float)
85+
cxx_link(dfly_transaction dfly_core strings_lib TRDP::fast_float TRDP::hdr_histogram)
8686
cxx_link(dragonfly_lib dfly_transaction dfly_facade redis_lib ${AWS_LIB} jsonpath
8787
strings_lib html_lib gcp_lib azure_lib
8888
http_client_lib absl::random_random TRDP::jsoncons TRDP::zstd TRDP::lz4

src/server/acl/acl_family.cc

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1186,12 +1186,13 @@ std::variant<User::UpdateRequest, ErrorReply> AclFamily::ParseAclSetUser(
11861186
}
11871187

11881188
void AclFamily::BuildIndexers(RevCommandsIndexStore families) {
1189-
acl::NumberOfFamilies(families.size());
1189+
size_t family_count = acl::NumberOfFamilies(families.size());
11901190
CommandsRevIndexer(std::move(families));
11911191
CategoryToCommandsIndexStore index;
11921192
cmd_registry_->Traverse([&](std::string_view, auto& cid) {
11931193
const uint32_t cat = cid.acl_categories();
11941194
const size_t family = cid.GetFamily();
1195+
DCHECK_LT(family, family_count);
11951196
const uint64_t bit_index = cid.GetBitIndex();
11961197
for (size_t i = 0; i < 32; ++i) {
11971198
if (cat & 1 << i) {

src/server/command_registry.cc

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -121,13 +121,27 @@ CmdLineMapping OriginalToAliasMap() {
121121
return original_to_alias;
122122
}
123123

124+
constexpr int64_t kLatencyHistogramMinValue = 1; // Minimum value in usec
125+
constexpr int64_t kLatencyHistogramMaxValue = 1000000; // Maximum value in usec (1s)
126+
constexpr int32_t kLatencyHistogramPrecision = 2;
127+
124128
} // namespace
125129

126130
CommandId::CommandId(const char* name, uint32_t mask, int8_t arity, int8_t first_key,
127131
int8_t last_key, std::optional<uint32_t> acl_categories)
128132
: facade::CommandId(name, ImplicitCategories(mask), arity, first_key, last_key,
129133
acl_categories.value_or(ImplicitAclCategories(mask))) {
130134
implicit_acl_ = !acl_categories.has_value();
135+
hdr_histogram* hist = nullptr;
136+
hdr_init(kLatencyHistogramMinValue, kLatencyHistogramMaxValue, kLatencyHistogramPrecision, &hist);
137+
latency_histogram_ = hist;
138+
}
139+
140+
CommandId::~CommandId() {
141+
// Aliases share the same latency histogram, so we only close it if this is not an alias.
142+
if (latency_histogram_ && !is_alias_) {
143+
hdr_close(latency_histogram_);
144+
}
131145
}
132146

133147
CommandId CommandId::Clone(const std::string_view name) const {
@@ -138,6 +152,11 @@ CommandId CommandId::Clone(const std::string_view name) const {
138152
cloned.acl_categories_ = acl_categories_;
139153
cloned.implicit_acl_ = implicit_acl_;
140154
cloned.is_alias_ = true;
155+
156+
// explicit sharing of the object since it's an alias we can do that.
157+
// I am assuming that the source object lifetime is at least as of the cloned object.
158+
hdr_close(cloned.latency_histogram_); // Free the histogram in the cloned object.
159+
cloned.latency_histogram_ = static_cast<hdr_histogram*>(latency_histogram_);
141160
return cloned;
142161
}
143162

@@ -157,7 +176,8 @@ bool CommandId::IsMultiTransactional() const {
157176
}
158177

159178
uint64_t CommandId::Invoke(CmdArgList args, const CommandContext& cmd_cntx) const {
160-
int64_t before = absl::GetCurrentTimeNanos();
179+
uint64_t before = cmd_cntx.conn_cntx->conn_state.cmd_start_time_ns;
180+
DCHECK_GT(before, 0u);
161181
handler_(args, cmd_cntx);
162182
int64_t after = absl::GetCurrentTimeNanos();
163183

src/server/command_registry.h

Lines changed: 32 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
#include <absl/container/flat_hash_map.h>
88
#include <absl/container/flat_hash_set.h>
99
#include <absl/types/span.h>
10+
#include <hdr/hdr_histogram.h>
1011

1112
#include <functional>
1213
#include <optional>
@@ -84,14 +85,41 @@ struct CommandContext {
8485
ConnectionContext* conn_cntx;
8586
};
8687

88+
// TODO: move it to helio
89+
// Makes sure that the POD T that is passed to the constructor is reset to default state
90+
template <typename T> class MoveOnly {
91+
public:
92+
MoveOnly() = default;
93+
94+
MoveOnly(const MoveOnly&) = delete;
95+
MoveOnly& operator=(const MoveOnly&) = delete;
96+
97+
MoveOnly(MoveOnly&& t) noexcept : value_(std::move(t.value_)) {
98+
t.value_ = T{}; // Reset the passed value to default state
99+
}
100+
101+
MoveOnly& operator=(const T& t) noexcept {
102+
value_ = t;
103+
return *this;
104+
}
105+
106+
operator const T&() const { // NOLINT
107+
return value_;
108+
}
109+
110+
private:
111+
T value_;
112+
};
113+
87114
class CommandId : public facade::CommandId {
88115
public:
89116
// NOTICE: name must be a literal string, otherwise metrics break! (see cmd_stats_map in
90117
// server_state.h)
91118
CommandId(const char* name, uint32_t mask, int8_t arity, int8_t first_key, int8_t last_key,
92119
std::optional<uint32_t> acl_categories = std::nullopt);
120+
CommandId(CommandId&& o) = default;
93121

94-
CommandId(CommandId&&) = default;
122+
~CommandId();
95123

96124
[[nodiscard]] CommandId Clone(std::string_view name) const;
97125

@@ -160,11 +188,13 @@ class CommandId : public facade::CommandId {
160188
}
161189

162190
private:
191+
// The following fields must copy manually in the move constructor.
163192
bool implicit_acl_;
193+
bool is_alias_{false};
164194
std::unique_ptr<CmdCallStats[]> command_stats_;
165195
Handler3 handler_;
166196
ArgValidator validator_;
167-
bool is_alias_{false};
197+
MoveOnly<hdr_histogram*> latency_histogram_; // Histogram for command latency in usec
168198
};
169199

170200
class CommandRegistry {

src/server/conn_context.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -147,7 +147,7 @@ struct ConnectionState {
147147
const ConnectionContext* owner = nullptr;
148148
};
149149

150-
enum MCGetMask { FETCH_CAS_VER = 1 };
150+
enum MCGetMask : uint8_t { FETCH_CAS_VER = 1 };
151151

152152
size_t UsedMemory() const;
153153

@@ -265,6 +265,7 @@ struct ConnectionState {
265265
std::unique_ptr<ScriptInfo> script_info;
266266
std::unique_ptr<SubscribeInfo> subscribe_info;
267267
ClientTracking tracking_info_;
268+
uint64_t cmd_start_time_ns = 0; // time when the last command started executing
268269
};
269270

270271
class ConnectionContext : public facade::ConnectionContext {

src/server/main_service.cc

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1044,11 +1044,10 @@ static optional<ErrorReply> VerifyConnectionAclStatus(const CommandId* cid,
10441044
return nullopt;
10451045
}
10461046

1047-
bool ShouldDenyOnOOM(const CommandId* cid) {
1047+
bool ShouldDenyOnOOM(const CommandId* cid, uint64_t curr_time_ns) {
10481048
ServerState& etl = *ServerState::tlocal();
10491049
if ((cid->opt_mask() & CO::DENYOOM) && etl.is_master) {
1050-
uint64_t start_ns = absl::GetCurrentTimeNanos();
1051-
auto memory_stats = etl.GetMemoryUsage(start_ns);
1050+
auto memory_stats = etl.GetMemoryUsage(curr_time_ns);
10521051

10531052
if (memory_stats.used_mem > max_memory_limit ||
10541053
(etl.rss_oom_deny_ratio > 0 &&
@@ -1062,14 +1061,14 @@ bool ShouldDenyOnOOM(const CommandId* cid) {
10621061
return false;
10631062
}
10641063

1065-
optional<ErrorReply> Service::VerifyCommandExecution(const CommandId* cid,
1066-
const ConnectionContext* cntx,
1064+
optional<ErrorReply> Service::VerifyCommandExecution(const ConnectionContext* cntx,
10671065
CmdArgList tail_args) {
1068-
if (ShouldDenyOnOOM(cid)) {
1066+
DCHECK(cntx->conn_state.cmd_start_time_ns);
1067+
if (ShouldDenyOnOOM(cntx->cid, cntx->conn_state.cmd_start_time_ns)) {
10691068
return facade::ErrorReply{OpStatus::OUT_OF_MEMORY};
10701069
}
10711070

1072-
return VerifyConnectionAclStatus(cid, cntx, "ACL rules changed between the MULTI and EXEC",
1071+
return VerifyConnectionAclStatus(cntx->cid, cntx, "ACL rules changed between the MULTI and EXEC",
10731072
tail_args);
10741073
}
10751074

@@ -1361,11 +1360,12 @@ DispatchResult Service::InvokeCmd(const CommandId* cid, CmdArgList tail_args,
13611360

13621361
ConnectionContext* cntx = cmd_cntx.conn_cntx;
13631362
cntx->cid = cid;
1363+
cntx->conn_state.cmd_start_time_ns = absl::GetCurrentTimeNanos();
13641364
auto* builder = cmd_cntx.rb;
13651365
DCHECK(builder);
13661366
DCHECK(cntx);
13671367

1368-
if (auto err = VerifyCommandExecution(cid, cntx, tail_args); err) {
1368+
if (auto err = VerifyCommandExecution(cntx, tail_args); err) {
13691369
// We need to skip this because ACK's should not be replied to
13701370
// Bonus points because this allows to continue replication with ACL users who got
13711371
// their access revoked and reinstated

src/server/main_service.h

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -49,8 +49,7 @@ class Service : public facade::ServiceInterface {
4949

5050
// Verify command can be executed now (check out of memory), always called immediately before
5151
// execution
52-
std::optional<facade::ErrorReply> VerifyCommandExecution(const CommandId* cid,
53-
const ConnectionContext* cntx,
52+
std::optional<facade::ErrorReply> VerifyCommandExecution(const ConnectionContext* cntx,
5453
CmdArgList tail_args);
5554

5655
// Verify command prepares excution in correct state.

0 commit comments

Comments
 (0)