Repository navigation
ux(reshape): surface freshness/staleness banner when cached Cost Explorer recs are old #150
Description
Summary
PR #79 replaced live DescribeReservedInstancesOfferings calls with reads from the cached AWS Cost Explorer purchase recommendations table. The freshness of the displayed cross-family alternatives is now coupled to the schedulers collection cadence rather than being live AWS truth.
Failure mode: A stuck or paused scheduler, an Azure-deploy outage (PR #142 just unblocked one), or a regional throttling event leaves the cache stale. The reshape page silently shows old recommendations as if they were current. Users receive no signal that what theyre looking at may be hours/days behind.
Current behaviour
internal/api/exchange_lookup.go::purchaseRecLookupFromStorereads fromrecommendationstable without checkingcollected_at.- Empty cache is handled gracefully (primary target only). Stale cache is not detected at all — the API returns alternatives whose age the UI cannot distinguish from "fresh".
- The dashboard already has a freshness banner pattern for the recommendations page itself (
frontend/src/freshness.ts+ the per-providerlast_collection_errorplumbing). Reshape page does not consume it.
Expected behaviour
Reshape page surfaces the age of the underlying recommendations data when it exceeds a threshold (proposal: 12h soft warning, 24h hard warning). Banner text mirrors the existing freshness component:
Cross-family alternatives are based on Cost Explorer recommendations last collected 23h ago. Some prices may be stale.
Proposed fix
- Extend
purchaseRecLookupFromStoreto also return the maximumcollected_atacross the scoped recs, OR expose a sibling helperlatestCollectionTime(ctx, region, accountID). - Plumb the timestamp through to the reshape API response (extra optional field on the JSON shape).
- Frontend reuses
freshness.ts(or a thin wrapper) to render the banner inside the reshape modal/page. - Threshold tuning: 12h soft / 24h hard mirrors the existing dashboard freshness rule; revisit if the scheduler cadence changes.
Test plan
- API: store seeded with recs
collected_at = now-13h→ response includesfreshness.staleness = "soft". Recscollected_at = now-25h→staleness = "hard". Recscollected_at = now-30m→stalenessempty. - Frontend: snapshot test of the banner with each staleness state.
- E2E: kill the scheduler in a dev env, wait, refresh reshape page → banner appears.
References
- Lands-the-pattern PR: perf(exchange): rec-driven cross-family alternatives via cached Cost Explorer recommendations #79
- Existing freshness mechanism:
frontend/src/freshness.ts+last_collection_errorplumbing - Files:
internal/api/exchange_lookup.go,internal/api/handler_ri_exchange.go,frontend/src/riexchange.ts,pkg/exchange/reshape.go
Severity
Medium — silent stale-data presentation in a financially-significant flow.
Effort
Small-Medium — backend timestamp plumbing + reuse of an existing frontend component. ~100 LOC + tests.
Activity
cristim commentedon Apr 30, 2026
Verification
Original concern: After PR #79 the reshape page reads cross-family alternatives from the cached AWS Cost Explorer recommendations table without any freshness signal. Stuck schedulers / outages can leave the cache hours or days behind, and the UI silently shows stale alternatives. Acceptance criteria: API surface a
stalenessindicator (12h soft / 24h hard); reshape modal renders a banner reusing the existingfreshness.tspattern.What merged: PR #79 is the cause (the rec-driven cross-family lookup) — not a fix. No follow-up PR linked here addresses the staleness banner.
Code state today (
origin/feat/multicloud-web-frontend):internal/api/exchange_lookup.go::purchaseRecLookupFromStorereturns[]exchange.OfferingOptiononly — nocollected_at/ max-collected timestamp plumbed through. NolatestCollectionTimehelper.internal/api/handler_ri_exchange.goreturns nostaleness/freshnessfield on the response shape.frontend/src/riexchange.tsdoes not import or callfreshness.ts; grep forfreshness|stale|collected_atin the file returns only an unrelated comment about a generation counter.Recommendation: keep-open. Missing: backend timestamp plumbing (extend
purchaseRecLookupFromStoreor a sibling helper to surface maxcollected_at), thread the timestamp through the reshape API response, and wire the existingfreshness.tscomponent (or a thin wrapper) into the reshape modal/page with the proposed 12h soft / 24h hard thresholds.feat(reshape): surface staleness banner when Cost Explorer cache is o…
feat(reshape): surface staleness banner when Cost Explorer cache is o…
cristim commentedon May 28, 2026
Verified shipped: merge commit 4529d59 (PR #576) is in origin/feat/multicloud-web-frontend (compare API: feat is ahead of 4529d59). PR #576 body says "Closes #150" and surfaces a staleness banner when the Cost Explorer cache is old. PR #79 (merge commit 62b8277, also in feat) contributed the underlying cross-family rec lookups that drove the reshape caching.