Skip to content

No supported way to rewind a sink watermark: recovering a drop-but-advance gap is a hand-edit that races the daemon #989

Description

@bgmcmullen

Summary

#958 established that a withheld source is dropped and the watermark still advances, so the un-shipped rows stay in the local cache while the sink believes it has moved past them. That issue explicitly defers recovery ("separate ops task, not this issue"). This issue is that deferred half: there is no supported way to perform it.

Recovering a drop-but-advance gap requires hand-editing a private state file that the running daemon overwrites roughly once a minute. There is no verb for it, no --since, no dry-run, and no way to see whether the edit survived.

What's missing

hyp sink has exactly one subcommand:

Subcommands:
  maintain  Run export maintenance (snapshot expiration; data-file compaction with --compact)

Same in deployed 1.22.0 and current master 1.24.0. Nothing resends, rewinds, or resets a watermark. --since exists only on hyp backfill, which imports client history into the cache — the opposite direction.

So the only route is editing:

<stateDir>/plugins/@hypaware/central/sink-instances/central/watermarks/ai_gateway_messages/source=claude.json
{ "v":1, "continuation":{"v":1,"seq":"674822"}, "exportedRowCount":255086, "updatedAt":"..." }

Why the hand-edit races

forwardPartition re-reads the watermark from disk every tick (central/src/sink.js:218), which is good — an edit needs no restart. But the same tick rewrites it at the end (sink.js:346). Measured on the Neutral container:

04:03:28  seq 675930
04:04:37  seq 675948     → 69s apart

That is the entire window. Edit outside it and the next tick silently re-advances past the rewind; nothing reports that the edit was discarded, because from the sink's perspective nothing unusual happened.

Avoiding the race by stopping the daemon is not free: the daemon owns the capture gateway (127.0.0.1:18521), which on this box had live ESTABLISHED connections from running agents. Stopping it to fix an export bookkeeping value interrupts unrelated in-flight capture.

Concrete stakes

On Neutral today: 488,016 rows cached, exportedRowCount frozen at 255,086 since 08-12, ~236,650 rows recoverable. The correct resume point (last seq at/before the last-landed row 08-12T00:50:47Z) is 355254, verified against the complete 488,088-row table and cross-checked across four independent compaction generations, which agree exactly (next seq after the boundary: 355257).

Deleting the watermark instead is not a workaround: with no since, readRowsSince full-scans and includeLegacy defaults true, re-sending all 255k already-delivered rows on chunk boundaries that won't match the idempotency ledger (#958 makes the same point).

Fix directions

  • A first-class verb, e.g. hyp sink rewind <instance> --dataset <d> --partition <p> --to-seq <n> (or --since <timestamp>, resolving the seq internally, since operators reason in time, not seq).
  • Make it safe against the tick: take the sink instance's lock, or apply the rewind through the driver rather than the file.
  • --dry-run reporting how many rows the rewind would re-send.
  • Failing a verb: make the watermark file's hand-edit path documented and safe — detect an externally-modified watermark (compare against last-written value) and honor it rather than clobbering it.
  • Related: an operator needs to know the seq for a wall-clock instant, but _hyp_ingest_seq is in INTERNAL_FIELDS and stripped from both readRows and the query layer (SELECT _hyp_ingest_seq → "Column not found"), so deriving it today means reading the parquet files directly.

🤖 Generated with Claude Code

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    neutral:fixDelegate this issue to neutral for an autonomous fix attempt (reproduce -> fix -> PR)neutral:stuckneutral attempted this but cannot complete it autonomously — needs a human

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions