Skip to content

Commit 04480b9

Browse files
jimwwalkerdaverigby
authored andcommitted
[BP] MB-59368: Use getPageableMemCurrent/MemLowWatermark in PagingVisitor
The MB highlighted that for ephemeral auto_delete, paging can be triggered as the trigger uses getPageableMemCurrent/MemLowWatermark but the PagingVisitor immediately cancels paging because it compared getEstimatedTotalMemoryUsed vs stats.mem_low_wat, nothing is freed and paging is immediately triggered to run again - resulting in a noticeable increase in CPU but no memory reduction. Commit changes PagingVisitor to use the Pageable functions when deciding to stop, memory is now released and no further paging is triggered. Conflicts: engines/ep/src/paging_visitor.cc engines/ep/tests/module_tests/item_pager_test.cc Not a clean backport and required some dependent test-code backporting. Change-Id: Ic83764c693cc35d4e0cc9b36c7aae8cb98e4bb30 Reviewed-on: https://review.couchbase.org/c/kv_engine/+/201918 Reviewed-by: Dave Rigby <[email protected]> Well-Formed: Restriction Checker Tested-by: Dave Rigby <[email protected]>
1 parent ea3be68 commit 04480b9

File tree

6 files changed

+95
-2
lines changed

6 files changed

+95
-2
lines changed

engines/ep/src/ephemeral_vb.cc

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -547,6 +547,8 @@ EphemeralVBucket::softDeleteStoredValue(const HashTable::HashBucketLock& hbl,
547547
const VBQueueItemCtx& queueItmCtx,
548548
uint64_t bySeqno,
549549
DeleteSource deleteSource) {
550+
softDeleteStoredValueHook(getStateLock());
551+
550552
std::lock_guard<std::mutex> lh(sequenceLock);
551553

552554
StoredValue* newSv = &v;

engines/ep/src/paging_visitor.cc

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -199,8 +199,8 @@ void PagingVisitor::visitBucket(VBucket& vb) {
199199
}
200200

201201
// skip active vbuckets if active resident ratio is lower than replica
202-
auto current = static_cast<double>(stats.getEstimatedTotalMemoryUsed());
203-
auto lower = static_cast<double>(stats.mem_low_wat);
202+
auto current = static_cast<double>(store.getPageableMemCurrent());
203+
auto lower = static_cast<double>(store.getPageableMemLowWatermark());
204204

205205
if (current <= lower) {
206206
// stop eviction whenever memory usage is below low watermark

engines/ep/src/vbucket.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2524,6 +2524,8 @@ class VBucket : public std::enable_shared_from_this<VBucket> {
25242524
*/
25252525
const SyncWriteTimeoutHandlerFactory syncWriteTimeoutFactory;
25262526

2527+
TestingHook<folly::SharedMutex&> softDeleteStoredValueHook;
2528+
25272529
private:
25282530
/**
25292531
* The replication topology, set as part of SET_VBUCKET_STATE.

engines/ep/tests/module_tests/evp_store_single_threaded_test.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -728,6 +728,13 @@ class STParameterizedBucketTest
728728
"item_eviction_policy=full_eviction"s);
729729
}
730730

731+
static auto ephAutoDeleteConfigValues() {
732+
using namespace std::string_literals;
733+
return ::testing::Values(
734+
"bucket_type=ephemeral:"
735+
"ephemeral_full_policy=auto_delete"s);
736+
}
737+
731738
bool persistent() const {
732739
return GetParam().find("persistent") != std::string::npos;
733740
}

engines/ep/tests/module_tests/item_pager_test.cc

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
#include "test_helpers.h"
3030
#include "tests/mock/mock_synchronous_ep_engine.h"
3131
#include "vbucket.h"
32+
#include "vbucket_utils.h"
3233

3334
#include <folly/portability/GTest.h>
3435
#include <platform/cb_arena_malloc.h>
@@ -1573,6 +1574,75 @@ TEST_P(STEphemeralItemPagerTest, ReplicaNotPaged) {
15731574
}
15741575
}
15751576

1577+
/**
1578+
* Test fixture for Ephemeral item pager tests with
1579+
* ephemeral_full_policy=auto_delete.
1580+
*/
1581+
class STEphemeralAutoDeleteItemPagerTest : public STItemPagerTest {};
1582+
1583+
// Test reproduced MB-59368. This was an issue where memory reached the
1584+
// threshold to trigger the pager (which will delete data), but code in the
1585+
// paging visitor was checking different values and stopped the pager. The pager
1586+
// would then just keep getting triggered leading to noticeable CPU increase
1587+
// and no memory reduction.
1588+
TEST_P(STEphemeralAutoDeleteItemPagerTest, MB_59368) {
1589+
// Need two vbuckets and one must be replica so that we account the replica
1590+
// memory usage which will affect the result of getPageableMemCurrent
1591+
const Vbid activeVbid = vbid;
1592+
const Vbid replicaVbid(1);
1593+
1594+
setVBucketState(replicaVbid, vbucket_state_replica);
1595+
1596+
auto setReplica = [this](Vbid replicaVbid, int count) {
1597+
auto key = makeStoredDocKey("replica_" + std::to_string(count));
1598+
auto item = make_item(replicaVbid, key, "value");
1599+
item.setCas(1 + count);
1600+
uint64_t seqno;
1601+
ASSERT_EQ(cb::engine_errc::success,
1602+
store->setWithMeta(item,
1603+
0,
1604+
&seqno,
1605+
cookie,
1606+
{vbucket_state_replica},
1607+
CheckConflicts::No,
1608+
true,
1609+
GenerateBySeqno::Yes,
1610+
GenerateCas::Yes));
1611+
};
1612+
1613+
auto setActive = [this](Vbid activeVbid, int count) {
1614+
auto key = makeStoredDocKey("active_" + std::to_string(count));
1615+
auto item = make_item(activeVbid, key, "value");
1616+
item.setFreqCounterValue(0);
1617+
storeItem(item);
1618+
};
1619+
1620+
auto& stats = engine->getEpStats();
1621+
int count = 0;
1622+
do {
1623+
// Write the active last as the path it executes will do the memory
1624+
// check that schedules the pager.
1625+
setReplica(replicaVbid, count);
1626+
setActive(activeVbid, count);
1627+
++count;
1628+
} while (store->getPageableMemCurrent() <
1629+
store->getPageableMemHighWatermark());
1630+
1631+
// This is the condition which produced the issue.
1632+
ASSERT_LT(stats.getPreciseTotalMemoryUsed(), stats.mem_low_wat);
1633+
1634+
auto& vb = *store->getVBucket(activeVbid);
1635+
bool softDeleteCalled{false};
1636+
VBucketTestIntrospector::setSoftDeleteStoredValueHook(
1637+
vb, {[&softDeleteCalled](folly::SharedMutex& vbStateLock) {
1638+
softDeleteCalled = true;
1639+
}});
1640+
1641+
// Run the pager and check that items got deleted.
1642+
runHighMemoryPager();
1643+
EXPECT_TRUE(softDeleteCalled) << "Nothing was deleted";
1644+
}
1645+
15761646
/**
15771647
* Test fixture for expiry pager tests - enables the Expiry Pager (in addition
15781648
* to what the parent class does).
@@ -2684,6 +2754,11 @@ INSTANTIATE_TEST_SUITE_P(PersistentFullValue,
26842754
STParameterizedBucketTest::persistentConfigValues(),
26852755
STParameterizedBucketTest::PrintToStringParamName);
26862756

2757+
INSTANTIATE_TEST_SUITE_P(EphemeralAutoDelete,
2758+
STEphemeralAutoDeleteItemPagerTest,
2759+
STParameterizedBucketTest::ephAutoDeleteConfigValues(),
2760+
STParameterizedBucketTest::PrintToStringParamName);
2761+
26872762
#else
26882763
GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(STItemPagerTest);
26892764
GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(MultiPagingVisitorTest);
@@ -2693,4 +2768,6 @@ GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(STValueEvictionExpiryPagerTest);
26932768
GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(MB_32669);
26942769
GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(STEphemeralItemPagerTest);
26952770
GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(MB_36087);
2771+
GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(
2772+
STEphemeralAutoDeleteItemPagerTest);
26962773
#endif

engines/ep/tests/module_tests/vbucket_utils.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,4 +38,9 @@ class VBucketTestIntrospector {
3838
static void destroyDM(VBucket& vb) {
3939
vb.durabilityMonitor.reset();
4040
}
41+
42+
static void setSoftDeleteStoredValueHook(
43+
VBucket& vb, TestingHook<folly::SharedMutex&> hook) {
44+
vb.softDeleteStoredValueHook = hook;
45+
}
4146
};

0 commit comments

Comments
 (0)