When a cluster has no sync zones (single-zone), DataLogTrimCR passes
max_marker() as the trim marker, which encodes target_gen = UINT64_MAX
from gencursor(). In DataLogBackends::trim_entries, after trimming the
head (last) generation, the break condition
if (be->gen_id == target_gen)
is false (e.g. 0 != UINT64_MAX), so the loop attempts its increment
expression:
be = upper_bound(be->gen_id)->second
upper_bound(head_gen) returns end(), and dereferencing end()->second
causes crash.
Fix: also break when be->gen_id >= head_gen. Once we've trimmed the
head generation there are no further backends in the map, so the
upper_bound dereference in the loop increment will be skipped.
This is a general bug that affects any cluster using max_marker() as a
trim target (i.e. every single-zone deployment).
Signed-off-by: Oguzhan Ozmen <oozmen@bloomberg.net>
l.unlock();
auto c = be->gen_id == target_gen ? cursor : be->max_marker();
co_await be->trim(dpp, shard_id, c);
- if (be->gen_id == target_gen)
+ if (be->gen_id == target_gen || be->gen_id >= head_gen)
break;
l.lock();
};
}
// trim_entries with max_marker() must not crash on a single-generation cluster.
-// Without the fix, the loop increment would call upper_bound(head_gen)->second,
-// dereferencing end() and causing a SIGSEGV.
CORO_TEST_F(DataLogBulky, TrimWithMaxMarker, DataLogBulky) {
for (const auto& bg : bulky) {
co_await add_entry(dpp(), bg);