Skip to content

ux(reshape): surface freshness/staleness banner when cached Cost Explorer recs are old #150

Closed

Description

@cristim

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::purchaseRecLookupFromStore reads from recommendations table without checking collected_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-provider last_collection_error plumbing). 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

  1. Extend purchaseRecLookupFromStore to also return the maximum collected_at across the scoped recs, OR expose a sibling helper latestCollectionTime(ctx, region, accountID).
  2. Plumb the timestamp through to the reshape API response (extra optional field on the JSON shape).
  3. Frontend reuses freshness.ts (or a thin wrapper) to render the banner inside the reshape modal/page.
  4. 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 includes freshness.staleness = "soft". Recs collected_at = now-25h → staleness = "hard". Recs collected_at = now-30m → staleness empty.
  • 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

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

  1. cristim commented on Apr 30, 2026

    @cristim
    MemberAuthor

    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 staleness indicator (12h soft / 24h hard); reshape modal renders a banner reusing the existing freshness.ts pattern.

    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::purchaseRecLookupFromStore returns []exchange.OfferingOption only — no collected_at / max-collected timestamp plumbed through. No latestCollectionTime helper.
    • internal/api/handler_ri_exchange.go returns no staleness / freshness field on the response shape.
    • frontend/src/riexchange.ts does not import or call freshness.ts; grep for freshness|stale|collected_at in the file returns only an unrelated comment about a generation counter.

    Recommendation: keep-open. Missing: backend timestamp plumbing (extend purchaseRecLookupFromStore or a sibling helper to surface max collected_at), thread the timestamp through the reshape API response, and wire the existing freshness.ts component (or a thin wrapper) into the reshape modal/page with the proposed 12h soft / 24h hard thresholds.

  2. added 2 commits that reference this issue on May 27, 2026
    106d4ef
    4529d59
  3. cristim commented on May 28, 2026

    @cristim
    MemberAuthor

    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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions