fix(app-detail): coalesce the 10x duplicate Application record fetch - #50
Conversation
One load of /applications/hydra-console issued TEN identical GET /apps/openregister/api/objects/openbuild/application/hydra-console requests, all 200. Two components resolve the same record independently — CnDetailPage's #header and #before-body slots forward only presentational props, not the resolved record — and each is driven by three triggers (mounted, the objectId watcher, the object watcher). None knew about the others. Adds a module-scoped in-flight map keyed by uuid so concurrent callers share one request and one promise. Deliberately NOT a cache: the entry is dropped as soon as the request settles, so every fresh call still reaches the server and no component can observe a stale record. Only the stampede is collapsed. Both components now also carry a staleness token, so a response for a superseded uuid cannot overwrite a newer record on a route change. Cleanup uses .then(cleanup, cleanup) rather than .finally(cleanup): .finally() returns a new promise that re-throws, and nothing handled it, so every failed fetch logged an unhandled rejection. Caught by the new spec. Ten round-trips for one record is most of why this page takes so long to settle, and a slow-settling page is why it *looks* broken under load — the placeholder header and empty register render until the real record lands. I mis-filed those placeholders as render defects in #49 and have corrected that issue; the redundant fetching is the part that survived verification. Also fixes a pre-existing failing test. SchemaDesigner.spec.js still asserted the raw un-namespaced slug ("new") for addSchema, but #41 deliberately namespaces it ("hello-world-new") so the schema is visible in the list it was created from and attached to the app's register. The assertion encoded the pre-#41 behaviour and had been failing since that fix landed; it now asserts the namespacing on both the payload and the follow-on navigation. Refs #49
…d fetch too Six components mix in applicationContext — the detail-page actions component and five sidebar tabs (Diff, Manifest, Export jobs, Icon, Versions) — and each issued its own GET for the same Application record. Measured caveat, recorded honestly: on /applications/hydra-console this changes nothing, because every one of those consumers short-circuits before fetching (the slot already passes `object`, so obLoadApp returns early). Instrumenting the shared helper on the live page showed it is called ZERO times there, while nine identical requests still go out. Those nine originate in nc-vue, not in openbuild — see the PR description. So this commit removes real duplicate-fetch paths for the routes where the record is NOT passed in, but it does not fix the 10x symptom on the app-detail page. Not claiming otherwise.
Important correction — this PR does NOT fix the 10x symptomI verified on the live instance instead of assuming, and the result contradicts my own PR description. Recording it here rather than quietly leaving the claim standing. I instrumented the shared helper and loaded Every openbuild-side consumer — the header, the dashboard, and all six The nine requests originate in nc-vue ( What this PR is still worth
RecommendationTreat this as hygiene, not as the fix for #49. The actual fix belongs in nc-vue's object store. I'd rather flag that than let a merged PR imply the symptom is resolved — anyone checking would find the same nine requests. Happy to open the nc-vue investigation as a follow-up if you want it chased. |
|
Follow-up: my "this PR does not fix the symptom" comment above was wrong, and I've corrected it on #49. That measurement was taken against the cached bundle — Re-measured in a browser context that had never loaded the instance:
Page renders correctly throughout. #49 closed as fixed by this PR ( |
The defect
One load of
/applications/hydra-consoleissued ten identical requests for the same record, all 200:ApplicationDetailHeaderandApplicationDetailDashboardeach resolve the record themselves — CnDetailPage's#header/#before-bodyslots forward only presentational props, not the resolved record — and each is driven by three independent triggers (mounted(), theobjectIdwatcher, theobjectwatcher). None knew about the others.The fix
A module-scoped in-flight map keyed by uuid, so concurrent callers share one request and one promise.
Deliberately not a cache: the entry is dropped as soon as the request settles, so every fresh call still reaches the server and no component can observe a stale record. Only the concurrent stampede is collapsed. Both components also gained a staleness token so a response for a superseded uuid can't overwrite a newer record on a route change.
One subtlety worth flagging: cleanup uses
.then(cleanup, cleanup)rather than.finally(cleanup)..finally()returns a new promise that re-throws the original rejection, and nothing handled it — so every failed fetch logged an unhandled rejection in the console. The new spec caught that in my own first draft.Correction to #49
I originally filed #49 claiming the header rendered "Untitled application", the register showed
openbuild--with 0 schemas, and four KPI tiles spun forever on a 404. Three of those four were wrong. I observed the page on a host that was thrashing (swap fully exhausted, load ~200 from concurrent e2e suites) and reported a half-loaded page as broken rendering. Re-tested on an idle host:openbuild--, 0 schemasopenbuild-hydra-console-production, SCHEMAS 1fetchInsights()already had correct 404 handling andrefreshApplication()already assigned the record properly. I've corrected and retitled that issue. The redundant fetching is the part that survived verification — and it is most of the reason the page takes so long to settle, which is what made it look broken in the first place.Also fixed: a pre-existing failing test
tests/views/SchemaDesigner.spec.jsasserted the raw un-namespaced slug ("new") foraddSchema, but #41 deliberately namespaces it ("hello-world-new") so the schema is visible in the list it was created from and attached to the app's register. The assertion encoded pre-#41 behaviour and had been failing ever since that fix landed. It now asserts the namespacing on both the payload and the follow-on navigation.Tests
New spec locks the measurable contract — N concurrent callers → exactly one request:
Full suite: 1347/1347 passing (was 1346/1347 — the SchemaDesigner failure above).
🤖 Generated with Claude Code