|
@@ -154,16 +154,33 @@ when the event carried one. Two behaviours follow from that placement rather
|
|
|
than from extra code: a handler that raises leaves the old cursor in place, so
|
|
than from extra code: a handler that raises leaves the old cursor in place, so
|
|
|
the event is redelivered, and an unreplayable event cannot wipe a good position.
|
|
the event is redelivered, and an unreplayable event cannot wipe a good position.
|
|
|
|
|
|
|
|
-### Why a restart still leaves a gap
|
|
|
|
|
-
|
|
|
|
|
-Persisting the cursor would extend the resume across process restarts, and the
|
|
|
|
|
-piece is cheap — one value. What is not cheap is the decision around it: a
|
|
|
|
|
-cursor restored from disk may be older than the server's retention, in which
|
|
|
|
|
-case the subscription falls back (initialize-from-snapshot or live-only) and the
|
|
|
|
|
-"backfill" silently becomes the same gap it was meant to close. That is a
|
|
|
|
|
-product decision about how much history to replay and what to tell the user when
|
|
|
|
|
-it cannot be, not a detail to smuggle in with a file write. Until it is made
|
|
|
|
|
-explicitly, the promise stops at the process boundary, and README says so.
|
|
|
|
|
|
|
+### What a restart keeps
|
|
|
|
|
+
|
|
|
|
|
+Persisting the cursor extends the resume across process restarts, and the piece
|
|
|
|
|
+is cheap — one value. The cost is in the *bounds*, which is why three of them
|
|
|
|
|
+ship with it:
|
|
|
|
|
+
|
|
|
|
|
+- **Only a clean stop writes.** `disconnect()` stores the last applied cursor;
|
|
|
|
|
+ a crash stores nothing, so the gap it leaves is the same gap that existed
|
|
|
|
|
+ before. Writing per event instead would buy crash coverage at the price of a
|
|
|
|
|
+ file write on every presence change — a trade this plugin does not make
|
|
|
|
|
+ silently (AGENTS.md says so, and the file is deliberately written in one
|
|
|
|
|
+ place).
|
|
|
|
|
+- **Only a fresh position is used.** `CHATTO_RESUME_WINDOW` (default 900 s)
|
|
|
|
|
+ decides how much of an outage may be replayed. Without it, a gateway that was
|
|
|
|
|
+ down overnight would answer yesterday's messages on startup; with it, an old
|
|
|
|
|
+ position is discarded and the stream starts at the live boundary. That is
|
|
|
|
|
+ also what the server would do anyway — chattolib requests
|
|
|
|
|
+ `INITIAL_STATE_LIVE_ONLY`, so a cursor it cannot resume degrades to the live
|
|
|
|
|
+ boundary rather than failing.
|
|
|
|
|
+- **Only the profile that wrote it reads it.** The file lives under the
|
|
|
|
|
+ profile's own home and carries `_profile_scope_key()`, the same key the
|
|
|
|
|
+ configuration cache and the platform registry use. Under multiplexing, a
|
|
|
|
|
+ position written by one profile must never be spent on another profile's
|
|
|
|
|
+ stream — the events differ even when the server is the same.
|
|
|
|
|
+
|
|
|
|
|
+A stored value that fails any of these checks is refused, not repaired: the
|
|
|
|
|
+adapter then behaves exactly as it did before the file existed.
|
|
|
|
|
|
|
|
## Configuration resolution
|
|
## Configuration resolution
|
|
|
|
|
|