mirror of
https://github.com/legop3/MultiRoombaRover.git
synced 2026-09-17 01:50:47 -04:00
performance slopping but actually very informative
This commit is contained in:
@@ -0,0 +1,164 @@
|
||||
# Issue 003: High-Volume Socket Log Stream
|
||||
|
||||
## Summary
|
||||
|
||||
The `/` page receives a very high volume of `log:entry` socket messages. These update the
|
||||
global session store even when logs are not the primary visible task.
|
||||
|
||||
This is not the single biggest CPU stack item, but it is a major source of live update
|
||||
pressure on low-end devices.
|
||||
|
||||
## Severity
|
||||
|
||||
Medium-high.
|
||||
|
||||
Fix after listener/context work, unless logs are known to be unusually noisy in production.
|
||||
|
||||
## Affected Files
|
||||
|
||||
- `webui/src/context/SessionContext.jsx`
|
||||
- `webui/src/components/LogPanel/index.jsx`
|
||||
- Server log emission paths, depending on where `log:entry` is emitted
|
||||
|
||||
## Evidence
|
||||
|
||||
Artifact:
|
||||
|
||||
```sh
|
||||
perf/results/2026-06-11T03-22-44-714Z-root-runtime/root-runtime-report.json
|
||||
```
|
||||
|
||||
In an 18s `/` audit at mobile viewport with 6x CPU throttle:
|
||||
|
||||
```txt
|
||||
log:entry count: 850
|
||||
log:entry bytes: 185,008
|
||||
```
|
||||
|
||||
Artifact:
|
||||
|
||||
```sh
|
||||
perf/results/2026-06-11T03-21-51-263Z-root-runtime/root-runtime-report.json
|
||||
```
|
||||
|
||||
In a 30s audit, among the most recent sampled 1000 WebSocket messages:
|
||||
|
||||
```txt
|
||||
log:entry count: 526
|
||||
log:entry bytes: 111,535
|
||||
```
|
||||
|
||||
In `SessionContext.jsx`, each log entry does:
|
||||
|
||||
```jsx
|
||||
function handleLogEntry(entry) {
|
||||
setState((prev) => ({ ...prev, logs: [...prev.logs.slice(-199), entry] }));
|
||||
}
|
||||
```
|
||||
|
||||
This creates a new session state object and a new logs array for every log message.
|
||||
|
||||
## Why This Matters
|
||||
|
||||
Even if `LogPanel` is not visible or not currently selected, `SessionContext` still processes
|
||||
the update. Every session subscriber selector is evaluated on each `setState`.
|
||||
|
||||
At 850 log messages in 18s, this is roughly 47 log entries per second.
|
||||
|
||||
On a powerful desktop this is fine. On a weaker phone or low-end laptop, it becomes steady
|
||||
background pressure.
|
||||
|
||||
## Likely Cause
|
||||
|
||||
The server appears to broadcast operational logs very frequently. The client stores the last
|
||||
200 logs globally.
|
||||
|
||||
The `/` page includes `LogPanel` under settings and may also have other components depending
|
||||
on session state.
|
||||
|
||||
## Fix Strategies
|
||||
|
||||
### Option A: Do Not Subscribe to Logs Unless Log UI Is Visible
|
||||
|
||||
Best conceptual fix:
|
||||
|
||||
- Add an explicit client event like `log:subscribe` / `log:unsubscribe`.
|
||||
- Only subscribe when `LogPanel` is open or user is admin.
|
||||
- For ordinary mobile control users, skip logs entirely.
|
||||
|
||||
### Option B: Batch Log Entries Client-Side
|
||||
|
||||
Keep receiving logs, but batch state updates:
|
||||
|
||||
```jsx
|
||||
const pendingLogsRef = useRef([]);
|
||||
const flushTimerRef = useRef(null);
|
||||
|
||||
function handleLogEntry(entry) {
|
||||
pendingLogsRef.current.push(entry);
|
||||
if (flushTimerRef.current) return;
|
||||
flushTimerRef.current = setTimeout(() => {
|
||||
const batch = pendingLogsRef.current.splice(0);
|
||||
flushTimerRef.current = null;
|
||||
setState((prev) => ({
|
||||
...prev,
|
||||
logs: [...prev.logs, ...batch].slice(-200),
|
||||
}));
|
||||
}, 250);
|
||||
}
|
||||
```
|
||||
|
||||
This changes 47 state updates/sec into about 4 updates/sec.
|
||||
|
||||
### Option C: Store Logs in a Separate External Store
|
||||
|
||||
Move logs out of main `SessionContext` so unrelated session consumers are not touched by
|
||||
log spam.
|
||||
|
||||
For example:
|
||||
|
||||
```jsx
|
||||
LogStoreProvider
|
||||
useLogEntries()
|
||||
```
|
||||
|
||||
This isolates log updates from user/roster/session state.
|
||||
|
||||
### Option D: Server-Side Sampling or Severity Filtering
|
||||
|
||||
Only send:
|
||||
|
||||
- warnings/errors by default
|
||||
- full logs to admin/log panel subscribers
|
||||
- sampled info/debug entries
|
||||
|
||||
## Recommended Path
|
||||
|
||||
Start with client batching. It is low-risk and easy to validate.
|
||||
|
||||
Then consider subscription gating if logs are not essential for normal users.
|
||||
|
||||
## Validation
|
||||
|
||||
Run:
|
||||
|
||||
```sh
|
||||
CPU_THROTTLE=6 VIEWPORT=390x844 MOBILE=1 SAMPLE_MS=30000 node perf/live-root-runtime-audit.mjs https://rover.otter.land/ perf/results
|
||||
```
|
||||
|
||||
The socket `log:entry` count may remain high if only batching client-side, but UI state
|
||||
updates should reduce. To measure batching directly, add temporary counters in
|
||||
`SessionContext` or extend the audit to count React commits after local changes.
|
||||
|
||||
Expected user-visible perf improvements:
|
||||
|
||||
- lower `ScriptDuration`
|
||||
- lower `TaskDuration`
|
||||
- fewer repeated DOM commits if log updates were causing broad subscribers to render
|
||||
|
||||
## Risks
|
||||
|
||||
- Logs may be used for operator visibility during incidents.
|
||||
- Batching can delay log display by 100-500ms. That should be acceptable for UI logs.
|
||||
- Subscription gating requires server/client protocol changes.
|
||||
|
||||
Reference in New Issue
Block a user