Skip to content

flowctl: parse fixture lines once and prepare them on worker threads - #3446

Open
mdibaiee wants to merge 3 commits into
masterfrom
mahdi/preview-next-fixture-feeder
Open

mdibaiee wants to merge 3 commits into
masterfrom
mahdi/preview-next-fixture-feeder

Conversation

@mdibaiee

@mdibaiee mdibaiee commented Aug 30, 2026 •

Copy link
Copy Markdown
Member

Description:

The preview-next fixture feeder parsed every streamed line into a serde_json::Value, cloned it, rebuilt it as a HeapNode, then archived, validated and keyed it, all on one task. Benchmark runs were bound by it: a TPC-DS scale 1 fixture through a no-op materialization ran at 84K docs/s with the runtime and connector mostly idle.

  • Document lines of a streamed fixture are built into shuffle::log_request::Appends on scoped worker threads, as the live slice builds them from journal reads: parsed once with simd-doc straight into a HeapNode, stamped with the synthetic UUID, archived, validated and keyed. The Appends are then routed and pushed in fixture order, so blocks and document clocks match a serial feeder.
  • runtime_local::segments splits that work into build_append (no transaction state, safe to run in parallel) and push_append (routing and serial block and frontier accounting). push_binding keeps its signature and uses both, so eager fixtures and catalog tests share one implementation.
  • --fixture-validate=false flags streamed documents valid unchecked, for generated fixtures known to be valid.

Same TPC-DS run on a c3-standard-22: 84K → 142K docs/s.

Workflow steps:

flowctl raw preview-next --fixture <fifo> [--fixture-validate=false]. Output is unchanged.

Documentation links affected:

None.

Notes for reviewers:

  • built_appends_match_the_value_path checks that the simd-doc path builds the same Appends as the serde_json::Value path; batches_write_the_same_log_on_any_number_of_workers checks that 1, 4 and 7 workers write identical segments and frontiers.
  • Past the feeder, the next limit in these runs was the shard's connector request queue (runtime-next: queue a shard's connector requests in a VecDeque #3638).

jacobmarble
jacobmarble previously approved these changes Aug 31, 2026
Base automatically changed from mahdi/preview-next-fixture-backlog-underflow to master August 31, 2026 22:25
@mdibaiee
mdibaiee dismissed jacobmarble’s stale review August 31, 2026 22:25

The base branch was changed.

@jgraettinger

jgraettinger commented Aug 31, 2026 •

Copy link
Copy Markdown
Member

Please hold off on merging this, until this code refactor move lands? #3416

@mdibaiee

Copy link
Copy Markdown
Member Author

@jgraettinger okay 👍🏽

The preview-next fixture feeder parsed every line into a serde_json::Value,
cloned it, rebuilt it as a HeapNode, archived, validated and keyed it, all
on one task. TPC-DS benchmark runs were bound by it at ~85K docs/s while
the runtime and connector idled.

Streamed document lines are now batched and prepared on scoped worker
threads, parsed once straight into a HeapNode with simd-doc, then pushed in
fixture order, so blocks and clocks are unchanged. segments::push_binding
keeps its signature and shares prepare_binding / push_prepared with the
parallel path. --fixture-validate=false flags streamed documents valid
unchecked, for generated fixtures known to be valid.
@mdibaiee
mdibaiee force-pushed the mahdi/preview-next-fixture-feeder branch from c5b4a2c to 6d54b29 Compare October 20, 2026 18:54
@strix-security

strix-security Bot commented Oct 20, 2026 •

Copy link
Copy Markdown

Strix Security Review

✅ No open security findings.

PR summary: This PR speeds up the streaming `--fixture` feeder of `flowctl raw preview-next` by parsing each fixture line once straight into a `HeapNode` and preparing it into shuffle-log `Append`s on worker threads (then pushing them in fixture order). It also splits the segment writer into a state-free `build_append` and a stateful `push_append`, and adds a `--fixture-validate` flag (default on) that lets an operator skip schema validation for fixtures already known to be valid.

Review summary

Reviewed all three changed files in the diff: the new fixture_validate CLI flag and its plumbing in crates/flowctl/src/raw/preview_next/mod.rs, the split of the segment writer into build_append / push_append (plus TxnState::take_doc_seconds) in crates/runtime-local/src/segments.rs, and the batched, worker-thread Append builder in crates/flowctl/src/raw/preview_next/fixture.rs, including the new equivalence tests. Two suspicions were raised and independently validated: whether --fixture-validate=false lets unvalidated documents carry the schema-valid bit into a trust boundary, and whether the reused arena plus unchecked archive access in the parallel builder could be unsound. Both were disproved — the flag is a documented, local-operator-only opt-out confined to the streaming fixture path whose shuffle log lives in a per-invocation temporary directory, and the archive is produced by the serializer over a live HeapNode while all Append payloads are copied out of the bump arena before it is reset, with no unsafe added. No security issues were found in the changed code. One non-blocking documentation note for the author: schema validation is also where redact annotations are applied, so --fixture-validate=false additionally skips redaction of the operator's own fixture documents; a sentence in the flag's help text would make that explicit.

Ruled out (3)
  • Can `--fixture-validate=false` let unvalidated documents pass a trust boundary by carrying the schema-valid flag?
    The flag is a plain CLI argument on a locally-invoked developer command, only applies to the streaming fixture path whose shuffle log is written to a per-invocation temporary directory, and the same operator already supplies both the fixture documents and the flag, so no boundary is crossed.
  • Is the reused bump arena and unchecked archive access in the parallel Append builder a use-after-reset or data race?
    `Allocator::reset` needs exclusive access that the `'alloc` lifetimes enforce, `simd_doc::Parser::parse_one` returns a real `HeapNode` whose archive is emitted by rkyv itself, and every `Append` payload is copied out of the arena before reset while each worker owns its own builder.
  • Can a malformed fixture line trigger out-of-bounds reads through the archived document view or key extraction?
    The archive passed to `ArchivedNode::from_archive` is serialized by rkyv from a live `HeapNode`, so alignment and internal offsets come from the serializer rather than from document content.
Review history (3)
  • Review 3 · 2966dfa · Oct 21 12:34 UTC · Nothing new
  • Review 2 · c6276ff · Oct 21 12:27 UTC · Nothing new
  • Review 1 · 6d54b29 · Oct 20 18:56 UTC · Nothing new

Review 3 on this PR · 2966dfa · requested Oct 21 12:31 UTC on a new commit


Reviewed by Strix
Re-run review · Configure security review settings

The fixture feeder emulates the live slice, which reads a journal document
into a shuffle::log_request::Append. Name the parallel step after it:
segments::build_append returns an Append, push_append routes and buffers it,
and a feeder worker is an AppendBuilder.
Comment thread crates/flowctl/src/raw/preview_next/fixture.rs Outdated
Comment thread crates/flowctl/src/raw/preview_next/fixture.rs Outdated
Comment thread crates/flowctl/src/raw/preview_next/fixture.rs Outdated
@mdibaiee
mdibaiee requested a review from jacobmarble October 21, 2026 12:47
@mdibaiee

Copy link
Copy Markdown
Member Author

This PR has been rebased on latest changes and is ready for review again now that #3416 has been merged. Only affects runtime-local and preview_next

let first_seconds = txn.take_doc_seconds(batch.len() as u64);
let chunk = batch.len().div_ceil(builders.len());

let built: Vec<anyhow::Result<Vec<Append>>> = std::thread::scope(|scope| {

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.

I'm learning a little about Rust here. Does this hold a tokio worker without letting other tasks schedule, even when the task blocks on I/O?

OTOH, this seems to be CPU bound, so probably better to let it burn without interruption?

This branch has not been deployed

No deployments
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.

3 participants