Skip to content

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

@cristim

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-25hstaleness = "hard". Recs collected_at = now-30mstaleness 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.

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