Anchor queries at the head, bound the replay window, stop acking failed writes - #34
Merged
Merged
Conversation
The archive numbered events from a single global counter incremented with a read-then-write under a JVM-local lock: not per project, and not safe with more than one service instance. Each project now has its own counter document advanced with a single findAndModify $inc, which MongoDB runs atomically server-side, so ordinals stay dense and gapless per project under concurrency. A startup migration seeds each project's counter to the maximum ordinal already archived, using setOnInsert so it is idempotent and never rewinds a counter that has advanced — newly minted numbers always stay above any bookmark a connected client still holds. After the archive write, the service now re-publishes the change bundle as a SequencedPackagedProjectChangeEvent carrying the assigned ordinal. The gateway will push that instead of the raw event, which is what lets it stamp truthful event tags — and because publication happens strictly after persistence, anything a client hears about is already fetchable through the pull path. Delivery hardening of this publish is #299's scope; the failure is not silently swallowed here. A compound index on (projectId, timeStamp) backs the pull query, and a read-only current-sequence accessor is exposed for the project-open anchor (#301). Fixes the service half of protegeproject/webprotege-gwt-ui#296 (part of protegeproject/webprotege-gwt-ui#303).
…s window The events query gains a latestOnly mode: instead of returning archived events it answers with an empty window positioned at the project's current head, read from the per-project sequence counter. A client opening a project uses it to start listening for live changes without downloading the past. The normal query is no longer unbounded: results come back ordered by sequence with a configurable cap (default 500), and the response's end tag marks the end of the returned window, so a far-behind client pages forward naturally over successive requests instead of receiving the whole archive in one response. The compound index from the sequence work backs the ordered query. The service half of protegeproject/webprotege-gwt-ui#301 (part of protegeproject/webprotege-gwt-ui#303).
A failed archive write was caught, logged, and acknowledged, so the event vanished from the catch-up history forever — exactly the store that reconnecting clients rely on to fill their gaps. The failure now propagates so the listener container redelivers the message, and a bounded retry policy with exponential backoff (tunable via webprotege.events.retry.*) turns transient store hiccups into recovered writes while stopping a genuinely poison message from hot-looping — after the attempts are exhausted it is rejected without requeue. Redelivery cannot duplicate archive rows because the event id is the Mongo document id, so a replayed save upserts; an integration test pins that. The listener container is built inside the ipc library, so the retry policy is attached through a bean post-processor. A retried attempt that already minted a sequence ordinal leaves a benign gap in the numbering; ordinals are opaque and monotonic to clients, so nothing waits on the missing value. The event-history companion to protegeproject/webprotege-gwt-ui#299 (part of protegeproject/webprotege-gwt-ui#303).
johardi
force-pushed
the
feat/301-no-history-replay
branch
from
July 27, 2026 19:22
263a4ee to
75a511f
Compare
This was referenced Jul 27, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Two commits, stacked on #33 (retarget to
epic/303-sseonce that merges); part of protegeproject/webprotege-gwt-ui#303.Commit 1 — the service half of protegeproject/webprotege-gwt-ui#301: the events query gains a
latestOnlymode returning an empty window at the project's current head (no Mongo read), and the normal query is bounded — ordered by sequence, capped at 500 (configurable), endTag = end of the returned window so far-behind clients page forward. Kills the unbounded full-history replay on every project open. Client half: protegeproject/webprotege-gwt-ui#313.Commit 2 — the event-history companion of protegeproject/webprotege-gwt-ui#299: a failed archive write was logged and ACKed, permanently deleting the event from catch-up history. It now rethrows so the container redelivers, with bounded exponential-backoff retry (BeanPostProcessor — the container is built inside the ipc library) and reject-without-requeue on exhaustion. Redelivery upserts by event id, so no duplicates (integration-tested).
Full module suite: 17/17 under JDK 17 (Testcontainers Mongo + Rabbit).