Files
MultiRoombaRover/perf/issues/003-high-volume-log-stream.md
T
2026-06-12 12:33:00 -04:00

4.2 KiB

FIXED!

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:

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:

log:entry count: 850
log:entry bytes: 185,008

Artifact:

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:

log:entry count: 526
log:entry bytes: 111,535

In SessionContext.jsx, each log entry does:

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:

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:

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

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:

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.