Skip to content

fix(cpp): never seal empty chunks and fix dangling ref in parallel tablet write - #909

Open
kkzi wants to merge 4 commits into
apache:developfrom
kkzi:fix/cpp-writer-empty-chunk-dangling-ref
Open

fix(cpp): never seal empty chunks and fix dangling ref in parallel tablet write#909
kkzi wants to merge 4 commits into
apache:developfrom
kkzi:fix/cpp-writer-empty-chunk-dangling-ref

Conversation

@kkzi

@kkzi kkzi commented Aug 18, 2026

Copy link
Copy Markdown

Fixes #908

Two fixes in cpp/src/writer/tsfile_writer.cc

1. Never seal empty chunks (non-aligned flush path)

flush_chunk_group() / flush_chunk_group_encoded() now skip registered-but-empty measurement columns via the existing ChunkWriter::hasData() check — mirroring what the aligned branch already does.

Before: a measurement that received no data in a window was sealed as an EMPTY chunk (count=0, dataSize=0). Java readers (TsFileSequenceReader self-check / TsFileSketchTool) treat the whole file as crashed and refuse to load it, even though the other chunks are valid.

After: empty columns produce no chunk at all.

2. Fix dangling reference capture in parallel aligned tablet write

In write_table()'s aligned parallel path, the tasks submitted to the thread pool previously captured the loop variables (ctx, vt) by reference. Since the pool executes tasks asynchronously (after the loop advances/exits), the references dangle and tasks can read wrong or out-of-scope state. The fix captures the per-iteration addresses by value (ctx_ptr / vt_ptr), so each task reads its own DeviceWriteCtx / ValueTask.

Verification

  • Both fixes compile cleanly with the existing MSVC/Ninja build (only tsfile_writer.cc changed, no build config touched).
  • Fix 1 validated end-to-end: a reproducer that registers 3 measurements but writes to only 2 now produces a file with 2 chunks (no count=0 chunk), and TsFileSketchTool / print-tsfile.bat read it completely (END of TsFile, TsFile Sketch End, no errors).

…blet 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.
@ColinLeeo
ColinLeeo requested review from ColinLeeo and a balanced review from Copilot August 18, 2026 15:01

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Warning

Copilot couldn't run its full agentic review because it didn't start before the timeout. Make sure your repository has a runner available, or add a copilot-code-review.yml file specifying one with the runs-on attribute. See the docs for more details.

Fixes two correctness issues in the C++ TsFile writer: avoiding creation of invalid empty chunks in the non-aligned flush path, and preventing dangling reference captures in the aligned parallel tablet write path.

Changes:

  • Skip sealing registered-but-empty chunk writers in flush_chunk_group() and flush_chunk_group_encoded() via ChunkWriter::hasData().
  • Capture per-iteration DeviceWriteCtx / ValueTask pointers by value when submitting thread-pool tasks in write_table().

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

@kkzi

kkzi commented Aug 19, 2026

Copy link
Copy Markdown
Author

Pushed 6363b02 to fix the spotless clang-format violations in tsfile_writer.cc.

CI runs for the new commit are waiting for approval (action_required) — could you approve the workflows to re-run them?

@ColinLeeo

Copy link
Copy Markdown
Contributor

Hi @Kzzi, thanks for creating the PR.
After making the code changes, I think we should also add some regression tests to verify that the fix works as expected.

gx added 2 commits August 25, 2026 23:01
…let write

Covers the two fixes in this PR (issue apache#908):

1. Never seal registered-but-empty measurements as count=0 chunks
   (non-aligned flush path). Seven tests drive write_tablet,
   write_record, multi-window flush, empty-window-then-write,
   sibling devices, and mixed aligned/non-aligned devices, asserting
   via TsFileReader timeseries metadata that an unwritten measurement
   is absent from the file while surviving columns carry real
   statistics.

2. Parallel aligned tablet write capture-by-value. Five tests drive
   the thread-pool path in write_table() through TsFileTableWriter
   with multiple devices x columns x rows crossing page boundaries,
   and verify every cell round-trips (row completeness, per-cell
   values, tag correctness).

Verified: with both fixes temporarily reverted, the empty-chunk
tests fail (empty column sealed, meta.size()==3); with the fixes in
place all 12 tests pass (10 consecutive runs) and the full TsFile_Test
suite (762 tests) passes.
…gressions

Fix 1 coverage:
- memory-threshold auto-flush path: check_memory_size_and_may_flush_chunks()
  is a second entry into flush_chunk_group that the explicit-flush tests
  never drive; drive it via a shrunk chunk_group_size_threshold_.
- partially-null column counter-check: the hasData() guard must not skip
  columns with some nulls (null-bitmap fallback path) — only fully empty
  ones.
- TEXT column variant: empty TEXT takes the string write path, distinct
  from the fixed-width paths used before.

Fix 2 coverage:
- tiny page size (8 points) so pool-thread tasks seal pages repeatedly and
  exercise the initial_page_points continuation across batches.
- thread-pool boundary configs: 1-thread pool (serialized workers, worst
  case for slot aliasing) and 8-thread pool, via set_thread_count().

Verified: auto-flush and TEXT tests fail with the hasData() guard
reverted; all 17 tests pass with the fixes (and the full TsFile_Test
suite, 767 tests).
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

fix(cpp): never seal empty chunks and fix dangling ref in parallel tablet write

3 participants