Skip to content

perf: stale-while-revalidate cache + configurable freshness window #27

@steven-pribilinskiy

Description

@steven-pribilinskiy

Problem

The dashboard cache today has a fixed 5-minute TTL (cacheMaxAge in internal/tui/app.go). The trade-off is rough:

  • Within 5 min: cache is served. Fine.
  • After 5 min: relaunch blocks on a full scan — ~1–3s on a 1000-repo tree before anything renders. The loading screen flashes on every cold start.
  • No knob: users with fast workflows or huge trees can't tune the window.
  • No signal that data is stale when the cache is served — and conversely no signal that a refresh is happening when it's running.

Proposal

Switch to stale-while-revalidate, with a configurable freshness window.

Cache strategy

  1. Warm start, cache < cacheFreshness: serve cache, no refresh. Silent and instant.
  2. Warm start, cache ≥ cacheFreshness: serve cache immediately, run a background refresh, swap data in when it lands. If the fresh data matches what's on screen (path-keyed equality on the displayed fields), the swap is silent — no badge flip, no table repaint, no "blink".
  3. Cold start (no cache): loading screen until first scan.
  4. Manual r / Worktree-toggle slow path / Post-action: same background-refresh pattern; status line shows ↻ Refreshing…, badge in stats bar shows during the in-flight scan.

Config

# How long the on-disk scan cache stays "fresh" — reads younger than this
# serve without firing a background refresh. Accepts any Go duration
# string (30s, 1m, 5m, 1h). Default: 1m.
cacheFreshness: 1m

Layout stability (drive-by fixes)

A few visible defects fall out of the same effort:

  • The status row is always reserved (1 line, blank when empty). Today it's conditional + uses MarginTop(1), so the dashboard grows by 2 lines when a status message appears, the terminal scrolls, and every redraw paints at a new origin — visible as "shimmer".
  • Panel render output (grass / disk / timeline) is cached on the model and only rebuilt on data-update or resize. Currently every keystroke while a panel is open re-runs the renderer.
  • Timeline panel content is bounded by actual emitted lines rather than entry-iterations, so it never overflows the panel allocation (overflow caused the same scroll-shimmer as the status row).

Why this matters

Removes the noticeable "loading → ready" transition on every relaunch and stops the visible shimmer that's been creeping in as the dashboard accumulated chrome.

Happy to send a PR — already have a branch ready.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions