From 26139ba57ba4eaf1445cd7dece72f31b05d2cc8e Mon Sep 17 00:00:00 2001 From: gx Date: Tue, 18 Aug 2026 20:19:33 +0800 Subject: [PATCH 1/2] fix(cpp): never seal empty chunks and fix dangling ref in parallel tablet write Two fixes in TsFileWriter: 1. flush_chunk_group / flush_chunk_group_encoded: skip registered-but-empty measurement columns. A measurement that received no data in a window used to be sealed as an EMPTY chunk (count=0, dataSize=0). Java readers (TsFileSequenceReader self-check) treat such a file as crashed and refuse to load it. Mirror the aligned branch's existing hasData() check so empty columns never produce a chunk. 2. write_table (aligned parallel path): the submitted tasks run asynchronously on the thread pool, but the lambdas captured the loop variables (ctx, vt) by reference. Once the loop advances, every queued task reads the same / already-destroyed loop variable. Capture the per-iteration addresses by value instead. --- cpp/src/writer/tsfile_writer.cc | 32 ++++++++++++++++++++++++-------- 1 file changed, 24 insertions(+), 8 deletions(-) diff --git a/cpp/src/writer/tsfile_writer.cc b/cpp/src/writer/tsfile_writer.cc index aa0e555f8..9deed467c 100644 --- a/cpp/src/writer/tsfile_writer.cc +++ b/cpp/src/writer/tsfile_writer.cc @@ -1351,17 +1351,24 @@ int TsFileWriter::write_table(Tablet& tablet) { common::g_thread_pool_ != nullptr) { std::vector> futures; for (auto& ctx : device_ctxs) { + // Capture pointers by value: the submitted tasks run on pool + // threads asynchronously (after this loop returns), so + // capturing the loop variables by reference would dangle + // (all tasks would read the same/out-of-scope ctx). + auto* ctx_ptr = &ctx; futures.push_back(common::g_thread_pool_->submit( - [&write_time_segments, &ctx]() { - return write_time_segments(ctx.tcw, ctx.segments, - ctx.initial_page_points); + [&write_time_segments, ctx_ptr]() { + return write_time_segments(ctx_ptr->tcw, + ctx_ptr->segments, + ctx_ptr->initial_page_points); })); for (auto& vt : ctx.value_tasks) { + auto* vt_ptr = &vt; futures.push_back(common::g_thread_pool_->submit( - [&write_value_segments, &vt, &ctx]() { + [&write_value_segments, vt_ptr, ctx_ptr]() { return write_value_segments( - vt.vcw, vt.col_idx, ctx.segments, - ctx.initial_page_points); + vt_ptr->vcw, vt_ptr->col_idx, + ctx_ptr->segments, ctx_ptr->initial_page_points); })); } } @@ -1898,7 +1905,13 @@ int TsFileWriter::flush_chunk_group_encoded(MeasurementSchemaGroup* chunk_group, for (MeasurementSchemaMapIter ms_iter = map.begin(); ms_iter != map.end(); ms_iter++) { MeasurementSchema* m_schema = ms_iter->second; - if (!chunk_group->is_aligned_ && m_schema->chunk_writer_ != nullptr) { + // Skip registered-but-empty columns: a measurement that was never + // written in this window would otherwise be sealed as an EMPTY chunk + // (count=0, dataSize=0). Java readers (TsFileSequenceReader self- + // check) treat such a file as crashed. Mirror the aligned branch's + // hasData() check below. + if (!chunk_group->is_aligned_ && m_schema->chunk_writer_ != nullptr && + m_schema->chunk_writer_->hasData()) { ChunkWriter*& chunk_writer = m_schema->chunk_writer_; FLUSH_CHUNK_ENCODED( chunk_writer, io_writer_, m_schema->measurement_name_, @@ -1935,7 +1948,10 @@ int TsFileWriter::flush_chunk_group(MeasurementSchemaGroup* chunk_group, for (MeasurementSchemaMapIter ms_iter = map.begin(); ms_iter != map.end(); ms_iter++) { MeasurementSchema* m_schema = ms_iter->second; - if (!chunk_group->is_aligned_ && m_schema->chunk_writer_ != nullptr) { + // See flush_chunk_group_encoded: never seal a registered-but-empty + // column as a count=0 chunk. + if (!chunk_group->is_aligned_ && m_schema->chunk_writer_ != nullptr && + m_schema->chunk_writer_->hasData()) { ChunkWriter*& chunk_writer = m_schema->chunk_writer_; FLUSH_CHUNK(chunk_writer, io_writer_, m_schema->measurement_name_, m_schema->data_type_, m_schema->encoding_, From 6363b029585c205c8ceed9669bf95ccadabcc880 Mon Sep 17 00:00:00 2001 From: gx Date: Wed, 19 Aug 2026 21:52:15 +0800 Subject: [PATCH 2/2] style(cpp): fix spotless clang-format violations in parallel tablet write --- cpp/src/writer/tsfile_writer.cc | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/cpp/src/writer/tsfile_writer.cc b/cpp/src/writer/tsfile_writer.cc index 9deed467c..20512e68a 100644 --- a/cpp/src/writer/tsfile_writer.cc +++ b/cpp/src/writer/tsfile_writer.cc @@ -1358,17 +1358,17 @@ int TsFileWriter::write_table(Tablet& tablet) { auto* ctx_ptr = &ctx; futures.push_back(common::g_thread_pool_->submit( [&write_time_segments, ctx_ptr]() { - return write_time_segments(ctx_ptr->tcw, - ctx_ptr->segments, - ctx_ptr->initial_page_points); + return write_time_segments( + ctx_ptr->tcw, ctx_ptr->segments, + ctx_ptr->initial_page_points); })); for (auto& vt : ctx.value_tasks) { auto* vt_ptr = &vt; futures.push_back(common::g_thread_pool_->submit( [&write_value_segments, vt_ptr, ctx_ptr]() { return write_value_segments( - vt_ptr->vcw, vt_ptr->col_idx, - ctx_ptr->segments, ctx_ptr->initial_page_points); + vt_ptr->vcw, vt_ptr->col_idx, ctx_ptr->segments, + ctx_ptr->initial_page_points); })); } }