Skip to content

Pr/reintroduce websockets#528

Open
Jackzzey wants to merge 11 commits into
dres-dev:devfrom
Jackzzey:pr/reintroduce-websockets
Open

Pr/reintroduce websockets#528
Jackzzey wants to merge 11 commits into
dres-dev:devfrom
Jackzzey:pr/reintroduce-websockets

Conversation

@Jackzzey

Copy link
Copy Markdown

Summary

Reintroduces WebSocket-based push updates for live evaluation views, replacing (or backing up) fixed-interval polling with near-instant updates pushed from the server.

  • Backend: RunExecutor now handles a /api/ws/run WebSocket endpoint — clients register/unregister per evaluation, send periodic pings, and receive
    ServerMessages (COMPETITION_START/UPDATE/END, TASK_PREPARE/START/UPDATED/END, PING) derived from internal StreamEvents via EventStreamProcessor.
  • Frontend: a new singleton WebSocketService connects per-evaluation, auto-reconnects on drop (5s), sends keep-alive pings (30s), and exposes a messages$
    observable. Six components now consume it.

Polling is not removed — each updated component merges a wsRefresh$ (filtered to the relevant ServerMessageTypes) into its existing 30s fallback timer/refresh
subject, so the UI stays responsive to pushes while remaining correct if the socket drops.

Backend changes

  • RunExecutor.kt: accept(ws: WsConfig) handles REGISTER / UNREGISTER / PING / ACK; eventToMessage() maps StreamEventServerMessage;
    handleStreamEvent() broadcasts to all sessions registered for the affected evaluation.
  • RestApi.kt: registers ws("ws/run", RunExecutor::accept).
  • DRES.kt: registers RunExecutor with EventStreamProcessor.
  • New RunExecutorWebSocketTest.kt — registration/unregistration, broadcast scoping, event→message mapping, ping/ack handling (14 tests).

Frontend changes

  • New WebSocketService (providedIn: 'root'): connect(evaluationId), disconnect(), messages$: Observable<IWsServerMessage>, auto-reconnect, ping keep-alive.
    Covered by websocket.service.spec.ts.

  • Wired into:

    • RunViewerComponent
    • RunAdminViewComponent
    • RunAsyncAdminViewComponent
    • JudgementViewerComponent
    • JudgementVotingViewerComponent
    • SubmissionsListComponent

    Each component now connects on init (using the evaluation/run id), disconnects on destroy, and merges wsRefresh$ (filtered to the message types relevant to that view
    — task lifecycle and/or competition updates) into its existing poll/refresh pipeline.

Testing

  • Frontend (Karma/Jasmine, ChromeHeadless): full suite 97/97 passing, including new WebSocket-wiring specs for all six updated components plus
    websocket.service.spec.ts. Each component spec verifies: connect-on-init, disconnect-on-destroy, refresh triggered on relevant ServerMessageTypes, and no spurious
    refresh on irrelevant types (e.g. PING).
  • Backend (Gradle/JUnit): full suite re-run from clean (./gradlew :backend:test --rerun-tasks) — 15 test classes, 173 tests, 0 failures, 0 errors, BUILD SUCCESSFUL in 7m17s. Includes RunExecutorWebSocketTest (14/14).

Test plan

  • Backend unit/integration tests (full suite)
  • Frontend unit tests (full suite)
  • Manual smoke test:

@lucaro

lucaro commented Jun 18, 2026

Copy link
Copy Markdown
Collaborator

That is a good start, but it does not as far as it could reasonably go. It would be much more interesting to send the state diffs via the websocket and have the frontend apply the diffs to update the state. This would increase synchronicity and reduce the need for traffic via polling.

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.

2 participants