Skip to content

Feat 83 recording (socket profiler recording & replay)#200

Draft
theultimatewasteofspace wants to merge 41 commits into
devfrom
feat-83-recording
Draft

Feat 83 recording (socket profiler recording & replay)#200
theultimatewasteofspace wants to merge 41 commits into
devfrom
feat-83-recording

Conversation

@theultimatewasteofspace
Copy link
Copy Markdown
Collaborator

@theultimatewasteofspace theultimatewasteofspace commented May 6, 2026

Summary

Implements Issue #83 — Socket Profiler recording & replay tool for stress-testing CARE.

What's included

Recording side

  • Per-session recording: admin picks specific socket connections to capture, not whole users
  • New connections during a recording are silently uncaptured; a warning toast goes to the recording owner
  • recording and trace tables with full lifecycle (start → recording → finished/interrupted → soft-deleted)
  • Recovery: server marks any in-flight recordings as interrupted on next boot

Replay side

  • Pooled scaling mode: sessions from all selected recordings are combined; iteration K runs K × N parallel sockets with wraparound (linear add)
  • Configurable per-run via Start Replay Modal: mode, speed (fast/realtime), max iterations, continue-past-failures toggle, multi-recording selection
  • Live load preview shows pool size, peak parallel sockets, and cumulative socket-runs before starting
  • Authenticated socket.io clients via session-cookie minting (replay/auth.js)
  • Pass/fail per trace based on server ack with success: true within 2s (will adjust later)

UI

  • Persistent recording icon in the topbar (red pulsing dot) replacing the previous global RecordingBar
  • Visible app-wide for admins; visible to non-admin users only when their socket is in the recording's participant list
  • Click is informational; admins navigate manually to Socket Profiler to stop(broken)
  • Results modal shows iteration-level breakdown with per-session drill-down to traces and DB changes

Architectural decisions

  • Pure session selection (not user-based): recording filters on socketId, not userId. Trade-off: reconnects/new tabs aren't auto-captured, but the warning toast keeps the user informed.
  • Pooled session scaling: selected recordings' sessions are merged into one pool that scales together, rather than each recording running an independent scaling test in parallel. Confirmed with Dennis.
  • Cross-session timing not preserved: scaling mode runs sessions parallel-from-zero. Original timing offsets between sessions are lost. Behavioral replay (timing-preserved) deserves a separate mode and isn't part of this PR.
  • publicTable: true on Recording model: needed so non-admin participants get the recording row in their store and the topbar icon can render.

Known issues or observations

  • Scaling stops at first failed iteration because of certain traces unless "continue past failures" is toggled on
  • Replay results disappear when modal closes — no persistence yet
  • Pass/fail is binary; failed/timed-out/aborted all bucket together
  • Modal still groups some things by user where session would be more accurate (results breakdown is per-session; save modal isn't yet)

Roadmap (out of scope for this PR)

  • Behavioral replay mode with timing preservation
  • Abort button + persistent ReplayBar (currently nothing can interrupt a running replay)
  • Speed control beyond fast/realtime
  • JSON export/import of replay results

Migration & merge notes

  • This branch was rebased/merged with dev (53 commits behind at last sync). The merge was clean at the file level except for .gitignore and package-lock.json, both auto-resolved.
  • Dev's restructure-nav_group migration relocated nav elements but didn't touch Socket Profiler since it lived on this branch. A small follow-up migration (20260506220447-move-socket-profiler-to-manage) moves Socket Profiler into the Manage group.

Testing

End-to-end manual testing covered:

  • Single-session recording → replay scaling
  • Multi-session recording → replay scaling
  • Multi-recording concurrent (pooled) replay
  • Continue-past-failures toggle behavior
  • Warning toast on uncaptured connections
  • Persistent topbar icon visibility across all auth states
  • Recovery of interrupted recordings on backend restart

Closes #83

Ilyas Mohammed and others added 30 commits March 22, 2026 01:37
- Add userId column to trace table (nullable FK to user)
- Refactor recorder to use server-wide activeRecordingId flag
- Attach/detach trace listeners on every connected user's socket
- New mid-recording connections auto-attach listeners on init
- Each trace tagged with the userId that emitted it
- Add recordingGetTraces endpoint for edit modal
…cator

- Add userId to trace table for per-user event tagging
- Refactor recorder to server-wide capture across all connected sockets
- Add participantUserIds filter on recording (null = everyone)
- Start Recording modal with user picker and online status
- Edit Recording modal with trace pruning
- Discard Recording button in save modal
- New endpoints: recordingGetTraces, recordingGetOnlineUsers
# Please enter a commit message to explain why this merge is necessary,
# especially if it merges an updated upstream into a topic branch.
#
# Lines starting with '#' will be ignored, and an empty message aborts
# the commit.
- Server.recoverInterruptedRecordings() runs on startup to mark stale
  'recording' rows as 'interrupted' after a server restart, freeing
  the Start Recording button
- RecordingModal confirm() now waits for rename + trace deletions
  before closing modal and reporting success
Each socket connection now writes a distinct socketId on every trace,
so admin's two tabs are recorded as two separate streams instead of
being collapsed into one merged userId stream. Foundation for
per-session replay.

- Migration: collapsed three trace migrations into one create-trace
  with userId + socketId from the start
- Model: added socketId field
- Recorder: writes socket.id on every trace
- Pre-select admin inside the getOnlineUsers callback so userTable is
  stable when we resolve the row, fixing 'Record 2 User(s)' when only
  admin was checked
- getOnlineUsers now returns per-user session count, exposed as
  Sessions column in the start-recording table
groupTracesBySocket replaces groupTracesByUser. Each session
(socket connection) becomes one replay client, so admin's two
recorded tabs replay as two parallel clients instead of one
merged stream. Falls back to per-user grouping for legacy
recordings without socketId.
Ilyas Mohammed added 5 commits May 3, 2026 16:24
Recording participants are now socketIds (one per active socket
connection), not userIds. Admin's two tabs are two selectable
sessions. New connections during a recording are silently uncaptured
- a warning toast goes to the recording owner so they know.

- Migration: add participantSocketIds JSONB to recording table
  (participantUserIds kept for now in case selection model changes,
  but no longer written to)
- Recorder: isUserIncluded -> isSessionIncluded, getOnlineUsers ->
  getOnlineSessions returning per-socket entries
- Server: warning toast to recording owner on uncaptured connection
- Start modal: per-session table, pre-selects current tab's session,
  shows '(this tab)' marker on owner's row
- TopBar.vue: red pulsing icon visible app-wide during recordings
  (admins always see; non-admin participants see when their socketId
  is in participantSocketIds; click is informational only)
- Dashboard.vue: remove RecordingBar mount/import
- SocketProfiler.vue: stop button calls recorderStop directly instead
  of going through eventBus -> RecordingBar
- StartRecordingModal.vue: drop name input (renamed at save time
  in RecordingModal instead)
- Recording model: publicTable=true so recording state reaches
  participants reactively (admin and non-admin)
- recorder.js: drop debugging console.logs

RecordingBar.vue file kept untouched in the repo for git history.
Click Play on a recording opens a configuration modal instead of
firing the replay immediately. The modal lets the user configure
the run before starting:

- Mode selector (Scaling now; Load mode placeholder for later)
- Speed: Fast or Realtime
- Max iterations (required positive integer)
- Continue past failures toggle
- Multi-recording selection (sessions get pooled)
- Live load preview: pool size, peak parallel sockets, cumulative
  socket-runs

Scaling math change: sessions from all selected recordings are
pooled into one list of N sessions. Iteration K runs K * N parallel
sockets, cycling the pool with wraparound. Linear add per iteration
(K=1 -> N sockets, K=2 -> 2N, ..., K=M -> M*N).

- replayer.js: rewritten around buildSessionPool + runScalingTest
  on the pooled list. New replayRun signature accepts recordingIds
  array, maxIterations, continueOnFailure
- ReplayResultsModal.vue: flat iteration list (was per-recording).
  Each session row labels its source recording so multi-recording
  runs are still readable
- SocketProfiler.vue: Play button opens StartReplayModal instead
  of running directly
- worker.js: emitWithTimeout now resolves success: false on timeout
  (was silently passing as success: true)
- StartRecordingModal.vue: drop stray 's' character (lint fix)

Cross-session timing offsets are intentionally not preserved during
scaling — that's a separate behavioral-replay mode for future work.
The original extend-nav-socket-profiler migration inserted Socket
Profiler into the legacy Admin group (id 2). Dev's later
restructure-nav_group migration replaced the Admin layout with
category-based groups but didn't relocate Socket Profiler since it
lived on a separate feature branch at the time.

After merging dev into feat-83-recording, Socket Profiler ends up
stranded in the legacy Admin group and disappears from the sidebar.
This migration relocates it into Manage, alongside other admin
operational tools like Users.
Ilyas Mohammed added 6 commits May 11, 2026 13:44
…igation

- Topbar.vue: show ticking HH:MM:SS timer next to the recording dot,
  updates every second via setInterval
- Restore click-to-navigate: admins jump to /dashboard/Socket_Profiler
  when clicking the icon (route path was wrong in the previous attempt;
  used the actual path from the router this time)
- Tooltip text simplified: elapsed time is now visible inline, no need
  to duplicate it in the tooltip
macOS auto-generates .DS_Store in every folder, which polluted
git status output. Adding the pattern to .gitignore and removing
the two tracked instances so they stop showing up as modified.
The mounted() method's closing brace was missing, causing a Vite
compile error. Added the missing }, between setInterval and
beforeUnmount.
The trace table in RecordingModal now shows when each trace fired,
both as absolute wall-clock time (HH:MM:SS) and as offset from the
recording's start time (+HH:MM:SS).

- Reads the recording's startTime reactively from the Vuex store
  so the elapsed calculation works in both the save-after-stop
  flow and the edit flow
- Falls back to '-' gracefully when timestamps are missing
- SocketProfiler.vue: move :max-table-height before @action
- StartRecordingModal.vue: reorder input attributes to match
  Vue style guide (directives -> bindings -> plain attrs)
The log was added during development to verify DB-change tracking
was working. The same info is now captured properly in each trace's
dbChanges result field, so the server-console echo is redundant.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[FEATURE] Recording & Replay Tool for CARE Platform

1 participant