Skip to content

perf: Suspense streaming for dashboard + async cache warming#153

Merged
neonwatty merged 5 commits intomainfrom
perf/async-suspense-streaming
Apr 19, 2026
Merged

perf: Suspense streaming for dashboard + async cache warming#153
neonwatty merged 5 commits intomainfrom
perf/async-suspense-streaming

Conversation

@neonwatty
Copy link
Copy Markdown
Collaborator

Summary

  • Suspense streaming: Dashboard data fetching moved to an async Server Component (DashboardContent) wrapped in <Suspense>. The shell (tabs, filters, nav) renders immediately while GitHub data streams in, replacing the previous all-or-nothing SSR.
  • Async cache warming: Repo-save cache warming changed from blocking await to fire-and-forget so the Settings page responds instantly.
  • CDN cache-busting: Octokit fetch wrapper adds _nc timestamp param + cache: "no-store" to bypass CDN/Next.js caching of GitHub API responses.
  • Error resilience: Dashboard catch block no longer pushes zero counts (which masked errors as empty state) — tabs keep the "·" placeholder on failure.
  • E2E hardening: Data-freshness tests use retry loops with SQLite cache clearing to handle GitHub API eventual consistency.

Closes #134, closes #139.

Test plan

  • pnpm turbo typecheck passes
  • pnpm turbo build passes
  • Dashboard loads with skeleton → streamed content transition
  • Tabs show "·" while loading, then real counts
  • Settings → save repo → no blocking spinner for cache warming
  • pnpm --filter @issuectl/web test:e2e — data-freshness A1/A2/A3 pass

ESLint only allows console.warn and console.error — console.debug
is not in the allowed list.
)

Split the dashboard into a shell (tabs, nav, filters) that renders
instantly and a Suspense-wrapped async Server Component that streams
data in. The shell shows placeholder counts ("·") that update via
React context when data arrives.

Also converts add-repo cache warming to fire-and-forget so the
response returns immediately after the DB write, and adds CDN
cache-busting to Octokit fetches.
- Remove ZERO_COUNTS push in DashboardContent catch block so tabs
  keep the "·" placeholder instead of showing misleading "0"
- Remove dead outer try/catch in gatherPulls (inner per-repo catch
  already handles errors, Promise.all can never reject)
- Promote post-retry revalidation failure log from warn to error
- Escalate gatherPulls per-repo failure log from warn to error (data loss)
- Rename serverStderrChunks → serverOutputChunks (captures both streams)
- Update research doc status to reflect implemented items
Keep our improvements: no ZERO_COUNTS on error, no dead outer
try/catch in gatherPulls, error-level PR fetch logging,
serverOutputChunks rename, updated research doc status.
@neonwatty neonwatty added this pull request to the merge queue Apr 19, 2026
Merged via the queue into main with commit a85eec5 Apr 19, 2026
5 checks passed
@neonwatty neonwatty deleted the perf/async-suspense-streaming branch April 19, 2026 20:01
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment