efiten and Claude Sonnet 4.6
3850600130
perf(server): TTL-cache /api/stats observations aggregate — eliminate per-request full-table scan ( #1460 ) ( #1516 )
...
## Problem
`GetStoreStats` ran a `SUM(CASE WHEN timestamp > ?)` over the full
`observations` table on **every** `/api/stats` call. The staging pprof
analysis (#1460 ) identified this as rank #9 CPU consumer:
`GetStoreStats.func2` at 920ms cumulative = ~10% of all server CPU.
The query:
```sql
SELECT
COALESCE(SUM(CASE WHEN timestamp > ? THEN 1 ELSE 0 END), 0),
COALESCE(SUM(CASE WHEN timestamp > ? THEN 1 ELSE 0 END), 0)
FROM observations WHERE timestamp > ?
```
scans ~1.9M rows each time `/api/stats` is polled (every 15s from the
dashboard).
## Fix
Add a **30-second TTL cache** on `PacketStore` for `PacketsLastHour` and
`PacketsLast24h`:
- Cache hit → skip the observations goroutine entirely, use stored
values
- Cache miss → run the query, update cache with result
- The node/observer `COUNT(*)` query is unchanged and always runs fresh
The hour/24h counts are display-only values; 30s accuracy is sufficient.
## Changes
`cmd/server/store.go`:
- 4 new fields on `PacketStore`: `statsCacheMu sync.Mutex`,
`statsCacheTime time.Time`, `statsLastHour int`, `statsLast24h int`
- `GetStoreStats`: check cache before launching goroutines; conditional
`wg.Add`; update cache after successful query
Builds clean. No tests changed.
Closes #1460 (P1#1 from staging CPU profile).
🤖 Generated with [Claude Code](https://claude.com/claude-code )
---------
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com >
2026-05-31 14:54:21 -07:00
..
2026-04-21 09:09:39 -07:00
2026-04-03 13:51:13 -07:00
2026-05-21 14:00:15 -07:00
2026-05-21 14:00:15 -07:00
2026-04-05 14:50:40 -07:00
2026-05-21 14:00:15 -07:00
2026-05-03 17:56:42 -07:00
2026-05-03 17:56:42 -07:00
2026-05-17 16:13:11 +00:00
2026-05-18 22:51:23 -07:00
2026-05-18 22:51:23 -07:00
2026-05-18 22:51:23 -07:00
2026-05-18 22:51:23 -07:00
2026-04-12 18:09:23 -07:00
2026-05-21 14:00:15 -07:00
2026-04-20 21:46:34 -07:00
2026-05-25 17:45:32 -07:00
2026-05-25 22:16:14 -07:00
2026-05-19 01:17:12 -07:00
2026-05-18 12:27:44 -07:00
2026-05-19 01:17:12 -07:00
2026-05-21 14:00:15 -07:00
2026-05-21 14:00:15 -07:00
2026-05-21 11:39:49 -07:00
2026-05-21 11:39:49 -07:00
2026-05-21 11:39:49 -07:00
2026-05-19 23:53:41 -07:00
2026-04-17 09:24:40 -07:00
2026-05-30 13:22:41 -07:00
2026-05-30 13:22:41 -07:00
2026-05-30 13:22:41 -07:00
2026-05-30 13:22:41 -07:00
2026-05-21 14:00:15 -07:00
2026-05-16 17:28:40 +00:00
2026-05-29 01:08:12 -07:00
2026-05-29 02:42:21 -07:00
2026-05-15 22:34:21 -07:00
2026-05-15 22:34:21 -07:00
2026-05-19 08:08:28 -07:00
2026-05-05 01:16:57 -07:00
2026-05-05 01:16:57 -07:00
2026-05-21 14:00:15 -07:00
2026-04-16 00:09:36 -07:00
2026-05-16 20:56:52 +00:00
2026-05-05 01:58:52 -07:00
2026-05-19 23:53:41 -07:00
2026-03-31 01:10:56 -07:00
2026-05-31 14:54:21 -07:00
2026-05-29 02:42:21 -07:00
2026-05-29 02:42:21 -07:00
2026-04-18 11:52:22 -07:00
2026-04-18 11:52:22 -07:00
2026-05-19 23:53:41 -07:00
2026-05-06 23:50:44 -07:00
2026-04-03 13:11:59 -07:00
2026-05-15 16:21:14 +00:00
2026-05-16 19:55:00 +00:00
2026-05-15 22:46:28 -07:00
2026-05-15 20:24:55 -07:00
2026-05-15 22:46:25 -07:00
2026-05-17 16:13:11 +00:00
2026-04-20 22:15:02 -07:00
2026-05-21 14:00:15 -07:00
2026-04-21 04:51:24 +00:00
2026-05-02 20:35:15 -07:00
2026-05-17 16:13:11 +00:00
2026-05-19 08:08:28 -07:00
2026-05-18 23:19:27 -07:00
2026-05-29 02:42:21 -07:00
2026-05-28 15:06:30 -07:00
2026-05-28 15:06:30 -07:00
2026-04-20 23:10:33 -07:00
2026-05-25 22:35:35 -07:00
2026-05-03 08:56:09 -07:00
2026-05-21 14:00:15 -07:00
2026-05-17 16:13:11 +00:00
2026-05-29 02:42:21 -07:00
2026-04-02 23:45:03 -07:00
2026-05-15 09:16:39 -07:00
2026-05-15 09:16:39 -07:00
2026-05-29 02:42:21 -07:00
2026-05-29 02:42:21 -07:00
2026-05-29 02:42:21 -07:00
2026-05-29 02:42:21 -07:00
2026-05-16 10:14:44 -07:00
2026-04-30 09:25:51 -07:00
2026-05-16 19:55:00 +00:00
2026-05-19 23:53:41 -07:00
2026-05-19 23:53:41 -07:00
2026-05-19 23:53:41 -07:00
2026-05-05 01:41:00 -07:00
2026-05-05 01:41:00 -07:00
2026-04-17 23:43:05 +00:00
2026-05-18 09:22:27 -07:00
2026-05-18 07:36:33 -07:00
2026-04-03 13:33:26 -07:00
2026-05-01 23:11:27 -07:00
2026-05-29 01:08:12 -07:00
2026-05-29 01:08:12 -07:00
2026-05-29 02:42:21 -07:00
2026-05-29 02:42:21 -07:00
2026-04-05 15:05:20 -07:00
2026-05-18 23:52:27 -07:00
2026-05-17 16:13:11 +00:00
2026-05-25 22:32:00 -07:00
2026-03-30 03:42:11 +00:00
2026-05-15 22:46:28 -07:00
2026-05-15 22:46:28 -07:00
2026-05-15 22:46:28 -07:00
2026-05-15 22:46:28 -07:00
2026-05-15 22:46:28 -07:00
2026-05-15 22:46:28 -07:00
2026-05-15 22:46:28 -07:00
2026-05-18 23:19:30 -07:00
2026-05-28 15:02:59 -07:00
2026-05-28 14:55:59 -07:00
2026-05-25 06:03:10 +00:00
2026-05-02 11:15:25 -07:00
2026-05-08 16:29:23 -07:00
2026-05-08 16:29:23 -07:00
2026-05-08 16:29:23 -07:00
2026-05-08 16:29:23 -07:00
2026-05-05 17:56:56 -07:00
2026-05-08 16:29:23 -07:00
2026-04-01 19:26:11 -07:00
2026-05-15 09:16:39 -07:00
2026-05-25 22:35:35 -07:00
2026-05-03 19:50:01 -07:00
2026-05-21 11:39:43 -07:00
2026-05-20 20:57:02 -07:00
2026-05-28 15:01:58 -07:00
2026-05-28 15:01:58 -07:00
2026-05-28 15:01:58 -07:00
2026-05-05 02:33:27 -07:00
2026-05-20 20:57:02 -07:00
2026-05-20 20:57:02 -07:00
2026-05-20 20:57:02 -07:00
2026-05-15 16:21:14 +00:00
2026-05-15 09:16:39 -07:00
2026-05-17 16:42:01 -07:00
2026-04-20 19:55:00 -07:00
2026-04-21 04:51:24 +00:00
2026-05-18 07:36:28 -07:00
2026-05-03 17:56:12 -07:00
2026-05-21 14:00:15 -07:00
2026-05-21 14:00:15 -07:00
2026-05-29 14:31:36 +00:00
2026-05-15 16:21:14 +00:00
2026-05-03 17:40:54 -07:00
2026-04-20 23:10:33 -07:00
2026-04-21 09:09:39 -07:00
2026-05-31 14:54:21 -07:00
2026-05-29 02:42:21 -07:00
2026-05-03 17:41:22 -07:00
2026-05-03 17:41:22 -07:00
2026-05-21 14:00:15 -07:00
2026-04-20 19:55:00 -07:00
2026-04-20 19:55:00 -07:00
2026-05-28 05:22:08 -07:00
2026-05-29 14:31:36 +00:00
2026-03-30 03:42:11 +00:00
2026-05-21 11:39:49 -07:00