Skip to content

feat(rollout): add opt-in ?flush=true for read-your-write appends - #191

Merged
beinan merged 1 commit into
mainfrom
feat/rollout-sync-visible-write
Jul 25, 2026
Merged

feat(rollout): add opt-in ?flush=true for read-your-write appends#191
beinan merged 1 commit into
mainfrom
feat/rollout-sync-visible-write

Conversation

@beinan

@beinan beinan commented Jul 25, 2026

Copy link
Copy Markdown
Collaborator

Closes #186.

Problem

A rollout append is durable on return but not readable until the flush sweeper seals the memtable — by default up to 30s later. RolloutStore::flush exists but had no path through the HTTP API, so callers needing read-your-write were stuck lowering ROLLOUT_FLUSH_INTERVAL_SECS globally, which penalizes every store in the process and re-serializes appends that never needed the guarantee.

Motivating cases: an RL training loop verifying a trajectory it just wrote; write-then-assert integration tests; any client doing POST .../records then GET.

Change

POST /rollouts/{name}/records?flush=true seals before responding.

  • Default unchanged — asynchronous visibility, no extra cost, so the concurrency win from the decoupling is preserved.
  • Conservative parsing — only flush=true, flush=1, or bare flush opt in. flush=false, flush=yes, noflush=true, and flushed=true all do not, so a typo cannot silently make every append pay a seal.
  • Still taken under the read lock: flush is &self and safe against concurrent appends.
  • New rollout_appends_flushed_total counter so opt-in usage is visible in metrics.

Query parsing is by hand rather than via a Query extractor because the handler consumes the whole Request to dispatch on Content-Type.

Testing

flush_true_gives_read_your_write_and_default_does_not drives the real handler and asserts both halves: the default append leaves the row invisible (no sweeper running in the test), and ?flush=true makes it readable before the response returns.

Verified it is a real guard by stubbing the flush call out:

FAILED: ?flush=true must make the row readable before responding, got []

Plus flush_query_param_is_parsed_conservatively covering the accept/reject table.

cargo test -p lance-context-server → 51 passed, 0 failed. fmt + clippy clean.

Follow-up, deliberately not in this PR

The Rust client and Python binding do not yet expose the flag. Threading it through means a breaking signature change to RolloutStoreApi::add, which affects both the local and remote implementations — worth its own PR and version bump rather than inflating this one. The HTTP API is the layer the issue's motivating cases hit directly.

🤖 Generated with Claude Code

Since the flush decoupling, a rollout append is durable on return but not
readable until the sweeper seals the memtable — up to
ROLLOUT_FLUSH_INTERVAL_SECS (default 30s) later. `RolloutStore::flush` could
close that gap but was unreachable from the HTTP API, so a caller needing
read-your-write had no option short of lowering the global interval, which
penalizes every store in the process and re-serializes writes that do not
need the guarantee.

Add `?flush=true` to the rollout append endpoint: seal before responding,
trading latency for immediate visibility, with the asynchronous path
unchanged as the default. Motivating cases are training loops that verify a
trajectory they just wrote and write-then-assert tests.

Parsing is deliberately conservative — only `flush=true`, `flush=1`, or a
bare `flush` opt in, so a typo cannot silently make every append pay a seal.
It reads the query off the URI because the handler consumes the whole
`Request` to dispatch on Content-Type.

Closes #186

Co-Authored-By: Claude <noreply@anthropic.com>
@beinan
beinan merged commit 5376162 into main Jul 25, 2026
9 checks passed
@beinan
beinan deleted the feat/rollout-sync-visible-write branch July 25, 2026 07:21
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.

feat(rollout): expose a synchronous-visibility write option on the HTTP API

1 participant