Skip to content

Commit 71804e2

Browse files
daverigbytrondn
authored andcommitted
MB-43680: Record Snappy decompress times
Time the duration of Snappy decompress called made as part of executing client requests, recoring the timings to: * A new trace span. * A new per-bucket histogram. The trace span will be reported as part of Slow operation logging - for example: WARNING 57: Slow operation. {...,"trace":"... snappy.decompress=6446937532739980:18555 ... The histograms will be reported via mctimings using the stat key 'snappy_decompress', this will be added to cbcollect_info via a future ns_server patch. Change-Id: I72d44cd79aaef3e20be8fad543d6e950e7bbfd75 Reviewed-on: http://review.couchbase.org/c/kv_engine/+/143527 Tested-by: Build Bot <[email protected]> Reviewed-by: Trond Norbye <[email protected]>
1 parent cbee4c1 commit 71804e2

File tree

14 files changed

+154
-41
lines changed

14 files changed

+154
-41
lines changed

daemon/buckets.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,9 @@ class Bucket {
134134
*/
135135
Hdr1sfMicroSecHistogram subjson_operation_times;
136136

137+
/// Snappy decompression time histogram.
138+
Hdr1sfMicroSecHistogram snappyDecompressionTimes;
139+
137140
using ResponseCounter = cb::RelaxedAtomic<uint64_t>;
138141

139142
/**

daemon/cookie.cc

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,8 @@
3636
#include <phosphor/phosphor.h>
3737
#include <platform/checked_snprintf.h>
3838
#include <platform/compress.h>
39+
#include <platform/histogram.h>
40+
#include <platform/scope_timer.h>
3941
#include <platform/string_hex.h>
4042
#include <platform/timeutils.h>
4143
#include <platform/uuid.h>
@@ -601,8 +603,7 @@ bool Cookie::inflateInputPayload(const cb::mcbp::Header& header) {
601603

602604
try {
603605
auto val = header.getValue();
604-
if (!cb::compression::inflate(
605-
cb::compression::Algorithm::Snappy,
606+
if (!inflateSnappy(
606607
{reinterpret_cast<const char*>(val.data()), val.size()},
607608
inflated_input_payload)) {
608609
setErrorContext("Failed to inflate payload");
@@ -616,6 +617,20 @@ bool Cookie::inflateInputPayload(const cb::mcbp::Header& header) {
616617
return true;
617618
}
618619

620+
bool Cookie::inflateSnappy(std::string_view input,
621+
cb::compression::Buffer& output) {
622+
// Record how long Snappy decompression takes to both Tracer and
623+
// bucket-level histogram.
624+
using namespace cb::tracing;
625+
ScopeTimer2<HdrMicroSecStopwatch, SpanStopwatch> timer(
626+
std::forward_as_tuple(
627+
getConnection().getBucket().snappyDecompressionTimes),
628+
std::forward_as_tuple(*this, Code::SnappyDecompress));
629+
630+
return cb::compression::inflate(
631+
cb::compression::Algorithm::Snappy, input, output);
632+
}
633+
619634
cb::rbac::PrivilegeAccess Cookie::checkPrivilege(
620635
cb::rbac::Privilege privilege,
621636
std::optional<ScopeID> sid,

daemon/cookie.h

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,9 @@ class Header;
3636
class Request;
3737
class Response;
3838
} // namespace cb::mcbp
39+
namespace cb::compression {
40+
class Buffer;
41+
} // namespace cb::compression
3942

4043
/**
4144
* The Cookie class represents the cookie passed from the memcached core
@@ -476,14 +479,26 @@ class Cookie : public cb::tracing::Traceable {
476479
std::string_view getInflatedInputPayload() const;
477480

478481
/**
479-
* Inflate the value (if deflated)
482+
* Inflate the value (if deflated); caching the inflated value inside the
483+
* cookie.
480484
*
481485
* @param header The packet header
482486
* @return true if success, false if an error occurs (the error context
483487
* contains the reason why)
484488
*/
485489
bool inflateInputPayload(const cb::mcbp::Header& header);
486490

491+
/**
492+
* Helper function to inflate the specified Snappy-compressed buffer.
493+
*
494+
* Records the time taken to decompress as SnappyDecompress tracing span
495+
* against this cookie.
496+
* @param input Snappy-compressed input buffer.
497+
* @param output Buffer to write uncompressed data to.
498+
* @returns true if successfully inflated, else false.
499+
*/
500+
bool inflateSnappy(std::string_view input, cb::compression::Buffer& output);
501+
487502
/// Set the current collection meta information. The packet validator
488503
/// is responsible for checking that the requested collection identifier
489504
/// is a legal scope (and return an error if it isn't) and to avoid having

daemon/mcbp.cc

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -49,8 +49,7 @@ static bool mcbp_response_handler(std::string_view key,
4949
mcbp::datatype::is_xattr(datatype))) {
5050
// The client is not snappy-aware, and the content contains
5151
// snappy encoded data. Or it's xattr compressed. We need to inflate it!
52-
if (!cb::compression::inflate(cb::compression::Algorithm::Snappy,
53-
payload, buffer)) {
52+
if (!cookie->inflateSnappy(payload, buffer)) {
5453
std::string mykey(key.data(), key.size());
5554
LOG_WARNING(
5655
"<{} ERROR: Failed to inflate body, "

daemon/protocol/mcbp/appendprepend_context.cc

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -95,9 +95,7 @@ ENGINE_ERROR_CODE AppendPrependCommandContext::getItem() {
9595
std::string_view payload(
9696
static_cast<const char*>(oldItemInfo.value[0].iov_base),
9797
oldItemInfo.value[0].iov_len);
98-
if (!cb::compression::inflate(cb::compression::Algorithm::Snappy,
99-
payload,
100-
buffer)) {
98+
if (!cookie.inflateSnappy(payload, buffer)) {
10199
return ENGINE_FAILED;
102100
}
103101
} catch (const std::bad_alloc&) {

daemon/protocol/mcbp/arithmetic_context.cc

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -57,10 +57,7 @@ ENGINE_ERROR_CODE ArithmeticCommandContext::getItem() {
5757
std::string_view payload(
5858
static_cast<const char*>(oldItemInfo.value[0].iov_base),
5959
oldItemInfo.value[0].iov_len);
60-
if (!cb::compression::inflate(
61-
cb::compression::Algorithm::Snappy,
62-
payload,
63-
buffer)) {
60+
if (!cookie.inflateSnappy(payload, buffer)) {
6461
return ENGINE_FAILED;
6562
}
6663
} catch (const std::bad_alloc&) {

daemon/protocol/mcbp/gat_context.cc

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -86,8 +86,7 @@ ENGINE_ERROR_CODE GatCommandContext::getAndTouchItem() {
8686

8787
ENGINE_ERROR_CODE GatCommandContext::inflateItem() {
8888
try {
89-
if (!cb::compression::inflate(cb::compression::Algorithm::Snappy,
90-
payload, buffer)) {
89+
if (!cookie.inflateSnappy(payload, buffer)) {
9190
LOG_WARNING("{}: Failed to inflate item", connection.getId());
9291
return ENGINE_FAILED;
9392
}

daemon/protocol/mcbp/get_context.cc

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -61,8 +61,7 @@ ENGINE_ERROR_CODE GetCommandContext::getItem() {
6161

6262
ENGINE_ERROR_CODE GetCommandContext::inflateItem() {
6363
try {
64-
if (!cb::compression::inflate(cb::compression::Algorithm::Snappy,
65-
payload, buffer)) {
64+
if (!cookie.inflateSnappy(payload, buffer)) {
6665
LOG_WARNING("{}: Failed to inflate item", connection.getId());
6766
return ENGINE_FAILED;
6867
}

daemon/protocol/mcbp/get_locked_context.cc

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -64,8 +64,7 @@ ENGINE_ERROR_CODE GetLockedCommandContext::getAndLockItem() {
6464

6565
ENGINE_ERROR_CODE GetLockedCommandContext::inflateItem() {
6666
try {
67-
if (!cb::compression::inflate(cb::compression::Algorithm::Snappy,
68-
payload, buffer)) {
67+
if (!cookie.inflateSnappy(payload, buffer)) {
6968
LOG_WARNING(
7069
"{}: GetLockedCommandContext::inflateItem:"
7170
" Failed to inflate item",

daemon/protocol/mcbp/stats_context.cc

Lines changed: 47 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -303,6 +303,36 @@ static ENGINE_ERROR_CODE stat_connections_executor(const std::string& arg,
303303
return ENGINE_EWOULDBLOCK;
304304
}
305305

306+
/**
307+
* Helper function to append JSON-formatted histogram statistics for the
308+
* specified Bucket histogram data member.
309+
*/
310+
static ENGINE_ERROR_CODE stat_histogram_executor(
311+
const std::string& arg,
312+
Cookie& cookie,
313+
Hdr1sfMicroSecHistogram Bucket::*histogram) {
314+
if (arg.empty()) {
315+
const auto index = cookie.getConnection().getBucketIndex();
316+
std::string json_str;
317+
if (index == 0) {
318+
// Aggregrated timings for all buckets.
319+
Hdr1sfMicroSecHistogram aggregated{};
320+
for (const auto& bucket : all_buckets) {
321+
aggregated += bucket.*histogram;
322+
}
323+
json_str = aggregated.to_string();
324+
} else {
325+
// Timings for a specific bucket.
326+
auto& bucket = all_buckets[cookie.getConnection().getBucketIndex()];
327+
json_str = (bucket.*histogram).to_string();
328+
}
329+
append_stats({}, json_str, &cookie);
330+
return ENGINE_SUCCESS;
331+
} else {
332+
return ENGINE_EINVAL;
333+
}
334+
}
335+
306336
/**
307337
* Handler for the <code>stats topkeys</code> command used to retrieve
308338
* the most popular keys in the attached bucket.
@@ -354,6 +384,19 @@ static ENGINE_ERROR_CODE stat_topkeys_json_executor(const std::string& arg,
354384
}
355385
}
356386

387+
/**
388+
* Handler for the <code>stats snappy_decompress</code> command used to retrieve
389+
* histograms of how long it took to decompress Snappy compressed values.
390+
*
391+
* @param arg - should be empty
392+
* @param cookie the command context
393+
*/
394+
static ENGINE_ERROR_CODE stat_snappy_decompress_executor(const std::string& arg,
395+
Cookie& cookie) {
396+
return stat_histogram_executor(
397+
arg, cookie, &Bucket::snappyDecompressionTimes);
398+
}
399+
357400
/**
358401
* Handler for the <code>stats subdoc_execute</code> command used to retrieve
359402
* information from the subdoc subsystem.
@@ -363,26 +406,8 @@ static ENGINE_ERROR_CODE stat_topkeys_json_executor(const std::string& arg,
363406
*/
364407
static ENGINE_ERROR_CODE stat_subdoc_execute_executor(const std::string& arg,
365408
Cookie& cookie) {
366-
if (arg.empty()) {
367-
const auto index = cookie.getConnection().getBucketIndex();
368-
std::string json_str;
369-
if (index == 0) {
370-
// Aggregrated timings for all buckets.
371-
Hdr1sfMicroSecHistogram aggregated{};
372-
for (const auto& bucket : all_buckets) {
373-
aggregated += bucket.subjson_operation_times;
374-
}
375-
json_str = aggregated.to_string();
376-
} else {
377-
// Timings for a specific bucket.
378-
auto& bucket = all_buckets[cookie.getConnection().getBucketIndex()];
379-
json_str = bucket.subjson_operation_times.to_string();
380-
}
381-
append_stats({}, json_str, &cookie);
382-
return ENGINE_SUCCESS;
383-
} else {
384-
return ENGINE_EINVAL;
385-
}
409+
return stat_histogram_executor(
410+
arg, cookie, &Bucket::subjson_operation_times);
386411
}
387412

388413
static ENGINE_ERROR_CODE stat_responses_json_executor(const std::string& arg,
@@ -561,6 +586,8 @@ static std::unordered_map<std::string, struct command_stat_handler>
561586
{"topkeys", {false, true, true, stat_topkeys_executor}},
562587
{"topkeys_json",
563588
{false, true, true, stat_topkeys_json_executor}},
589+
{"snappy_decompress",
590+
{false, true, true, stat_snappy_decompress_executor}},
564591
{"subdoc_execute",
565592
{false, true, true, stat_subdoc_execute_executor}},
566593
{"responses",

0 commit comments

Comments
 (0)