Problem Statement / Feature Objective
The dashboard schedules recurring Soroban contract executions (e.g., daily meter snapshots, weekly tariff recalculations) that must fire at precise wall-clock times regardless of the browser tab's throttling state. A high-resolution timer wheel must schedule up to 1,000 concurrent jobs with millisecond precision, using SharedArrayBuffer-backed cross-worker timekeeping to resist background-tab throttling. When the browser throttles setTimeout/setInterval to 1,000 ms in background tabs, the timer wheel compensates by running a dedicated Web Worker with a high-resolution loop and posting correction deltas to the main thread.
Technical Invariants & Bounds
- Timer wheel: 1,024 slots, each slot representing 100 ms (total wheel span: 102.4 seconds). Jobs are hashed into slots by their next-fire-time modulo wheel span.
- Maximum scheduled jobs: 1,000. Each job has a fire-at timestamp, interval (if recurring), and a handler key.
- Precision requirement: execution within +/- 50 ms of the scheduled fire-at time.
- Background-tab detection: document.hidden transitions trigger a recalibration event. The worker stores the last known performance.now() via SharedArrayBuffer (Int32Array) and the main thread writes a "drift" value each frame.
- Fallback: if SharedArrayBuffer is unavailable (missing COOP/COEP headers), degrade to a polling interval of 200 ms on the main thread with a warning log.
Codebase Navigation Guide
- src/services/timerWheel.ts - Main thread facing timer wheel API: schedule(job), cancel(jobId), tick(). Interfaces with the worker for timekeeping.
- src/workers/timerWheel.worker.ts - Worker holding the actual wheel data structure and running the tick loop via Atomics.wait.
- src/utils/sharedBuffer.ts - SharedArrayBuffer creation and atomic access helpers for drift correction.
- src/hooks/useScheduledExecution.ts - React hook that wraps timerWheel for component-level scheduling, auto-cancels on unmount.
- src/store/slices/scheduleSlice.ts - Tracks scheduled job metadata for UI display (next fire time, status, missed-fire count).
- src/pages/SchedulerDashboard.tsx - Admin page showing all scheduled jobs, their status, and last execution time.
Implementation Blueprint
- Create SharedArrayBuffer in src/utils/sharedBuffer.ts: 4 KiB buffer with Int32Array views for "worker heartbeat", "main drift correction", and "command channel" (schedule/cancel/terminate commands).
- Implement the timer wheel in src/workers/timerWheel.worker.ts: allocate 1024 buckets (each an array of job references). The worker loop uses Atomics.wait(buffer, HEARTBEAT_INDEX, 0, 100) to sleep 100 ms per tick. On each tick, iterate the current slot, fire all due jobs, postMessage the results, advance the cursor.
- In src/services/timerWheel.ts, provide schedule(cb, fireAt, intervalMs) that writes a schedule command into the shared buffer and wakes the worker via Atomics.store + Atomics.notify.
- Implement background-drift compensation: the main thread runs a requestAnimationFrame loop; on each frame, if document.hidden === true, compute drift = expectedTime - performance.now() and write it into the shared buffer. The worker reads drift each tick and adjusts its cursor advance accordingly.
- The useScheduledExecution hook wraps timerWheel and ties cleanup to useEffect return. It also exposes a status object { nextFire, isMissed, drift }.
- scheduleSlice stores all active jobs and their metadata; the SchedulerDashboard renders a table with columns for job name, next fire time, interval, status, and a cancel button.
Problem Statement / Feature Objective
The dashboard schedules recurring Soroban contract executions (e.g., daily meter snapshots, weekly tariff recalculations) that must fire at precise wall-clock times regardless of the browser tab's throttling state. A high-resolution timer wheel must schedule up to 1,000 concurrent jobs with millisecond precision, using SharedArrayBuffer-backed cross-worker timekeeping to resist background-tab throttling. When the browser throttles setTimeout/setInterval to 1,000 ms in background tabs, the timer wheel compensates by running a dedicated Web Worker with a high-resolution loop and posting correction deltas to the main thread.
Technical Invariants & Bounds
Codebase Navigation Guide
Implementation Blueprint