Skip to content

Commit 46152ca

Browse files
committed
MB-27554: [BP] Don't over-count VBucket item counts
Originally 526b3f4 TODO: Check what happens in FE if an item is created and deleted in the same flusher step - I believe the create will be de-duplicated with the delete; so only the delete persistence callback will run. In that case I think the item count may be incorrect - or at the very least it may require newCacheItem to handle it correctly. Change-Id: Id131081e9e80436fcc861ce9f917be040d74b430 Reviewed-on: http://review.couchbase.org/88380 Well-Formed: Build Bot <[email protected]> Tested-by: Build Bot <[email protected]> Reviewed-by: Dave Rigby <[email protected]>
1 parent 1ff902e commit 46152ca

File tree

12 files changed

+94
-58
lines changed

12 files changed

+94
-58
lines changed

engines/ep/src/ep_bucket.cc

Lines changed: 22 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -596,12 +596,16 @@ ENGINE_ERROR_CODE EPBucket::scheduleCompaction(uint16_t vbid,
596596
void EPBucket::flushOneDeleteAll() {
597597
for (VBucketMap::id_type i = 0; i < vbMap.getSize(); ++i) {
598598
VBucketPtr vb = getVBucket(i);
599+
if (!vb) {
600+
continue;
601+
}
599602
// Reset the vBucket if it's non-null and not already in the middle of
600603
// being created / destroyed.
601-
if (vb && !(vb->isBucketCreation() || vb->isDeletionDeferred())) {
602-
LockHolder lh(vb_mutexes[vb->getId()]);
604+
if (!(vb->isBucketCreation() || vb->isDeletionDeferred())) {
603605
getRWUnderlying(vb->getId())->reset(i);
604606
}
607+
// Reset disk item count.
608+
vb->ht.setNumTotalItems(0);
605609
}
606610

607611
--stats.diskQueueSize;
@@ -909,12 +913,7 @@ class EPDiskRollbackCB : public RollbackCB {
909913
if (gcb.getStatus() == ENGINE_SUCCESS) {
910914
UniqueItemPtr it(std::move(gcb.item));
911915
if (it->isDeleted()) {
912-
bool ret = vb->deleteKey(it->getKey());
913-
if (!ret) {
914-
setStatus(ENGINE_KEY_ENOENT);
915-
} else {
916-
setStatus(ENGINE_SUCCESS);
917-
}
916+
removeDeletedDoc(*vb, it->getKey());
918917
} else {
919918
MutationStatus mtype = vb->setFromInternal(*it);
920919

@@ -923,19 +922,28 @@ class EPDiskRollbackCB : public RollbackCB {
923922
}
924923
}
925924
} else if (gcb.getStatus() == ENGINE_KEY_ENOENT) {
926-
bool ret = vb->deleteKey(itm->getKey());
927-
if (!ret) {
928-
setStatus(ENGINE_KEY_ENOENT);
929-
} else {
930-
setStatus(ENGINE_SUCCESS);
931-
}
925+
removeDeletedDoc(*vb, itm->getKey());
932926
} else {
933927
LOG(EXTENSION_LOG_WARNING,
934928
"EPDiskRollbackCB::callback:Unexpected Error Status: %d",
935929
gcb.getStatus());
936930
}
937931
}
938932

933+
/// Remove a deleted-on-disk document from the VBucket's hashtable.
934+
void removeDeletedDoc(VBucket& vb, const DocKey& key) {
935+
if (vb.deleteKey(key)) {
936+
setStatus(ENGINE_SUCCESS);
937+
} else {
938+
// Document didn't exist in memory - may have been deleted in since
939+
// the checkpoint.
940+
setStatus(ENGINE_KEY_ENOENT);
941+
}
942+
// Irrespective of if the in-memory delete succeeded; the document
943+
// doesn't exist on disk; so decrement the item count.
944+
vb.ht.decrNumTotalItems();
945+
}
946+
939947
private:
940948
EventuallyPersistentEngine& engine;
941949
};

engines/ep/src/ep_vb.cc

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -257,15 +257,15 @@ size_t EPVBucket::getNumItems() const {
257257
if (eviction == VALUE_ONLY) {
258258
return ht.getNumInMemoryItems() - ht.getNumDeletedItems();
259259
} else {
260-
return ht.getNumItems() - ht.getNumDeletedItems();
260+
return ht.getNumTotalItems() - ht.getNumDeletedItems();
261261
}
262262
}
263263

264264
size_t EPVBucket::getNumNonResidentItems() const {
265265
if (eviction == VALUE_ONLY) {
266266
return ht.getNumInMemoryNonResItems();
267267
} else {
268-
size_t num_items = ht.getNumItems();
268+
size_t num_items = ht.getNumTotalItems();
269269
size_t num_res_items =
270270
ht.getNumInMemoryItems() - ht.getNumInMemoryNonResItems();
271271
return num_items > num_res_items ? (num_items - num_res_items) : 0;

engines/ep/src/hash_table.cc

Lines changed: 0 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -324,7 +324,6 @@ void HashTable::statsPrologue(const StoredValue& v) {
324324
--numTempItems;
325325
} else {
326326
--numItems;
327-
--numTotalItems;
328327
if (v.isDeleted()) {
329328
--numDeletedItems;
330329
} else {
@@ -346,7 +345,6 @@ void HashTable::statsEpilogue(const StoredValue& v) {
346345
++numTempItems;
347346
} else {
348347
++numItems;
349-
++numTotalItems;
350348
if (v.isDeleted()) {
351349
++numDeletedItems;
352350
} else {
@@ -475,9 +473,6 @@ MutationStatus HashTable::insertFromWarmup(
475473
v->markNotResident();
476474
++numNonResidentItems;
477475
}
478-
/* We need to decrNumTotalItems because ht.numTotalItems is already
479-
set by warmup when it estimated the item count from disk */
480-
decrNumTotalItems();
481476
v->setNewCacheItem(false);
482477
} else {
483478
if (keyMetaDataOnly) {
@@ -746,12 +741,6 @@ bool HashTable::unlocked_restoreValue(
746741

747742
if (v.isDeleted()) {
748743
++numDeletedItems;
749-
// We have restored a deleted item. This doesn't count as an alive
750-
// item, however it does contribute to the total number of documents
751-
// in the HashTable, hence we need to also increment numTotalItems.
752-
// Note: at the vBucket level we determine the number of (alive) items
753-
// as (numTotal - numDeleted).
754-
++numTotalItems;
755744
}
756745

757746
increaseCacheSize(v.getValue()->length());

engines/ep/src/hash_table.h

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -222,21 +222,27 @@ class HashTable {
222222
/**
223223
* Get the number of non-resident and resident items managed by
224224
* this hash table. Includes items marked as deleted.
225-
* Note that this will be equal to numItems if
226-
* VALUE_ONLY_EVICTION is chosen as a cache management.
227225
*/
228-
size_t getNumItems(void) const {
229-
return numTotalItems;
226+
size_t getNumItems() const {
227+
return numItems;
230228
}
231229

232230
void setNumTotalItems(size_t totalItems) {
233231
numTotalItems = totalItems;
234232
}
235233

234+
size_t getNumTotalItems() const {
235+
return numTotalItems;
236+
}
237+
236238
void decrNumItems() {
237239
--numItems;
238240
}
239241

242+
void incrNumTotalItems() {
243+
++numTotalItems;
244+
}
245+
240246
void decrNumTotalItems() {
241247
--numTotalItems;
242248
}

engines/ep/src/persistence_callback.cc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,9 +51,9 @@ void PersistenceCallback::callback(mutation_result& value) {
5151
if (value.second) {
5252
// Insert in value-only or full eviction mode.
5353
++vbucket->opsCreate;
54+
vbucket->ht.incrNumTotalItems();
5455
vbucket->incrMetaDataDisk(*queuedItem);
5556
} else { // Update in full eviction mode.
56-
vbucket->ht.decrNumTotalItems();
5757
++vbucket->opsUpdate;
5858
}
5959

engines/ep/src/stored-value.cc

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -290,6 +290,13 @@ bool StoredValue::deleteImpl() {
290290
}
291291

292292
void StoredValue::setValueImpl(const Item& itm) {
293+
if (isDeleted() && !itm.isDeleted()) {
294+
// Transitioning from deleted -> alive - this should be considered
295+
// a new cache item as it is increasing the number of (alive) items
296+
// in the vBucket.
297+
setNewCacheItem(true);
298+
}
299+
293300
setDeletedPriv(itm.isDeleted());
294301
flags = itm.getFlags();
295302
datatype = itm.getDataType();

engines/ep/src/vbucket.cc

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1870,7 +1870,6 @@ void VBucket::deletedOnDiskCbk(const Item& queuedItem, bool deleted) {
18701870
// 1. Item is existent in hashtable, and deleted flag is true
18711871
// 2. rev seqno of queued item matches rev seqno of hash table item
18721872
if (v && v->isDeleted() && (queuedItem.getRevSeqno() == v->getRevSeqno())) {
1873-
bool newCacheItem = v->isNewCacheItem();
18741873
bool isDeleted = deleteStoredValue(hbl, *v);
18751874
if (!isDeleted) {
18761875
throw std::logic_error(
@@ -1879,11 +1878,8 @@ void VBucket::deletedOnDiskCbk(const Item& queuedItem, bool deleted) {
18791878
std::to_string(v->getBySeqno()) + "' from bucket " +
18801879
std::to_string(hbl.getBucketNum()));
18811880
}
1882-
if (newCacheItem && deleted) {
1883-
// Need to decrement the item counter again for an item that
1884-
// exists on DB file, but not in memory (i.e., full eviction),
1885-
// because we created the temp item in memory and incremented
1886-
// the item counter when a deletion is pushed in the queue.
1881+
if (deleted) {
1882+
// Removed an item from disk - decrement the count of total items.
18871883
ht.decrNumTotalItems();
18881884
}
18891885

engines/ep/tests/ep_testsuite.cc

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -253,6 +253,7 @@ static int checkCurrItemsAfterShutdown(ENGINE_HANDLE *h, ENGINE_HANDLE_V1 *h1,
253253
keys.push_back(key);
254254
}
255255

256+
// Check preconditions.
256257
checkeq(0, get_int_stat(h, h1, "ep_total_persisted"),
257258
"Expected ep_total_persisted equals 0");
258259
checkeq(0, get_int_stat(h, h1, "curr_items"),
@@ -279,11 +280,15 @@ static int checkCurrItemsAfterShutdown(ENGINE_HANDLE *h, ENGINE_HANDLE_V1 *h1,
279280

280281
checkeq(0, get_int_stat(h, h1, "ep_total_persisted"),
281282
"Incorrect ep_total_persisted, expected 0");
282-
std::stringstream ss;
283-
ss << "Incorrect curr_items, expected " << numItems2Load;
284-
std::string errmsg(ss.str());
285-
checkeq(numItems2Load, get_int_stat(h, h1, "curr_items"),
286-
errmsg.c_str());
283+
284+
// Can only check curr_items in value_only eviction; full-eviction
285+
// relies on persistence to complete (via flusher) to update count.
286+
const auto evictionPolicy = get_str_stat(h, h1, "ep_item_eviction_policy");
287+
if (evictionPolicy == "value_only") {
288+
checkeq(numItems2Load,
289+
get_int_stat(h, h1, "curr_items"),
290+
"Expected curr_items to reflect item count");
291+
}
287292

288293
// resume flusher before shutdown + warmup
289294
pkt = createPacket(PROTOCOL_BINARY_CMD_START_PERSISTENCE);

engines/ep/tests/ep_testsuite_dcp.cc

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2034,7 +2034,6 @@ static enum test_result test_dcp_producer_stream_req_backfill(ENGINE_HANDLE *h,
20342034
write_items(h, h1, batch_items, start_seqno);
20352035
}
20362036

2037-
verify_curr_items(h, h1, num_items, "Wrong amount of items");
20382037
wait_for_stat_to_be_gte(h, h1, "vb_0:num_checkpoints", 2, "checkpoint");
20392038

20402039
const void *cookie = testHarness.create_cookie();
@@ -4413,14 +4412,11 @@ static enum test_result test_dcp_consumer_mutate(ENGINE_HANDLE *h,
44134412

44144413
static enum test_result test_dcp_consumer_delete(ENGINE_HANDLE *h, ENGINE_HANDLE_V1 *h1) {
44154414
// Store an item
4416-
item *i = NULL;
44174415
checkeq(ENGINE_SUCCESS,
4418-
store(h, h1, NULL, OPERATION_ADD,"key", "value", &i),
4416+
store(h, h1, NULL, OPERATION_ADD, "key", "value", nullptr),
44194417
"Failed to fail to store an item.");
4420-
h1->release(h, NULL, i);
4421-
verify_curr_items(h, h1, 1, "one item stored");
4422-
44234418
wait_for_flusher_to_settle(h, h1);
4419+
verify_curr_items(h, h1, 1, "one item stored");
44244420

44254421
check(set_vbucket_state(h, h1, 0, vbucket_state_replica),
44264422
"Failed to set vbucket state.");

engines/ep/tests/ep_testsuite_xdcr.cc

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -296,6 +296,7 @@ static enum test_result test_get_meta_with_set(ENGINE_HANDLE *h, ENGINE_HANDLE_V
296296
checkeq(ENGINE_SUCCESS,
297297
store(h, h1, NULL, OPERATION_SET, key1, "someothervalue", &i),
298298
"Failed set.");
299+
wait_for_flusher_to_settle(h, h1);
299300

300301
checkeq(1, get_int_stat(h, h1, "curr_items"), "Expected single curr_items");
301302
checkeq(0, get_int_stat(h, h1, "curr_temp_items"), "Expected zero temp_items");
@@ -944,6 +945,7 @@ static enum test_result test_set_with_meta(ENGINE_HANDLE *h, ENGINE_HANDLE_V1 *h
944945
checkeq(ENGINE_SUCCESS,
945946
store(h, h1, NULL, OPERATION_SET, key, val, &i),
946947
"Failed set.");
948+
wait_for_flusher_to_settle(h, h1);
947949

948950
// get metadata for the key
949951
check(get_meta(h, h1, key), "Expected to get meta");
@@ -1119,6 +1121,8 @@ static enum test_result test_set_with_meta_deleted(ENGINE_HANDLE *h, ENGINE_HAND
11191121
// do set with meta with the correct cas value. should pass.
11201122
set_with_meta(h, h1, key, keylen, newVal, newValLen, 0, &itm_meta, cas_for_set);
11211123
checkeq(PROTOCOL_BINARY_RESPONSE_SUCCESS, last_status.load(), "Expected success");
1124+
wait_for_flusher_to_settle(h, h1);
1125+
11221126
// check the stat
11231127
checkeq(1, get_int_stat(h, h1, "ep_num_ops_set_meta"), "Expect some ops");
11241128
checkeq(0, get_int_stat(h, h1, "ep_num_ops_get_meta_on_set_meta"),
@@ -1172,6 +1176,8 @@ static enum test_result test_set_with_meta_nonexistent(ENGINE_HANDLE *h, ENGINE_
11721176
// do set with meta with the correct cas value. should pass.
11731177
set_with_meta(h, h1, key, keylen, val, valLen, 0, &itm_meta, cas_for_set);
11741178
checkeq(PROTOCOL_BINARY_RESPONSE_SUCCESS, last_status.load(), "Expected success");
1179+
wait_for_flusher_to_settle(h, h1);
1180+
11751181
// check the stat
11761182
checkeq(1, get_int_stat(h, h1, "ep_num_ops_set_meta"), "Expect some ops");
11771183
checkeq(1, get_int_stat(h, h1, "curr_items"), "Expected single curr_items");

0 commit comments

Comments
 (0)