SSE update enhancements for multi-history view and published histories#22614
Merged
jmchilton merged 4 commits intogalaxyproject:devfrom May 1, 2026
Merged
SSE update enhancements for multi-history view and published histories#22614jmchilton merged 4 commits intogalaxyproject:devfrom
jmchilton merged 4 commits intogalaxyproject:devfrom
Conversation
The history-store SSE handler only refreshed the current history. With polling disabled under enable_sse_updates=true, other histories visible in the multi-history view stayed stale until the user switched into them. Iterate over all changed history ids in the SSE event and refresh each one already tracked in storedHistories via updateContentStats and historyItemsStore.fetchHistoryItems, so multi-view panels for non-current histories pick up uploads as they happen. Adds an integration_selenium test that uploads to a non-current history through the API and asserts the new item renders in the multi-view without a current-history switch — exercising the SSE-driven refresh path end-to-end.
The refresh button's title and red-when-stale logic was designed for
the polling era and is misleading under SSE: lastCheckedTime only ticks
on real history changes (so the "Last refreshed Xs ago" text describes
nothing actionable), the 2-minute idle cutoff goes red even when SSE
is healthy, and the click handler called the idempotent
startWatchingHistory which short-circuits once initialized — clicking
did nothing.
In SSE mode the button now shows "Refresh history" with a normal link
variant, and only turns red ("Live updates disconnected. Click to
refresh.") when the SSE socket actually drops after a previous
successful open (gated on the new sseEverConnected latch in
useNotificationSSE so the brief initial-connect window isn't flagged
as an outage). Clicking now forces a real refresh via
refreshHistoryFromPush in both modes. Polling-mode title/variant is
unchanged.
Adds a HistoryCounter Vitest covering the SSE healthy / initial /
lost transitions plus the polling-mode legacy behavior.
Owner dispatch only delivers history_update events to the user who owns the changed history. Watching a shared or pinned non-owned history (e.g. via the multi-history view) silently received nothing under SSE because the dispatch path keys on history ownership and there was no way for a viewer to opt in. Add an additive viewer-subscription channel: the client POSTs the history ids it wants pushes for to /api/events/history-subscriptions, the dispatcher fans the record out to every webapp worker over Kombu, and history_update fanout pushes to subscribed viewers in addition to the owner. Subscriptions are refcounted on the client and replayed on every EventSource onopen so reconnects don't drop them. Per-worker subscription state is purged when a user's last connection on that worker closes; the client's onopen replay re-asserts the desired set. No authorization check on the dispatch path: SSE events only carry history ids, the client follows up with a normal access-controlled REST fetch, and leaking "history changed at T" pings is acceptable. MultipleViewItem now subscribes whenever it renders a history the current user does not own and SSE is enabled; owned histories continue through owner dispatch with zero subscribe round-trip. Tests: integration test exercising subscribe -> mutate -> receive -> unsubscribe -> stop receiving end-to-end, plus vitest for the client refcount and replay logic.
Member
Author
|
Live on test.galaxyproject.org now, seems to work well. |
|
This PR was merged without a "kind/" label, please correct. |
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.
Three follow-ups making SSE history updates work everywhere the polling path used to cover.
Push SSE history updates to non-current multi-view panels — the SSE handler only refreshed the current history, so other panels in the multi-history view stayed stale once polling was disabled. It now iterates the changed history ids in the event and refreshes every one already tracked in
storedHistories.Make history refresh button SSE-aware — the refresh button's "last refreshed Xs ago" text and 2-minute red-stale cutoff were polling-era artifacts that misrepresented SSE state, and the click handler hit an idempotent no-op. Under SSE the title becomes "Refresh history", goes red only when the socket actually drops after a successful open (gated on a new
sseEverConnectedlatch), and clicking forces a real refresh viarefreshHistoryFromPush.Push SSE history updates to non-owner viewers — owner dispatch keys on history ownership, so watching a shared or pinned non-owned history (multi-history view) silently received nothing. Adds an additive viewer-subscription channel: the client POSTs desired history ids to
/api/events/history-subscriptions, the dispatcher fans the record out to every webapp worker over Kombu, andhistory_updatefanout pushes to subscribed viewers alongside the owner. Subscriptions are refcounted client-side and replayed on everyEventSourceonopenso reconnects don't drop them; per-worker state is purged when a user's last connection on that worker closes. No authorization check on the dispatch path is needed — events only carry history ids and the client follows up with the normal access-controlled REST fetch.How to test the changes?
(Select all options that apply)
License