From b5495d5467ddc923314af36812c753217b24a657 Mon Sep 17 00:00:00 2001 From: mrcs64 <9069178+mrcs64@users.noreply.github.com> Date: Fri, 10 Jul 2026 08:09:34 +0200 Subject: [PATCH 1/2] fix(segment): propagate forward-store writer open failure instead of crashing MemForwardStore::Open() stored the result of ChunkedFileWriter::Open() (a unique_ptr that is null when the underlying arrow FileOutputStream cannot be created, e.g. under file-descriptor pressure from many concurrent collection opens) but returned Status::OK() unconditionally. The failure was swallowed: the store reported success with a null writer_, and a later flush()/close() dereferenced it, crashing with SIGSEGV at memory_forward_store.cc:283. init_memory_components() also assigned memory_store_ to the member before Open() succeeded, so a failed init left a non-null, half-built memory_store_ that satisfied the `if (!memory_store_)` re-init guard and got flushed on close. - MemForwardStore::Open(): return an error when the writer cannot be opened. - MemForwardStore::flush(): guard against a null writer_ before writing. - SegmentImpl::init_memory_components(): roll back partial components on failure via AILEGO_DEFER so a failed init leaves memory_store_ null. Adds a mem_store_test case asserting Open() reports an error when the writer cannot be created. --- src/db/index/segment/segment.cc | 12 ++++++++++++ src/db/index/storage/memory_forward_store.cc | 9 +++++++++ tests/db/index/storage/mem_store_test.cc | 14 ++++++++++++++ 3 files changed, 35 insertions(+) diff --git a/src/db/index/segment/segment.cc b/src/db/index/segment/segment.cc index e21ececa2..246ddded5 100644 --- a/src/db/index/segment/segment.cc +++ b/src/db/index/segment/segment.cc @@ -4116,6 +4116,17 @@ VectorColumnIndexer::Ptr SegmentImpl::create_vector_indexer( } Status SegmentImpl::init_memory_components() { + // Roll back any partially-created components on failure so a failed init + // leaves memory_store_ null (the caller's `if (!memory_store_)` retry guard + // depends on it) and never gets flushed on close. + bool committed = false; + AILEGO_DEFER([&]() { + if (!committed) { + memory_store_.reset(); + memory_vector_indexers_.clear(); + quant_memory_vector_indexers_.clear(); + } + }); // init memory block id auto &mem_block = segment_meta_->writing_forward_block().value(); @@ -4186,6 +4197,7 @@ Status SegmentImpl::init_memory_components() { } } + committed = true; return Status::OK(); } diff --git a/src/db/index/storage/memory_forward_store.cc b/src/db/index/storage/memory_forward_store.cc index 94a6aa33a..40ce7b8f7 100644 --- a/src/db/index/storage/memory_forward_store.cc +++ b/src/db/index/storage/memory_forward_store.cc @@ -54,6 +54,10 @@ Status MemForwardStore::Open() { physic_schema_ = arrow::schema(fields); // Initialize file writer writer_ = ChunkedFileWriter::Open(path_, physic_schema_, format_); + if (!writer_) { + return Status::InternalError( + "failed to open forward store writer at [", path_, "]"); + } return Status::OK(); } @@ -216,6 +220,11 @@ Status MemForwardStore::flush() { return Status::OK(); } + if (!writer_) { + return Status::InternalError( + "forward store writer not open, cannot flush [", path_, "]"); + } + auto result = convertToRecordBatch(); if (!result.ok()) { return Status::InternalError("failed to convert cache to RecordBatch: ", diff --git a/tests/db/index/storage/mem_store_test.cc b/tests/db/index/storage/mem_store_test.cc index da0bd64be..d8ca52bc1 100644 --- a/tests/db/index/storage/mem_store_test.cc +++ b/tests/db/index/storage/mem_store_test.cc @@ -1156,3 +1156,17 @@ TEST_F(MemStoreTest, General) { } #endif + +// Regression: MemForwardStore::Open() used to ignore a failed +// ChunkedFileWriter::Open (null writer_) and return OK, which then crashed on +// flush(). Open() must report the failure instead. +TEST(MemStoreOpenTest, OpenReportsErrorWhenWriterCreationFails) { + auto schema = GetCollectionSchema(); + // Parent directory does not exist -> arrow FileOutputStream::Open fails -> + // ChunkedFileWriter::Open returns nullptr. + auto store = std::make_shared( + schema, "/nonexistent_zvec_dir_xyz/scalar.block.0", FileFormat::IPC); + auto status = store->Open(); + EXPECT_FALSE(status.ok()); + EXPECT_EQ(store->writer_, nullptr); +} From 854513333022b992aa7478938f0ed73c03c6c260 Mon Sep 17 00:00:00 2001 From: mrcs64 <9069178+mrcs64@users.noreply.github.com> Date: Thu, 16 Jul 2026 08:22:07 +0200 Subject: [PATCH 2/2] style(segment): satisfy clang-format on forward-store error path --- src/db/index/storage/memory_forward_store.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/db/index/storage/memory_forward_store.cc b/src/db/index/storage/memory_forward_store.cc index 40ce7b8f7..268a7c581 100644 --- a/src/db/index/storage/memory_forward_store.cc +++ b/src/db/index/storage/memory_forward_store.cc @@ -55,8 +55,8 @@ Status MemForwardStore::Open() { // Initialize file writer writer_ = ChunkedFileWriter::Open(path_, physic_schema_, format_); if (!writer_) { - return Status::InternalError( - "failed to open forward store writer at [", path_, "]"); + return Status::InternalError("failed to open forward store writer at [", + path_, "]"); } return Status::OK(); }