You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
adapter: refuse a write timestamp past the write timeline's bound
`GroupCommitter::commit_timestamped` documented an obligation it never
discharged. Its doc lists the wall-clock throttle among the things it skips,
"`target_timestamp` is the caller's to choose, and it must not run the write
timeline ahead of the clock", and then it committed whatever it was handed.
The oracle is monotone and durable, so that matters. A write far ahead of the
clock is applied to the oracle, and every later write and strict-serializable
read on the timeline then blocks until the clock catches up. Restarting does
not help, since the same timestamp also reaches the catalog shard's upper,
which boot re-applies to the oracle. Under `serializable` it is worse than a
block: those reads never consult the oracle, so they pick a timestamp near the
clock and the acknowledged write stays invisible.
The frontend read-then-write path can produce such a target today. It writes
at the frontier its subscribe observed, and for a selection over a
materialized view with a `REFRESH` option that frontier is legitimately hours
or days out.
So enforce the bound where it is stated. A target above
`write_ts_upper_bound(now)`, the ceiling `check_runaway_write_ts` already
measures against, is refused before the append and surfaces as a statement
error. This is a backstop rather than a fix. The right answer is for the
caller to take its timestamp from the oracle and use the frontier only to
certify what it read. What this guarantees is that the worst outcome is one
failed statement instead of a stalled timeline.
Two smaller things in the same area. `check_runaway_write_ts` soft panics
instead of logging, so a runaway fails a test rather than leaving a line in a
log, and degrades to that log line in production. Boot reports a catalog upper
that is already past the bound, the one channel that survives a restart and
was silent. It does not refuse to start, because the timeline is stalled
either way and a process that will not boot turns that into an outage plus a
crash loop.
Tests: an integration test drives the far-future target from a materialized
view with two refreshes, one seconds out so the view is readable at all and
one far away so the target is deterministic. It asserts the statement is
refused, the oracle did not move, and the timeline still takes writes.
0 commit comments