Skip to content

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

Description

@kkzi

Bug 1: registered-but-empty measurements are sealed as EMPTY chunks, making Java readers refuse the file

Environment: TsFileCpp 2.3.2.dev (cpp tree-model writer)

When a tree-model TsFileWriter registers a measurement but receives no data for it during a window (e.g. event-driven parameters that fire sporadically, or a wide schema where only a subset of columns receive values), flush_chunk_group() / flush_chunk_group_encoded() seal that column as an empty chunk:

[Chunk] of <device>.<measurement>, startTime: 9223372036854775807 endTime: -9223372036854775808 count: 0
[Chunk Header] marker=5, dataSize=0, size=78

Java readers treat this as a crashed file — TsFileSequenceReader self-check fails (self-check cannot proceed at position ... because: 0) and TsFileSketchTool throws IOException: ... because the file has crashed plus an NPE while printing the empty chunk. The whole file becomes unreadable by the Java toolchain, even though most chunks contain valid data.

Cause: the non-aligned branch of both flush functions only checks m_schema->chunk_writer_ != nullptr and lacks the hasData() guard that the aligned branch already has:

if (!chunk_group->is_aligned_ && m_schema->chunk_writer_ != nullptr) {
    FLUSH_CHUNK(chunk_writer, ...);   // seals count=0 chunk when empty
}

Expected behavior: an empty column should produce no chunk (mirroring the aligned branch and Java IoTDB behavior).


Bug 2: dangling reference capture in parallel aligned tablet write

In write_table() (aligned parallel path), the lambdas submitted to the thread pool capture the loop variables by reference:

for (auto& ctx : device_ctxs) {
    futures.push_back(common::g_thread_pool_->submit(
        [&write_time_segments, &ctx]() { ... }));   // captures loop var by reference
    for (auto& vt : ctx.value_tasks) {
        futures.push_back(common::g_thread_pool_->submit(
            [&write_value_segments, &vt, &ctx]() { ... }));
    }
}

submit enqueues the task for asynchronous execution on pool threads. When the worker threads run the tasks, the loop has already advanced (or exited), so the references dangle / alias the same loop slot — reading out-of-scope or wrong ctx/vt state. This is a use-after-scope bug that can corrupt parallel tablet writes when parallel_write_enabled_ is on.

Expected behavior: capture the per-iteration addresses by value (ctx_ptr/vt_ptr) so each task reads its own DeviceWriteCtx/ValueTask.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions