Skip to content

Commit 7f9f80f

Browse files
committed
MB-15009: Merge branch 'couchbase/vulcan' into 'couchbase/alice'
* couchbase/vulcan: MB-15009: Allow stored value age to be changed by cbepctl MB-15009: 3/3 Defragment HashTable - StoredValue MB-15009: 2/3 Refactor VBucketTest MB-15009: 1/3 Make frequency counter 8-bit Change-Id: I096d84e05d08e693b4ab961d3e9d98e74625d544
2 parents f775dfe + 4aef3a8 commit 7f9f80f

29 files changed

+528
-146
lines changed

engines/ep/benchmarks/defragmenter_bench.cc

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -101,9 +101,12 @@ class DefragmentBench : public benchmark::Fixture {
101101
std::chrono::milliseconds chunk_duration) {
102102
// Create and run visitor for the specified number of iterations, with
103103
// the given age.
104-
DefragmentVisitor visitor(age_threshold,
105-
DefragmenterTask::getMaxValueSize(
106-
get_mock_server_api()->alloc_hooks));
104+
DefragmentVisitor visitor(DefragmenterTask::getMaxValueSize(
105+
get_mock_server_api()->alloc_hooks));
106+
107+
visitor.setBlobAgeThreshold(age_threshold);
108+
visitor.setStoredValueAgeThreshold(age_threshold);
109+
107110
// Need to run 10 passes; so we allow the deframenter to defrag at
108111
// least once (given the age_threshold may be up to 10).
109112
const size_t passes = 10;

engines/ep/configuration.json

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -289,7 +289,7 @@
289289
"dynamic": false,
290290
"type": "std::string"
291291
},
292-
"dcp_noop_mandatory_for_v5_features": {
292+
"dcp_noop_mandatory_for_v5_features": {
293293
"default": "true",
294294
"descr": "Forces clients to enable noop for v5 features",
295295
"type": "bool"
@@ -307,7 +307,12 @@
307307
},
308308
"defragmenter_age_threshold": {
309309
"default": "10",
310-
"descr": "How old (measured in number of defragmenter passes) must a document be to be considered for degragmentation.",
310+
"descr": "How old (measured in number of DefragmenterVisitor passes) must a document be to be considered for defragmentation.",
311+
"type": "size_t"
312+
},
313+
"defragmenter_stored_value_age_threshold": {
314+
"default": "10",
315+
"descr": "How old (measured in number of DefragmenterVisitor passes) must a StoredValue be to be considered for defragmentation.",
311316
"type": "size_t"
312317
},
313318
"defragmenter_chunk_duration": {

engines/ep/docs/stats.org

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -300,6 +300,8 @@ For introductory information on stats within Couchbase, start with the
300300
| | run (in seconds). |
301301
| ep_defragmenter_num_moved | Number of items moved by the |
302302
| | defragmentater task. |
303+
| ep_defragmenter_sv_num_moved | Number of StoredValues moved by the |
304+
| | defragmentater task. |
303305
| ep_defragmenter_num_visited | Number of items visited (considered |
304306
| | for defragmentation) by the |
305307
| | defragmenter task. |

engines/ep/management/cbepctl

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -188,6 +188,9 @@ Available params for "set":
188188
defragmenter_age_threshold - How old (measured in number of defragmenter
189189
passes) must a document be to be considered
190190
for defragmentation.
191+
defragmenter_stored_value_age_threshold - How old (measured in number of defragmenter
192+
passes) must a StoredValue (key + meta) be to be considered
193+
for defragmentation.
191194
defragmenter_chunk_duration - Maximum time (in ms) defragmentation task
192195
will run for before being paused (and
193196
resumed at the next defragmenter_interval).

engines/ep/src/defragmenter.cc

Lines changed: 24 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -40,9 +40,11 @@ bool DefragmenterTask::run(void) {
4040
// then resume from where we last were, otherwise create a new visitor
4141
// starting from the beginning.
4242
if (!prAdapter) {
43-
prAdapter = std::make_unique<PauseResumeVBAdapter>(
44-
std::make_unique<DefragmentVisitor>(
45-
getAgeThreshold(), getMaxValueSize(alloc_hooks)));
43+
auto visitor = std::make_unique<DefragmentVisitor>(
44+
getMaxValueSize(alloc_hooks));
45+
46+
prAdapter =
47+
std::make_unique<PauseResumeVBAdapter>(std::move(visitor));
4648
epstore_position = engine->getKVBucket()->startPosition();
4749
}
4850

@@ -70,6 +72,13 @@ bool DefragmenterTask::run(void) {
7072
const auto start = ProcessClock::now();
7173
const auto deadline = start + getChunkDuration();
7274
visitor.setDeadline(deadline);
75+
visitor.setBlobAgeThreshold(getAgeThreshold());
76+
// Only defragment StoredValues of persistent buckets because the
77+
// HashTable defrag method doesn't yet know how to maintain the
78+
// ephemeral seqno linked-list
79+
if (engine->getConfiguration().getBucketType() == "persistent") {
80+
visitor.setStoredValueAgeThreshold(getStoredValueAgeThreshold());
81+
}
7382
visitor.clearStats();
7483

7584
// Do it - set off the visitor.
@@ -80,9 +89,7 @@ bool DefragmenterTask::run(void) {
8089
// Defrag complete. Restore thread caching.
8190
alloc_hooks->enable_thread_cache(old_tcache);
8291

83-
// Update stats
84-
stats.defragNumMoved.fetch_add(visitor.getDefragCount());
85-
stats.defragNumVisited.fetch_add(visitor.getVisitedCount());
92+
updateStats(visitor);
8693

8794
// Release any free memory we now have in the allocator back to the OS.
8895
// TODO: Benchmark this - is it necessary? How much of a slowdown does it
@@ -153,6 +160,17 @@ size_t DefragmenterTask::getAgeThreshold() const {
153160
return engine->getConfiguration().getDefragmenterAgeThreshold();
154161
}
155162

163+
size_t DefragmenterTask::getStoredValueAgeThreshold() const {
164+
return engine->getConfiguration().getDefragmenterStoredValueAgeThreshold();
165+
}
166+
167+
void DefragmenterTask::updateStats(DefragmentVisitor& visitor) {
168+
stats.defragNumMoved.fetch_add(visitor.getDefragCount());
169+
stats.defragStoredValueNumMoved.fetch_add(
170+
visitor.getStoredValueDefragCount());
171+
stats.defragNumVisited.fetch_add(visitor.getVisitedCount());
172+
}
173+
156174
size_t DefragmenterTask::getMaxValueSize(ALLOCATOR_HOOKS_API* alloc_hooks) {
157175
size_t nbins{0};
158176
alloc_hooks->get_allocator_property("arenas.nbins", &nbins);

engines/ep/src/defragmenter.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,10 @@ class DefragmenterTask : public GlobalTask {
123123
// must be to be considered for defragmentation.
124124
size_t getAgeThreshold() const;
125125

126+
// Minimum age (measured in defragmenter task passes) that a StoredValue
127+
// must be to be considered for defragmentation.
128+
size_t getStoredValueAgeThreshold() const;
129+
126130
// Upper limit on how long each defragmention chunk can run for, before
127131
// being paused.
128132
std::chrono::milliseconds getChunkDuration() const;
@@ -133,6 +137,9 @@ class DefragmenterTask : public GlobalTask {
133137
/// Returns the underlying DefragmentVisitor instance.
134138
DefragmentVisitor& getDefragVisitor();
135139

140+
/// Update the EPStats from the visitor
141+
void updateStats(DefragmentVisitor& visitor);
142+
136143
/// Reference to EP stats, used to check on mem_used.
137144
EPStats &stats;
138145

engines/ep/src/defragmenter_visitor.cc

Lines changed: 29 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,10 +19,8 @@
1919

2020
// DegragmentVisitor implementation ///////////////////////////////////////////
2121

22-
DefragmentVisitor::DefragmentVisitor(uint8_t age_threshold_,
23-
size_t max_size_class)
22+
DefragmentVisitor::DefragmentVisitor(size_t max_size_class)
2423
: max_size_class(max_size_class),
25-
age_threshold(age_threshold_),
2624
defrag_count(0),
2725
visited_count(0),
2826
currentVb(nullptr) {
@@ -35,6 +33,14 @@ void DefragmentVisitor::setDeadline(ProcessClock::time_point deadline) {
3533
progressTracker.setDeadline(deadline);
3634
}
3735

36+
void DefragmentVisitor::setBlobAgeThreshold(uint8_t age) {
37+
age_threshold = age;
38+
}
39+
40+
void DefragmentVisitor::setStoredValueAgeThreshold(uint8_t age) {
41+
sv_age_threshold = age;
42+
}
43+
3844
bool DefragmentVisitor::visit(const HashTable::HashBucketLock& lh,
3945
StoredValue& v) {
4046
const size_t value_len = v.valuelen();
@@ -57,6 +63,15 @@ bool DefragmentVisitor::visit(const HashTable::HashBucketLock& lh,
5763
v.getValue()->incrementAge();
5864
}
5965
}
66+
67+
if (sv_age_threshold) {
68+
if (v.getAge() >= sv_age_threshold.get()) {
69+
defragmentStoredValue(v);
70+
} else {
71+
v.incrementAge();
72+
}
73+
}
74+
6075
visited_count++;
6176

6277
// See if we have done enough work for this chunk. If so
@@ -67,6 +82,7 @@ bool DefragmentVisitor::visit(const HashTable::HashBucketLock& lh,
6782
void DefragmentVisitor::clearStats() {
6883
defrag_count = 0;
6984
visited_count = 0;
85+
sv_defrag_count = 0;
7086
}
7187

7288
size_t DefragmentVisitor::getDefragCount() const {
@@ -77,6 +93,16 @@ size_t DefragmentVisitor::getVisitedCount() const {
7793
return visited_count;
7894
}
7995

96+
size_t DefragmentVisitor::getStoredValueDefragCount() const {
97+
return sv_defrag_count;
98+
}
99+
80100
void DefragmentVisitor::setCurrentVBucket(VBucket& vb) {
81101
currentVb = &vb;
82102
}
103+
104+
void DefragmentVisitor::defragmentStoredValue(StoredValue& v) const {
105+
if (currentVb->ht.reallocateStoredValue(std::forward<StoredValue>(v))) {
106+
sv_defrag_count++;
107+
}
108+
}

engines/ep/src/defragmenter_visitor.h

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,13 +31,24 @@
3131
*/
3232
class DefragmentVisitor : public VBucketAwareHTVisitor {
3333
public:
34-
DefragmentVisitor(uint8_t age_threshold_, size_t max_size_class);
34+
DefragmentVisitor(size_t max_size_class);
3535

3636
~DefragmentVisitor();
3737

3838
// Set the deadline at which point the visitor will pause visiting.
3939
void setDeadline(ProcessClock::time_point deadline_);
4040

41+
/**
42+
* Set the age at which Blobs are defragged 0 - 255
43+
*/
44+
void setBlobAgeThreshold(uint8_t age);
45+
46+
/**
47+
* Set the age at which StoredValues are defragged - by default SV
48+
* defragging is off until an age is set (0-255)
49+
*/
50+
void setStoredValueAgeThreshold(uint8_t age);
51+
4152
// Implementation of HashTableVisitor interface:
4253
virtual bool visit(const HashTable::HashBucketLock& lh,
4354
StoredValue& v) override;
@@ -51,16 +62,22 @@ class DefragmentVisitor : public VBucketAwareHTVisitor {
5162
// Returns the number of documents that have been visited.
5263
size_t getVisitedCount() const;
5364

65+
// Returns the number of StoredValues that have been defragmented.
66+
size_t getStoredValueDefragCount() const;
67+
5468
void setCurrentVBucket(VBucket& vb) override;
5569

5670
private:
71+
/// Request to reallocate the StoredValue
72+
void defragmentStoredValue(StoredValue& v) const;
73+
5774
/* Configuration parameters */
5875

5976
// Size of the largest size class from the allocator.
6077
const size_t max_size_class;
6178

6279
// How old a blob must be to consider it for defragmentation.
63-
const uint8_t age_threshold;
80+
uint8_t age_threshold{0};
6481

6582
/* Runtime state */
6683

@@ -72,7 +89,12 @@ class DefragmentVisitor : public VBucketAwareHTVisitor {
7289
size_t defrag_count;
7390
// How many documents have been visited.
7491
size_t visited_count;
92+
// How many stored-values have been defrag'd
93+
mutable size_t sv_defrag_count{0};
7594

7695
// The current vbucket that is being processed
7796
VBucket* currentVb;
97+
98+
// If defined, the age at which StoredValue's are de-fragmented
99+
boost::optional<uint8_t> sv_age_threshold;
78100
};

engines/ep/src/ep_engine.cc

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -592,6 +592,10 @@ protocol_binary_response_status EventuallyPersistentEngine::setFlushParam(
592592
std::stoull(valz));
593593
} else if (strcmp(keyz, "defragmenter_age_threshold") == 0) {
594594
getConfiguration().setDefragmenterAgeThreshold(std::stoull(valz));
595+
} else if (strcmp(keyz, "defragmenter_stored_value_age_threshold") ==
596+
0) {
597+
getConfiguration().setDefragmenterStoredValueAgeThreshold(
598+
std::stoull(valz));
595599
} else if (strcmp(keyz, "defragmenter_chunk_duration") == 0) {
596600
getConfiguration().setDefragmenterChunkDuration(std::stoull(valz));
597601
} else if (strcmp(keyz, "defragmenter_run") == 0) {
@@ -2923,6 +2927,10 @@ ENGINE_ERROR_CODE EventuallyPersistentEngine::doEngineStats(const void *cookie,
29232927
add_stat, cookie);
29242928
add_casted_stat("ep_defragmenter_num_moved", epstats.defragNumMoved,
29252929
add_stat, cookie);
2930+
add_casted_stat("ep_defragmenter_sv_num_moved",
2931+
epstats.defragStoredValueNumMoved,
2932+
add_stat,
2933+
cookie);
29262934

29272935
add_casted_stat("ep_item_compressor_num_visited",
29282936
epstats.compressorNumVisited,

engines/ep/src/hash_table.cc

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -569,6 +569,21 @@ MutationStatus HashTable::insertFromWarmup(
569569
return MutationStatus::NotFound;
570570
}
571571

572+
bool HashTable::reallocateStoredValue(StoredValue&& sv) {
573+
// Search the chain and reallocate
574+
for (StoredValue::UniquePtr* curr =
575+
&values[getBucketForHash(sv.getKey().hash())];
576+
curr->get().get();
577+
curr = &curr->get()->getNext()) {
578+
if (&sv == curr->get().get()) {
579+
auto newSv = valFact->copyStoredValue(sv, std::move(sv.getNext()));
580+
curr->swap(newSv);
581+
return true;
582+
}
583+
}
584+
return false;
585+
}
586+
572587
void HashTable::dump() const {
573588
std::cerr << *this << std::endl;
574589
}

0 commit comments

Comments
 (0)