Skip to content

Commit 24828dd

Browse files
[release/6.x] Cherry pick: Fix historical query LRU cache size calculations (#7511) (#7512)
Co-authored-by: Max <maxtropets@microsoft.com>
1 parent ad76a7e commit 24828dd

3 files changed

Lines changed: 158 additions & 7 deletions

File tree

CHANGELOG.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,10 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
1515

1616
### Fixed
1717

18-
- CheckQuorum now requires a quorum in every configuration (#7375)
18+
- CheckQuorum now requires a quorum in every configuration (#7375).
1919
- `read_ledger.py` validates the offsets table in committed ledger files, reporting an error if this is truncated (#7501).
20-
- Allow carriage returns in PEM certificatees (#7507)
20+
- Allow carriage returns in PEM certificatees (#7507).
21+
- Fixed a bug in calculation of historical query cache size, which could have resulted in evicted unnecessarily (#7511).
2122

2223
### Changed
2324

src/node/historical_queries.h

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -249,8 +249,6 @@ namespace ccf::historical
249249
{
250250
std::vector<SeqNo> removed{}, added{};
251251

252-
bool any_diff = false;
253-
254252
// If a seqno is earlier than the earliest known ledger secret, we will
255253
// store that it was requested with a nullptr in `my_stores`, but not
256254
// add it to `all_stores` to begin fetching until a sufficiently early
@@ -278,7 +276,6 @@ namespace ccf::historical
278276
// Remove it from my_stores
279277
removed.push_back(prev_it->first);
280278
prev_it = my_stores.erase(prev_it);
281-
any_diff |= true;
282279
}
283280
else
284281
{
@@ -308,19 +305,23 @@ namespace ccf::historical
308305
added.push_back(*new_it);
309306
prev_it = my_stores.insert_or_assign(prev_it, *new_it, details);
310307
}
311-
any_diff |= true;
312308
}
313309
}
314310

315311
if (prev_it != my_stores.end())
316312
{
317313
// If we have a suffix of seqnos previously requested, now
318314
// unrequested, purge them
315+
for (auto it = prev_it; it != my_stores.end(); ++it)
316+
{
317+
removed.push_back(it->first);
318+
}
319319
my_stores.erase(prev_it, my_stores.end());
320-
any_diff |= true;
321320
}
322321
}
323322

323+
const bool any_diff = !removed.empty() || !added.empty();
324+
324325
if (!any_diff && (should_include_receipts == include_receipts))
325326
{
326327
HISTORICAL_LOG("Identical to previous request");
@@ -1397,6 +1398,12 @@ namespace ccf::historical
13971398
return store;
13981399
}
13991400

1401+
size_t get_estimated_store_cache_size()
1402+
{
1403+
std::lock_guard<ccf::pal::Mutex> guard(requests_lock);
1404+
return estimated_store_cache_size;
1405+
}
1406+
14001407
void tick(const std::chrono::milliseconds& elapsed_ms)
14011408
{
14021409
std::lock_guard<ccf::pal::Mutex> guard(requests_lock);

src/node/test/historical_queries.cpp

Lines changed: 143 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2005,6 +2005,149 @@ TEST_CASE("Valid merkle proof from receipts")
20052005
ccf::crypto::openssl_sha256_shutdown();
20062006
}
20072007

2008+
TEST_CASE("Cache size estimation")
2009+
{
2010+
ccf::crypto::openssl_sha256_init();
2011+
auto state = create_and_init_state();
2012+
auto& kv_store = *state.kv_store;
2013+
2014+
write_transactions_and_signature(kv_store, 10);
2015+
2016+
auto ledger = construct_host_ledger(state.kv_store->get_consensus());
2017+
2018+
auto stub_writer = std::make_shared<StubWriter>();
2019+
ccf::historical::StateCacheImpl cache(
2020+
kv_store, state.ledger_secrets, stub_writer);
2021+
2022+
cache.set_soft_cache_limit(0);
2023+
2024+
ccf::historical::CompoundHandle handle = {
2025+
ccf::historical::RequestNamespace::Application, 1};
2026+
2027+
{
2028+
ccf::ds::ContiguousSet<ccf::SeqNo> seqnos;
2029+
seqnos.insert(10);
2030+
cache.get_stores_for(handle, seqnos, std::chrono::seconds(1));
2031+
}
2032+
2033+
REQUIRE(cache.get_estimated_store_cache_size() == 0);
2034+
cache.handle_ledger_entry(10, ledger.at(10));
2035+
REQUIRE(cache.get_estimated_store_cache_size() == ledger.at(10).size());
2036+
2037+
{
2038+
ccf::ds::ContiguousSet<ccf::SeqNo> seqnos;
2039+
seqnos.insert(5);
2040+
cache.get_stores_for(handle, seqnos, std::chrono::seconds(1));
2041+
}
2042+
2043+
cache.tick(std::chrono::milliseconds(1000));
2044+
2045+
REQUIRE(cache.get_estimated_store_cache_size() == 0);
2046+
ccf::crypto::openssl_sha256_shutdown();
2047+
}
2048+
2049+
TEST_CASE("adjust_ranges")
2050+
{
2051+
ccf::crypto::openssl_sha256_init();
2052+
using SeqNoSet = std::set<ccf::SeqNo>;
2053+
2054+
struct AdjustRangesAccessor : public ccf::historical::StateCacheImpl
2055+
{
2056+
Request request;
2057+
2058+
AdjustRangesAccessor(
2059+
ccf::kv::Store& store,
2060+
const std::shared_ptr<ccf::LedgerSecrets>& secrets,
2061+
const ringbuffer::WriterPtr& host_writer) :
2062+
StateCacheImpl(store, secrets, host_writer),
2063+
request(all_stores)
2064+
{}
2065+
2066+
std::pair<SeqNoSet, SeqNoSet> adjust_ranges(const SeqNoSet& seqnos)
2067+
{
2068+
ccf::SeqNoCollection seqno_collection;
2069+
for (const auto& seqno : seqnos)
2070+
{
2071+
seqno_collection.insert(seqno);
2072+
}
2073+
2074+
auto [removed_v, added_v] =
2075+
request.adjust_ranges(seqno_collection, true, 0);
2076+
SeqNoSet removed(removed_v.begin(), removed_v.end());
2077+
SeqNoSet added(added_v.begin(), added_v.end());
2078+
return {removed, added};
2079+
}
2080+
};
2081+
2082+
auto state = create_and_init_state();
2083+
auto stub_writer = std::make_shared<StubWriter>();
2084+
2085+
{
2086+
DOCTEST_INFO("Minimal regression test");
2087+
AdjustRangesAccessor cache(
2088+
*state.kv_store, state.ledger_secrets, stub_writer);
2089+
2090+
auto [removed1, added1] = cache.adjust_ranges({100});
2091+
REQUIRE(added1.size() == 1);
2092+
REQUIRE(added1 == SeqNoSet{100});
2093+
REQUIRE(removed1.size() == 0);
2094+
2095+
auto [removed2, added2] = cache.adjust_ranges({42});
2096+
REQUIRE(added2.size() == 1);
2097+
REQUIRE(added2 == SeqNoSet{42});
2098+
REQUIRE(removed2.size() == 1);
2099+
REQUIRE(removed2 == SeqNoSet{100});
2100+
}
2101+
2102+
{
2103+
const auto seed = time(NULL);
2104+
DOCTEST_INFO("Random permutations, using seed: ", seed);
2105+
srand(seed);
2106+
for (size_t i = 0; i < 100; ++i)
2107+
{
2108+
DOCTEST_INFO("Iteration #", i);
2109+
AdjustRangesAccessor cache(
2110+
*state.kv_store, state.ledger_secrets, stub_writer);
2111+
SeqNoSet before;
2112+
for (auto j = 0; j < rand() % 6; ++j)
2113+
{
2114+
before.insert(rand() % 30);
2115+
}
2116+
2117+
auto [removed_init, added_init] = cache.adjust_ranges(before);
2118+
REQUIRE(added_init == before);
2119+
REQUIRE(removed_init.empty());
2120+
2121+
std::set<ccf::SeqNo> after;
2122+
for (auto j = 0; j < rand() % 6; ++j)
2123+
{
2124+
after.insert(rand() % 30);
2125+
}
2126+
2127+
auto [actual_removed, actual_added] = cache.adjust_ranges(after);
2128+
2129+
SeqNoSet expected_added;
2130+
std::set_difference(
2131+
after.begin(),
2132+
after.end(),
2133+
before.begin(),
2134+
before.end(),
2135+
std::inserter(expected_added, expected_added.begin()));
2136+
SeqNoSet expected_removed;
2137+
std::set_difference(
2138+
before.begin(),
2139+
before.end(),
2140+
after.begin(),
2141+
after.end(),
2142+
std::inserter(expected_removed, expected_removed.begin()));
2143+
2144+
REQUIRE(actual_added == expected_added);
2145+
REQUIRE(actual_removed == expected_removed);
2146+
}
2147+
}
2148+
ccf::crypto::openssl_sha256_shutdown();
2149+
}
2150+
20082151
int main(int argc, char** argv)
20092152
{
20102153
threading::ThreadMessaging::init(1);

0 commit comments

Comments
 (0)