Skip to content

Commit 8967c0b

Browse files
committed
MB-70483: Add expected duration for topkeys
And time out and clean up topkeys samples running too long Change-Id: I3a9fa7aff2cde0ae6e85a14bb16c66140f085e14 Reviewed-on: https://review.couchbase.org/c/kv_engine/+/239904 Reviewed-by: Jim Walker <jim@couchbase.com> Tested-by: Build Bot <build@couchbase.com>
1 parent 789a034 commit 8967c0b

File tree

6 files changed

+75
-13
lines changed

6 files changed

+75
-13
lines changed

daemon/connection.cc

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1034,6 +1034,11 @@ bool Connection::executeCommandsCallback() {
10341034
// Make sure any core dumps from this code contain the bucket name.
10351035
cb::DebugVariable bucketName(cb::toCharArrayN<32>(getBucket().name));
10361036
const auto start = last_used_timestamp = std::chrono::steady_clock::now();
1037+
1038+
if (thread.keyTrace && thread.keyTrace->is_expired(start)) {
1039+
thread.keyTrace.reset();
1040+
}
1041+
10371042
current_timeslice_end = start + Settings::instance().getCommandTimeSlice();
10381043

10391044
processBlockedSendQueue(start);

daemon/ioctl.cc

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -354,6 +354,23 @@ static cb::engine_errc ioctlSetTopkeysStart(Cookie& cookie,
354354
}
355355
}
356356

357+
std::size_t expected_duration = 60;
358+
if (args.contains("expected_duration")) {
359+
try {
360+
std::size_t val =
361+
std::stoul(args.find("expected_duration")->second) * 1.3;
362+
expected_duration = std::min(val, expected_duration);
363+
if (val == 0) {
364+
cookie.setErrorContext("expected_duration cannot be zero");
365+
return cb::engine_errc::invalid_arguments;
366+
}
367+
} catch (const std::exception&) {
368+
cookie.setErrorContext(
369+
"Failed to parse expected_duration argument");
370+
return cb::engine_errc::invalid_arguments;
371+
}
372+
}
373+
357374
std::vector<std::size_t> bucket_filter;
358375
if (args.contains("bucket_filter")) {
359376
std::unordered_map<std::string, std::size_t> bucketnames;
@@ -385,8 +402,11 @@ static cb::engine_errc ioctlSetTopkeysStart(Cookie& cookie,
385402
}
386403
}
387404

388-
auto collector =
389-
cb::trace::topkeys::Collector::create(limit, shards, bucket_filter);
405+
auto expiry_time = cb::time::steady_clock::now() +
406+
std::chrono::seconds(expected_duration);
407+
408+
auto collector = cb::trace::topkeys::Collector::create(
409+
limit, shards, expiry_time, bucket_filter);
390410
if (!collector) {
391411
cookie.setErrorContext("Failed to create topkeys collector");
392412
LOG_WARNING_CTX("Failed to create topkeys collector",

daemon/top_keys.cc

Lines changed: 15 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -75,8 +75,10 @@ void to_json(nlohmann::json& json, const Result& result) {
7575

7676
class CountingCollector : public Collector {
7777
public:
78-
CountingCollector(std::size_t max, std::size_t shards)
79-
: limit(max), shardmaps(shards) {
78+
CountingCollector(std::size_t max,
79+
std::size_t shards,
80+
cb::time::steady_clock::time_point expiry_time)
81+
: Collector(expiry_time), limit(max), shardmaps(shards) {
8082
}
8183

8284
void access(const size_t bucket,
@@ -209,8 +211,10 @@ class FilteredCountingCollector : public CountingCollector {
209211
public:
210212
FilteredCountingCollector(std::size_t max,
211213
std::size_t shards,
214+
cb::time::steady_clock::time_point expiry_time,
212215
std::vector<std::size_t> buckets)
213-
: CountingCollector(max, shards), bucketfilter(std::move(buckets)) {
216+
: CountingCollector(max, shards, expiry_time),
217+
bucketfilter(std::move(buckets)) {
214218
}
215219

216220
void access(const size_t bucket,
@@ -225,15 +229,18 @@ class FilteredCountingCollector : public CountingCollector {
225229
std::vector<std::size_t> bucketfilter;
226230
};
227231

228-
std::shared_ptr<Collector> Collector::create(std::size_t num_keys,
229-
std::size_t shards,
230-
std::vector<std::size_t> buckets) {
232+
std::shared_ptr<Collector> Collector::create(
233+
std::size_t num_keys,
234+
std::size_t shards,
235+
cb::time::steady_clock::time_point expiry_time,
236+
std::vector<std::size_t> buckets) {
231237
if (num_keys) {
232238
if (buckets.empty()) {
233-
return std::make_unique<CountingCollector>(num_keys, shards);
239+
return std::make_unique<CountingCollector>(
240+
num_keys, shards, expiry_time);
234241
}
235242
return std::make_unique<FilteredCountingCollector>(
236-
num_keys, shards, std::move(buckets));
243+
num_keys, shards, expiry_time, std::move(buckets));
237244
}
238245
return {};
239246
}

daemon/top_keys.h

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
#include "memcached/dockey_view.h"
1414

1515
#include <nlohmann/json_fwd.hpp>
16+
#include <platform/cb_time.h>
1617
#include <memory>
1718
#include <unordered_map>
1819
#include <vector>
@@ -51,6 +52,9 @@ class Collector {
5152
*
5253
* @param num_keys The maximum number of keys to track in a shard
5354
* @param shards The number of internal maps to shard the keyspace across
55+
* @param expiry_time The time after which the collected data should be
56+
* considered expired and can be discarded (and the
57+
* collector can be reset)
5458
* @param buckets If non-empty only track keys accessed in the specified
5559
* bucket. For space efficiencty we use the bucket *id*
5660
* and not the bucket name (as buckets typically don't
@@ -59,8 +63,18 @@ class Collector {
5963
static std::shared_ptr<Collector> create(
6064
std::size_t num_keys,
6165
std::size_t shards,
66+
cb::time::steady_clock::time_point expiry_time =
67+
cb::time::steady_clock::now() + std::chrono::minutes(1),
6268
std::vector<std::size_t> buckets = {});
6369

70+
/// Is this collector expired or not (e.g. should we discard the collected
71+
/// data). The time is passed in as an argument to avoid having to fetch the
72+
/// clock multiple times when we want to check multiple collectors (e.g. for
73+
/// each front end thread).
74+
bool is_expired(cb::time::steady_clock::time_point now) {
75+
return now >= expiry_time;
76+
}
77+
6478
/**
6579
* Register access for a key in a given bucket
6680
*
@@ -80,7 +94,8 @@ class Collector {
8094
virtual Result getResults(size_t limit) const = 0;
8195

8296
protected:
83-
Collector() = default;
97+
Collector(cb::time::steady_clock::time_point exp) : expiry_time(exp) {};
98+
const cb::time::steady_clock::time_point expiry_time;
8499
};
85100

86101
} // namespace cb::trace::topkeys

daemon/top_keys_test.cc

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,11 @@ TEST(TopKeyCollector, TestAccessNoFilter) {
7777
TEST(TopKeyCollector, testFiltered) {
7878
constexpr std::size_t num_shards = 4;
7979
constexpr std::size_t num_keys = 10;
80-
auto collector = Collector::create(num_keys, num_shards, {{0, 1}});
80+
auto collector = Collector::create(
81+
num_keys,
82+
num_shards,
83+
cb::time::steady_clock::now() + std::chrono::minutes(1),
84+
{{0, 1}});
8185
for (int ii = 0; ii < 10; ++ii) {
8286
collector->access(ii, false, fmt::format("key-{}", ii));
8387
}
@@ -154,3 +158,13 @@ TEST(TopKeyCollector, TestSingleShard) {
154158
auto& keys = bucket["cid:0x0"];
155159
ASSERT_EQ(100, keys.size()) << "json: " << keys.dump(2);
156160
}
161+
162+
TEST(TopKeyCollector, testExpiration) {
163+
cb::time::steady_clock::use_chrono = false;
164+
auto collector = Collector::create(
165+
1, 1, cb::time::steady_clock::now() + std::chrono::minutes(1));
166+
EXPECT_FALSE(collector->is_expired(cb::time::steady_clock::now()));
167+
cb::time::steady_clock::advance(std::chrono::minutes(2));
168+
EXPECT_TRUE(collector->is_expired(cb::time::steady_clock::now()));
169+
cb::time::steady_clock::use_chrono = true;
170+
}

programs/mctopkeys/mctopkeys.cc

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -191,8 +191,9 @@ int main(const int argc, char** argv) {
191191
}
192192

193193
const auto start_command = fmt::format(
194-
"topkeys.start?limit={}{}{}",
194+
"topkeys.start?limit={}&expected_duration={}{}{}",
195195
collect_limit,
196+
duration.count(),
196197
bucket_filter.empty()
197198
? ""
198199
: fmt::format("&bucket_filter={}", bucket_filter),

0 commit comments

Comments
 (0)