mirror of
https://github.com/scylladb/scylladb.git
synced 2026-04-28 12:17:02 +00:00
combined: mergers: remove recursion in operator()()
In mutation_reader_merger and clustering_order_reader_merger, the operator()() is responsible for producing mutation fragments that will be merged and pushed to the combined reader's buffer. Sometimes, it might have to advance existing readers, open new and / or close some existing ones, which requires calling a helper method and then calling operator()() recursively. In some unlucky circumstances, a stack overflow can occur: - Readers have to be opened incrementally, - Most or all readers must not produce any fragments and need to report end of stream without preemption, - There has to be enough readers opened within the lifetime of the combined reader (~500), - All of the above needs to happen within a single task quota. In order to prevent such a situation, the code of both reader merger classes were modified not to perform recursion at all. Most of the code of the operator()() was moved to maybe_produce_batch which does not recur if it is not possible for it to produce a fragment, instead it returns std::nullopt and operator()() calls this method in a loop via seastar::repeat_until_value. A regression test is added. Fixes: scylladb/scylladb#14415 Closes #14452
This commit is contained in:
committed by
Botond Dénes
parent
5648bfb9a0
commit
ee9bfb583c
@@ -669,6 +669,50 @@ SEASTAR_THREAD_TEST_CASE(test_sm_fast_forwarding_combining_reader_with_galloping
|
||||
assertions.produces_end_of_stream();
|
||||
}
|
||||
|
||||
class selector_of_empty_readers : public reader_selector {
|
||||
schema_ptr _schema;
|
||||
reader_permit _permit;
|
||||
size_t _remaining;
|
||||
public:
|
||||
selector_of_empty_readers(schema_ptr s, reader_permit permit, size_t count)
|
||||
: reader_selector(s, dht::ring_position_view::min())
|
||||
, _schema(s)
|
||||
, _permit(std::move(permit))
|
||||
, _remaining(count) {
|
||||
}
|
||||
virtual std::vector<flat_mutation_reader_v2> create_new_readers(const std::optional<dht::ring_position_view>& pos) override {
|
||||
if (_remaining == 0) {
|
||||
return {};
|
||||
}
|
||||
--_remaining;
|
||||
std::vector<flat_mutation_reader_v2> ret;
|
||||
ret.push_back(make_empty_flat_reader_v2(_schema, _permit));
|
||||
return ret;
|
||||
}
|
||||
virtual std::vector<flat_mutation_reader_v2> fast_forward_to(const dht::partition_range& pr) override {
|
||||
assert(false); // Fast forward not supported by this reader
|
||||
return {};
|
||||
}
|
||||
};
|
||||
|
||||
// Reproduces scylladb/scylladb#14415
|
||||
SEASTAR_THREAD_TEST_CASE(test_combined_reader_with_incrementally_opened_empty_readers) {
|
||||
static constexpr size_t empty_reader_count = 10 * 1000;
|
||||
|
||||
simple_schema s;
|
||||
tests::reader_concurrency_semaphore_wrapper semaphore;
|
||||
auto permit = semaphore.make_permit();
|
||||
|
||||
auto reader = make_combined_reader(s.schema(), permit,
|
||||
std::make_unique<selector_of_empty_readers>(s.schema(), permit, empty_reader_count),
|
||||
streamed_mutation::forwarding::no,
|
||||
mutation_reader::forwarding::no);
|
||||
|
||||
// Expect that the reader won't produce a stack overflow
|
||||
assert_that(std::move(reader))
|
||||
.produces_end_of_stream();
|
||||
}
|
||||
|
||||
SEASTAR_TEST_CASE(combined_mutation_reader_test) {
|
||||
return sstables::test_env::do_with_async([] (sstables::test_env& env) {
|
||||
simple_schema s;
|
||||
|
||||
Reference in New Issue
Block a user