Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

flowctl: re-tool preview on the runtime-next stack - #3117

#3117
Merged
jgraettinger merged 4 commits into
masterestuary/flow:masterfrom
johnny/previewestuary/flow:johnny/previewCopy head branch name to clipboard
Jul 8, 2026
Merged

flowctl: re-tool preview on the runtime-next stack#3117
jgraettinger merged 4 commits into
masterestuary/flow:masterfrom
johnny/previewestuary/flow:johnny/previewCopy head branch name to clipboard

Conversation

@jgraettinger

@jgraettinger jgraettinger commented Jul 4, 2026

Copy link
Copy Markdown
Member

What & why

flowctl preview now runs on the runtime-next + shuffle stack instead of the
legacy V1 runtime::harness. It was developed on this branch as flowctl raw preview-next, then promoted to be the preview; the V1 preview harness — the
last consumer of runtime::harness — is deleted along with the now-dead
runtime/src/harness/* and its private exchange combinator.

The user-facing contract is unchanged: the CLI flags, stdout/stderr line shapes,
and exit codes that estuary/connectors CI depends on. --shards and
--debug-port are new, additive flags.

To get there, runtime-next's host-specific behavior — how a task publishes
documents, reads its sources, and emits logs — was factored into three factory
seams
. Production installs journal-backed factories; preview installs
output-capturing / fixture-replay factories from flowctl. runtime-next is now
preview-agnostic, so the lone Task.preview flag — the "write documents to
output instead of collections" wire control — is removed from the runtime proto
(field 2 reserved); how a task publishes and logs is now a host concern of the
installed factories, not a wire flag. (max_transactions stays as the
transaction bound, re-documented as a general preview/harness control.)

The three host seams (runtime-next)

Each seam is a factory the leader and shard Services are generic over (static
RPITIT, not dyn):

  • publish — a PublisherFactory / JournalPublisher seam replaces the
    Publisher enum (a Real variant plus a Preview variant that baked
    NDJSON-to-stdout output into the crate); the publisher now carries only
    document output, with its former observation calls moved to the logger seam.
  • shuffle (leader) — a new leader/shuffle.rs provides ShuffleSession(Factory)
    • ShuffleServiceFactory, factoring out the leader's previously-inline
      shuffle-Session opening and checkpoint sourcing.
  • log — new Logger / LoggerFactory seam sinking both connector logs
    (log) and structured LogEvents (event) — a #[non_exhaustive] enum
    (persist / applied / inferred-schema / container lifecycle) carrying borrowed,
    verbatim payloads so a host can intercept them structurally. LogEvent::to_log
    flattens each event into the ops-log line it replaced, and event defaults to
    that flattening, so a host overrides only the events it cares about. Production
    shards install FnLoggerFactory; leaders and tests install
    TracingLoggerFactory.

New harness helpers: seed_initial_connector_state / read_final_connector_state
seed and read shard zero's RocksDB by path; shuffle::log::BlockMeta is
re-exposed for segment writers.

Preview on the seams (flowctl)

  • publish: PreviewPublisherFactory writes captured/derived documents to
    stdout as NDJSON.
  • log: PreviewLoggerFactory sinks the task log and powers --output-state
    / --output-apply (the legacy connectorState / applied lines) by
    intercepting LogEvent::Persist / LogEvent::Applied structurally and
    flattening everything else through LogEvent::to_log. The run-end final reduced
    state comes from re-opening shard zero's RocksDB once the session loop releases
    it.
  • shuffle: PreviewShuffleFactory selects per run between a live in-process
    journal-reading Session and a --fixture replay (FixtureOpener).
  • --initial-state seeds shard zero's RocksDB via seed_initial_connector_state.
    --delay, fixtures, and --sessions planning are unchanged.

Tests / soak

  • Deleted plans/runtime-v2/preview-harness.md, a closed bring-up doc for raw preview-next.
  • Soak README updated to the new command name.

Notes for reviewers

@jgraettinger jgraettinger changed the title Johnny/preview flowctl: re-tool preview on the runtime-next stack Jul 5, 2026
@jgraettinger
jgraettinger force-pushed the johnny/preview branch 5 times, most recently from f6adb49 to 53cacd6 Compare July 6, 2026 16:34
@jgraettinger
jgraettinger marked this pull request as ready for review July 6, 2026 18:21

@williamhbaker williamhbaker left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

A couple of questions

Comment thread crates/runtime-next/src/logger.rs Outdated
schema,
..
} => {
fields.push(("collection", json_field(collection_name)));

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Wanting to confirm this field name...should it be collection_name? Since the L1 inferred schemas rollup is looking for that:

$fields->>'collection_name' as collection_name,

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Yes good catch. I'll fix, but I'd also like to work schema inference into the soak test so it provides a positive verification it's working.

// The patch payload is valid JSON (a tab-delimited JSON array;
// see `crate::patches`), so it embeds verbatim.
fields.push(("patches", persist.connector_patches_json.clone()));
(ops::LogLevel::Debug, "persisted connector-state delta")

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Will these get filtered out for info and above level tasks, since they don't go through Tokio's tracing subscriber?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Yes. We're paying a bit of cost that tracing avoids, in that we're actually building the structured event to have it then be filtered by tracing at point of emission. I wasn't concerned though (Bytes ref-count clone, or bare &ref)

@jgraettinger

Copy link
Copy Markdown
Member Author

PTAL -- fixed the schema inference regression, and added new coverage in the soak test to provide E2E verification.

williamhbaker
williamhbaker previously approved these changes Jul 8, 2026

@williamhbaker williamhbaker left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

LGTM

…tories

Factor runtime-next's host-specific behavior — how a task publishes, shuffles,
and logs — out of the crate into three uniform, monomorphized host seams, each a
factory the leader and shard `Service`s are generic over:

- publish: replace the `Publisher` enum — a `Real` variant plus a `Preview`
  variant that baked NDJSON-to-stdout output into the crate — with a
  `PublisherFactory` / `JournalPublisher(Factory)` seam. The publisher now
  carries only document output; its former observation calls move to the Logger
  seam.
- leader: factor the leader's inline shuffle-`Session` opening and checkpoint
  sourcing into a new `leader/shuffle.rs`, providing `ShuffleSession` /
  `ShuffleSessionFactory` (static RPITIT, not `dyn`) plus a
  `ShuffleServiceFactory`.
- log: new `Logger` / `LoggerFactory` seam — the task's out-of-band log and
  event stream. A `Logger` sinks both the connector's own logs (`log`) and
  structured runtime `LogEvent`s (`event`): a `#[non_exhaustive]` enum
  (persist / applied / inferred-schema / container lifecycle) whose variants
  carry borrowed, verbatim payloads so a host may intercept them structurally.
  Its canonical `LogEvent::to_log` flattens each event into the ops-log line it
  replaced, and `event` defaults to that flattening — so a host overrides only
  to intercept, delegating the rest. Production shards install `FnLoggerFactory`
  (sinking to the task-log writer); leaders and tests install
  `TracingLoggerFactory`.

Remove the now-unnecessary `Task.preview` flag from the runtime proto
(reserving field 2): how a task publishes and logs is a host concern decided by
the installed factories, not a protocol flag. `max_transactions` stays as the
transaction bound, re-documented as a general preview/harness control rather
than tied to the removed flag. Make the `shard::rocksdb` module public —
exposing `RocksDB` (with its `open`, `scan`, and now-public
`put_connector_state_base`) and re-exporting its `DecodeError` — so a host
harness can seed shard zero's RocksDB by path and read back its final reduced
state itself, and re-expose `shuffle::log::BlockMeta` for harness segment
writers.
Drive `flowctl raw preview-next` through runtime-next's three factory seams by
installing preview-specific implementations of each:

- publish: an output-capturing `PreviewPublisherFactory` writes captured /
  derived documents to stdout as NDJSON.
- log: a `PreviewLoggerFactory` sinks the task's log stream and powers
  `--output-state` / `--output-apply` (the legacy `connectorState` / `applied`
  lines) by intercepting runtime-next's `LogEvent::Persist` / `LogEvent::Applied`
  structurally — decoding state deltas from its tab-framed patch wire form — and
  flattening every other event through `LogEvent::to_log` into the same log
  handler as the connector's own logs. The `--output-state` run-end final reduced
  state is emitted by re-opening shard zero's RocksDB (`read_preview_state`)
  once the session loop has released it.
- shuffle: a `PreviewShuffleFactory` enum selects per run between a live
  in-process journal-reading shuffle Session and a `--fixture` replay
  (`FixtureOpener`, a `ShuffleSessionFactory` that writes log segments and feeds
  synthetic checkpoint Frontiers).

`--initial-state` seeds shard zero's RocksDB by path, and `--output-state`'s
run-end read reopens it — both managed by preview itself through the now-public
`runtime_next::shard::rocksdb` handle (the local `seed_preview_state` /
`read_preview_state` helpers over `RocksDB::open`, `put_connector_state_base`,
and `scan`). Each driver also encodes its task spec once and threads the shared
bytes to its shards rather than re-encoding per shard. `--delay`, eager/streaming
fixtures, and `--sessions` planning are unchanged.
`flowctl preview` now runs on the runtime-next + shuffle stack that was
developed on this branch as `flowctl raw preview-next`, which the prior
commits verified to be behaviorally compatible with the legacy preview.

Move the `raw/preview_next/` module into `preview/`, delete the old
V1-runtime `preview/{mod,journal_reader}.rs`, and drop the `raw
preview-next` command wiring. The CLI flag surface, stdout/stderr line
shapes, and exit-code contract that the estuary/connectors CI harnesses
depend on are preserved; `--shards` and `--debug-port` are additive.

The old preview was the sole consumer of `runtime::harness` (and its
private `exchange` combinator), so remove that now-dead module too.

Update the soak README to the new command name, and delete the
runtime-v2 preview harness guide: a closed, point-in-time document
describing `raw preview-next` as it stood during bring-up.
Gate the soak's downstream chain on schema inference so it provides
continuous, positive proof that inference works — the failure mode that
motivated this (a silently mis-keyed inferred-schema ops log) presents as
`accounts` never coming up, which the previous fixed-schema soak missed.

- events/{alpha,beta,gamma} take an inferred readSchema
  (allOf: [flow://write-schema, flow://inferred-schema]); events.schema.json
  stays the writeSchema / wire contract.
- source-soak emits a partial SourcedSchema per binding: id/ts/set/oracle are
  sourced and stable; seq/balanceDelta/transfer are left to doc-driven
  inference, so their bounds churn under load while the existing exactly-once
  and conservation probes ride through each spec-update-driven restart.
- accounts keeps its explicit schema (a derivation has no SourcedSchema, and
  seeded from a complete schema it never widens → no inferred_schemas row,
  expected). README/derivation comments document this.

No runtime changes.

@williamhbaker williamhbaker left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

LGTM

@jgraettinger
jgraettinger merged commit e9024de into master Jul 8, 2026
11 checks passed
@jgraettinger
jgraettinger deleted the johnny/preview branch July 8, 2026 20:34
mdibaiee added a commit to estuary/homebrew-flowctl that referenced this pull request Jul 15, 2026
## What's Changed
* runtime: periodically store primary FSMHints of V2 shards by @williamhbaker in estuary/flow#3027
* mise build:flowctl for a local flowctl binary by @mdibaiee in estuary/flow#3031
* go.mod: bump gazette to latest by @williamhbaker in estuary/flow#3033
* data-plane-controller: dns and s3 passthroughs by @qaoj in estuary/flow#3022
* Self-service private-link configuration by @jshearer in estuary/flow#2944
* supabase: scoped refresh tokens so CI writes via PostgREST instead of direct psql by @skord in estuary/flow#3013
* docs: Describe SQL Server replica suport by @willdonnelly in estuary/flow#3040
* proto-gazette: regenerate to pick up SUSPEND_KEEP by @williamhbaker in estuary/flow#3041
* docs: correct custom-column-types backfill behavior for DDL changes by @jwhartley in estuary/flow#3036
* Refresh token GraphQL operations and token exchange endpoint by @GregorShear in estuary/flow#3020
* docs: shopify stream & resource config additions by @Alex-Bair in estuary/flow#3034
* validation/collection: exclude `flow://inferred-schema` from the managed-defs redact check by @jshearer in estuary/flow#3046
* docs: update Bigtable connector permissions example by @mwillman-estuary in estuary/flow#3045
* docs: disable auto-discover for multi-binding file source captures by @jwhartley in estuary/flow#3037
* docs: add streams and API pinning details to stripe-native by @nicolaslazo in estuary/flow#3055
* data-plane-controller: restart setting by @qaoj in estuary/flow#3048
* deploy: route control-plane services through the VPC NAT for a stable egress IP by @skord in estuary/flow#3044
* docs: document the Exclude Flow Document (no_flow_document) materialization option by @jwhartley in estuary/flow#3018
* oidc-discovery-server: survive direct VPC egress cold-start on deploy by @skord in estuary/flow#3062
* docs: clarify retain_existing_data_on_backfill requires allow_existing_tables_for_new_bindings by @jwhartley in estuary/flow#3049
* data-plane-controller: restart on ansiblehost by @qaoj in estuary/flow#3066
* flow-web: Bump version to release by @jshearer in estuary/flow#3068
* ops-catalog: misc updates by @williamhbaker in estuary/flow#3067
* proto-flow: tolerate unknown JSON fields in connector protocols by @williamhbaker in estuary/flow#3059
* flowctl: add raw split-shards to scale out V2 tasks by @williamhbaker in estuary/flow#3021
* supabase: qualify replace_data_plane_releases DELETE for PostgREST by @skord in estuary/flow#3076
* docs: kcat recipes for testing a Dekaf topic by @jwhartley in estuary/flow#3072
* Docs: document per-prefix alert scoping and configurable thresholds by @jwhartley in estuary/flow#3039
* notifications: trial bucket retention is 20 days, not 30 by @jwhartley in estuary/flow#3074
* runtime/container: use ops::decode::Decoder for log lines by @williamhbaker in estuary/flow#3091
* data-plane-controller: remove restart flag by @qaoj in estuary/flow#3087
* runtime-next: durably seed initial connector state as {} to match V1 by @williamhbaker in estuary/flow#3081
* runtime-next: don't abandon the final capture transaction on connector EOF by @jgraettinger in estuary/flow#3090
* Docs: Remove an IP address from GCP allowed list by @jwhartley in estuary/flow#3080
* docs: remove static data plane IP list, reference dashboard by @jwhartley in estuary/flow#3097
* dekaf: improve logging by @williamhbaker in estuary/flow#3078
* agent: make startup logging consistent by @williamhbaker in estuary/flow#3098
* shuffle: make the shuffle disk limit configurable per-task by @jgraettinger in estuary/flow#3096
* runtime-next: automatically split journals under sustained append-rate throttling by @dgreer-dev in estuary/flow#3029
* billing: add tenant billing contact fields and per-tenant controller by @jshearer in estuary/flow#2902
* flowctl-go(api test): raise test-shard readiness window from ~3s to ~30s by @jshearer in estuary/flow#3101
* runtime: strip the V2 committed-close marker for derivations by @williamhbaker in estuary/flow#3100
* deps: bump aws-lc-sys and lz4_flex for security advisories by @skord in estuary/flow#2875
* data-plane-controller: fix db timeout by @qaoj in estuary/flow#3105
* agent-api: survive direct VPC egress cold-start on instance startup by @skord in estuary/flow#3109
* docs: avoiding backfills during a database failover or host change by @jwhartley in estuary/flow#3086
* docs: clarify column-level SELECT grants unsupported for MySQL/MariaDB CDC by @jwhartley in estuary/flow#3085
* json: require RFC3339 'T' separator in date-time format validator by @jacobmarble in estuary/flow#3116
* control-plane: reserve privileged tenant names by @jwhartley in estuary/flow#3083
* agent-api: harden startup and shutdown on Cloud Run by @skord in estuary/flow#3114
* Incidental fixes: gazette Append retry, connector-init image, materialize tear-down by @jgraettinger in estuary/flow#3121
* agent: flag to create new captures as runtime-v2 by @williamhbaker in estuary/flow#3107
* runtime-next: add minimum interval for post-txn triggers by @dgreer-dev in estuary/flow#3110
* flowctl: chunk API requests by url size rather than fixed count by @mdibaiee in estuary/flow#3123
* dekaf: use projection_constraints only, no constraints by @mdibaiee in estuary/flow#3124
* flowctl: normalize --prefix trailing slash to avoid misleading PermissionDenied by @GregorShear in estuary/flow#3075
* docs: retain_existing_data_on_backfill no longer requires allow_existing_tables_for_new_bindings by @jwhartley in estuary/flow#3125
* Restart Materializations sessions before IAM token expiry by @dgreer-dev in estuary/flow#3130
* docs: add "bring your own app" instructions for source-quickbooks by @nicolaslazo in estuary/flow#3135
* flowctl: re-tool `preview` on the runtime-next stack by @jgraettinger in estuary/flow#3117
* Adding support for updating the quotas when payment info changes by @bbartman in estuary/flow#3127
* control-plane-api: add unauthenticated publicDataPlanes GraphQL query by @GregorShear in estuary/flow#3129
* go/bindings: update snapshots for materialize-sqlite ser_policy by @dgreer-dev in estuary/flow#3142
* Revert "flowctl: replace `preview` with the runtime-next implementation" by @jgraettinger in estuary/flow#3141
* local: per-checkout stacks — every checkout runs its own isolated stack by @jgraettinger in estuary/flow#3137
* dekaf: log tls handshake eof instead of returning as error by @danielnelson in estuary/flow#3143
* Updating migrations to better support testing. by @bbartman in estuary/flow#3146
* shuffle: raise causal-hint stall timeout to 15m; lower prune horizon to 2GB by @jgraettinger in estuary/flow#3148
* ci: fix Platform Build by packaging Go binaries from per-checkout $GOBIN by @jgraettinger in estuary/flow#3149
* capture+materialize: Support Nonsensitive Field Overlays by @willdonnelly in estuary/flow#3119
* Docs: The Great Description-ing by @aeluce in estuary/flow#3057
* runtime: fix recursive read-lock deadlock in capture-v2 buildJoin by @jgraettinger in estuary/flow#3156
* validation: surface write-schema redact annotations on projections of split-schema collections by @GregorShear in estuary/flow#3147
* control-plane-api: retry Stripe customer search on index-lag misses by @jgraettinger in estuary/flow#3157
* flowctl: multi-shard --fixture for raw preview-next by @mdibaiee in estuary/flow#3154
* dekaf: prevent cached `TimeoutNoData` fetch responses from incorrectly signaling EOF by @jshearer in estuary/flow#3153
* mise: remove Lima VM host share and other VM tweaks by @jgraettinger in estuary/flow#3163
* docs: document connecting Azure Private Link to native Azure resources by @jwhartley in estuary/flow#3138
* docs: explain why exclusiveCollectionFilter needs few enabled bindings by @jwhartley in estuary/flow#3126

## New Contributors
* @bbartman made their first contribution in estuary/flow#3127

**Full Changelog**: estuary/flow@v0.6.10...v0.6.11

Co-authored-by: mdibaiee <mdibaiee@users.noreply.github.com>
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.

2 participants

Morty Proxy This is a proxified and sanitized view of the page, visit original site.