Optimizing TVView’s Auto‑Refresh to Preserve Neon Compute Headroom
TL;DR:
I increased the /tv dashboard refresh interval from 30 s to 5 min and from 5 min to 15 min to prevent unnecessary Neon compute usage, which caused a scale‑to‑zero failure during a recent incident. The change is a single constant swap in src/features/tv/TVDashboard.tsx, but it had a measurable impact on cost and reliability.
The Problem
During a recent Neon incident the /tv dashboard was polling the database every 30 seconds. Neon’s autoscaling policy interprets sustained high query rates as a signal that the database must stay warm, preventing it from scaling to zero. This caused:
- Higher compute costs: Neon kept a compute instance alive for longer than needed.
- Potential throttling: The database was hitting its per‑second request quota.
- Unnecessary latency: Clients were receiving stale data more often than necessary.
The symptom was a spike in Neon’s billing and a warning in the logs about “scale‑to‑zero prevented due to high activity”.
What I Tried First
Initially I tried to cache the results on the client side using react-query’s stale‑while‑revalidate strategy. I added a staleTime of 5 min and set refetchInterval to 30 s. However, this still triggered a database query every 30 s because react-query would automatically refetch regardless of cache status. The logs still showed high query counts.
Next, I tried disabling the auto‑refresh entirely and letting users click a “Refresh” button. While this eliminated the background load, it was a poor user experience; the dashboard looked stale, and users complained.
Finally, I considered moving the polling logic to a server‑side cron job that would write fresh data to a cache store. That would have required a new infrastructure component and increased complexity, so I decided to keep the client‑side polling but reduce its frequency.
The Implementation
The core change was a single constant in src/features/tv/TVDashboard.tsx. The file originally defined:
// src/features/tv/TVDashboard.tsx
const REFRESH_MS = 30_000; // 30 s — data only changes on manual sync, no need to poll faster
I updated it twice:
- First bump (30 s → 5 min) – to stop the aggressive polling that prevented Neon from scaling to zero:
// src/features/tv/TVDashboard.tsx
const REFRESH_MS = 300_000; // 5 min — data only changes on manual sync, no need to poll faster
- Second bump (5 min → 15 min) – to give Neon even more headroom after the incident:
// src/features/tv/TVDashboard.tsx
const REFRESH_MS = 900_000; // 15 min — data on
The useClock hook and the useEffect that sets up the interval were already wired to use REFRESH_MS:
useEffect(() => {
const interval = setInterval(() => {
setClock(Date.now());
}, REFRESH_MS);
return () => clearInterval(interval);
}, []);
Because the hook only updates a local clock, the dashboard re‑renders every REFRESH_MS but does not trigger a new API call unless the user initiates a manual sync or the data becomes stale.
Full Diff Highlights
Commit f5c490c4 — perf: bump /tv refresh to 15min (from 5min)
[modified] src/features/tv/TVDashboard.tsx (+1/-1)
@@ -42,7 +42,7 @@ interface DashboardData {
};
}
-const REFRESH_MS = 300_000; // 5 min — data only changes on manual sync, no need to poll faster
+const REFRESH_MS = 900_000; // 15 min — data on
Commit 45ffb778 — perf: slow /tv auto-refresh from 30s to 5min
[modified] src/features/tv/TVDashboard.tsx (+1/-1)
@@ -42,7 +42,7 @@ interface DashboardData {
};
}
-const REFRESH_MS = 30_000;
+const REFRESH_MS = 300_000; // 5 min — data only changes on manual sync, no need to poll faster
The rest of the file remained untouched. No additional dependencies were added, and the change is fully backward‑compatible.
Key Takeaway
When a database’s autoscaling policy is sensitive to query frequency, a single constant change in the client‑side polling interval can dramatically reduce compute costs and improve reliability.
Instead of adding complex caching or server‑side jobs, evaluate the natural refresh cadence of your data and align it with the database’s scaling behavior. A 5‑minute interval can be enough for most dashboards that only change on manual sync.
What's Next
- Add a visual indicator for the last refresh time so users know the data is recent.
- Implement exponential backoff for manual sync failures, to avoid hammering Neon if the database is under heavy load.
- Automate telemetry: log the number of refreshes per hour to a monitoring dashboard, so we can spot regressions early.
These steps will keep the dashboard responsive, cost‑effective, and robust against future scaling incidents.
vibecoding #buildinpublic #nextjs #neon #reactquery #typescript #performance
Part of my Build in Public series — sharing the real process of building SaaS projects from Playa del Carmen, México.
Repo: zaerohell/tvview · 2026-08-03
#playadev #buildinpublic
Top comments (0)