From fbffec5abbd5eda8131ff1532a405b142a8184b1 Mon Sep 17 00:00:00 2001 From: Vitaliy Filippov Date: Sat, 20 Jun 2026 11:15:47 +0300 Subject: [PATCH] Add a regression test for the last 2 fixed bugs --- src/test/ringloop_mock.cpp | 7 ++- src/test/ringloop_mock.h | 7 ++- src/test/test_blockstore_v1.cpp | 80 +++++++++++++++++++++++++++++++++ 3 files changed, 91 insertions(+), 3 deletions(-) diff --git a/src/test/ringloop_mock.cpp b/src/test/ringloop_mock.cpp index 2b32349f..cd32544c 100644 --- a/src/test/ringloop_mock.cpp +++ b/src/test/ringloop_mock.cpp @@ -72,7 +72,7 @@ int ring_loop_mock_t::register_eventfd() io_uring_sqe* ring_loop_mock_t::get_sqe() { - if (free_ring_datas.size() == 0) + if (free_ring_datas.size() == 0 || is_full && is_full()) { return NULL; } @@ -399,3 +399,8 @@ bool disk_mock_t::submit(io_uring_sqe *sqe) // 3) when fsync is enabled, write cache should be sometimes lost during a simulated power outage return true; } + +void ring_loop_mock_t::set_fake_full(std::function is_full) +{ + this->is_full = is_full; +} diff --git a/src/test/ringloop_mock.h b/src/test/ringloop_mock.h index d8d846bc..879742bd 100644 --- a/src/test/ringloop_mock.h +++ b/src/test/ringloop_mock.h @@ -16,9 +16,11 @@ class ring_loop_mock_t: public ring_loop_i std::vector submit_ring_datas; std::vector completed_ring_datas; std::function submit_cb; - bool in_loop; - bool loop_again; + bool fake_full = false; + bool in_loop = false; + bool loop_again = false; bool support_zc = false; + std::function is_full; public: ring_loop_mock_t(int qd, std::function submit_cb); @@ -40,6 +42,7 @@ public: void restore(unsigned sqe_tail); void mark_completed(ring_data_t *data); + void set_fake_full(std::function is_full); }; class disk_mock_t diff --git a/src/test/test_blockstore_v1.cpp b/src/test/test_blockstore_v1.cpp index c53775d8..7a080bdc 100644 --- a/src/test/test_blockstore_v1.cpp +++ b/src/test/test_blockstore_v1.cpp @@ -376,9 +376,89 @@ static void test_validate_padded_journal() free(read_op.buf); } +// Check that read is retried and temporary read buffers are freed correctly (LSAN) +// TODO: Check retries in other configuration +static void test_read_retry_on_ring_full_1M_csum4k_clean() +{ + printf("\n-- test_read_retry_on_ring_full_1M_csum4k_clean\n"); + + bs_test_t test; + test.default_cfg(); + test.config["inmemory_metadata"] = "0"; + test.config["block_size"] = "1048576"; + test.config["data_csum_type"] = "crc32c"; + test.config["csum_block_size"] = "4096"; + test.init(); + printf("blockstore initialized\n"); + + // Big_write without external bitmap(!) - also checks if journaled big_writes + // are handled correctly (they don't have an external bitmap) + printf("write v1 0+1M\n"); + blockstore_op_t op; + op.opcode = BS_OP_WRITE_STABLE; + op.oid = { .inode = 1, .stripe = 0 }; + op.version = 1; + op.offset = 0; + op.len = 1024*1024; + op.buf = (uint8_t*)memalign_or_die(MEM_ALIGNMENT, op.len); + memset(op.buf, 0xAA, op.len); + test.exec_op(&op); + assert(op.retval == op.len); + + // Wait for flushing + printf("triggering compaction\n"); + test.flusher()->request_trim(); + while (test.flusher()->get_queue_size()) + test.ringloop->loop(); + while (test.flusher()->is_active()) + test.ringloop->loop(); + test.flusher()->release_trim(); + assert(!test.flusher()->get_queue_size()); + printf("compaction complete\n"); + + // Read with retry + printf("read with ring-full-retries\n"); + blockstore_op_t read_op; + read_op.opcode = BS_OP_READ; + read_op.oid = { .inode = 1, .stripe = 0 }; + read_op.version = 2; + read_op.offset = 0; + read_op.len = 1024*1024; + read_op.buf = (uint8_t*)memalign_or_die(MEM_ALIGNMENT, read_op.len); + int req_count = 0; + int retry_on = 0; + bool done = false; + read_op.callback = [&](blockstore_op_t *op) + { + done = true; + }; + // Force a retry after each sqe + test.ringloop->set_fake_full([&] + { + if (req_count == retry_on) + { + printf("hit retry on sqe %d\n", req_count); + req_count = 0; + retry_on++; + return true; + } + req_count++; + return false; + }); + test.bs->enqueue_op(&read_op); + while (!done) + test.ringloop->loop(); + read_op.callback = nullptr; + assert(read_op.retval == read_op.len); + + free(op.buf); + free(read_op.buf); +} + int main(int narg, char *args[]) { test_preserve_corruption(); test_validate_padded_journal(); + test_read_retry_on_ring_full_1M_csum4k_clean(); return 0; }