fix(cold start): serve pages when a service cannot answer - #687
Merged
Conversation
An instance with an empty API cache and no query instance the nanopub library admits could not serve any page at all, its own error page included. Session construction loads the user data, which needs a query call, and a failed one threw; Wicket then tried to render the error page, which needs a session of its own and failed the same way, so the request ended in an unhandled exception. It takes a cold cache and an unavailable query service at once -- a fresh deployment whose query service is still loading, or a fresh container started during an outage -- and it defeats the service note of #681, since nothing can say a service is unavailable when no page renders at all. User data now builds from whatever the services can tell and marks itself incomplete when one of them cannot answer. Incomplete data is used but never replaces complete data, and another load is attempted in the background at most every 30 seconds, so an instance that came up during an outage comes right on its own. Session construction treats a failed profile load as an anonymous session rather than as fatal, and loads the profile again once the user data is complete. The space and maintained-resource repositories keep their last snapshot when their query cannot be answered, as they already did for a missing response: that was the same failure one layer up, where the home page resolves the home resource. ApiCache.retrieveResponseIfAvailable is the null-instead-of-throw retrieval they use; retrieveResponseSync still throws, so a single view keeps reporting its own failure. The error page falls back to no title bar rather than to no page, since it is where the other pages' errors end up. The missing-introduction and pending-approval notices are held back while the user data is incomplete, so the degraded page does not report on what could not be loaded. Closes #684 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LqfrG3VXH5tzWXPvYJzXML
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.
Closes #684.
The failure
Reproduced on master with an empty API cache and a query service the library does not admit: the home page hung,
/publishanswered 500, and the log carried 37 unhandled exceptions — the chain from the issue,UserData.<init>←NanodashSession.loadProfileInfo← page construction, with the error page needing a session of its own and failing the same way.Behind it was a second one the issue does not name: with the session fixed,
HomePagestill 500s inMaintainedResourceRepository.findById→ApiCache.retrieveResponseSync. Both repositories already treated a missing response as "nothing yet" but a failing one as fatal.The fix
UserDatabuilds from whatever answered and carriesisComplete(). Nothing in it is fatal any more except an unreadable local setting file — a misconfiguration, not an outage.User.install/shouldReplacenever let incomplete data replace complete data (a passing outage must not empty out good data);retryIfIncompletereloads in the background, at most every 30s, never on a request thread (PR Show outdated view results with a spinner instead of a bare spinner (#599) #600's rule).NanodashSessionconstruction cannot fail on profile loading;refreshProfileInfoIfIncomplete(), called from theNanodashPageconstructor, reloads the profile once the user data is whole again.ApiCache.retrieveResponseIfAvailable— null instead of throw, for the callers holding state whole pages are built from (SpaceRepository,MaintainedResourceRepository).retrieveResponseSyncstill throws, so a single view keeps reporting its own failure.ErrorPagerenders without its title bar rather than not at all.JustPublishedMessagePanel: the missing-introduction and pending-approval notices are held back while the user data is incomplete — a degraded page must not tell users something false about their own account.Verification
A second jetty on :37374 against a stub query service answering
503+Nanopub-Query-Status: LOADING_INITIAL, withNANODASH_API_CACHE_FILE=none— the issue's fresh-deployment scenario — then the stub switched to forwarding to the real service://publish,/profile,/errorRecovery was watched end to end in the browser: degraded page → service becomes ready → the instance serves its full home page again.
Tests: 1299 pass (8 new —
UserDataResilienceTest, two inApiCacheTest, one inErrorPageTest). The error-page test was checked against unfixed code: it fails there with the issue's own "Can't instantiate page" error.Two notes for the reviewer
NANOPUB_QUERY_EVICTION_COOLDOWN_SECONDS(default 300), so the 30-second retry can only succeed once that lapses. It does recover unattended — just up to five minutes after the service is ready.ServiceHealth.checkQuerycan report HEALTHY while dispatch still fails.QueryCall.getApiInstances()returned non-empty the moment the cooldown lapsed while actual calls still threwNotEnoughAPIInstancesException, so the Show a note when no READY registry or query service is available, instead of scattered "API call failed." #681 note can clear before queries work. Left alone here — that is the note's own logic and deserves its own decision.🤖 Generated with Claude Code
https://claude.ai/code/session_01LqfrG3VXH5tzWXPvYJzXML