CodeZen is a high-performance, full-stack platform for competitive programming and technical hiring. It combines a VSCode-like code editor, a scalable real-time contest engine, a peer-to-peer WebRTC interview suite, and an AI-powered job recommendation system — all engineered with production-grade reliability in mind.
- ✨ Feature Highlights
- 🏗️ Architecture
- 🛠️ Tech Stack
- 📁 Project Structure
- 🔌 API Reference
- ⚙️ Configuration
- 🚀 Getting Started
- 🔐 Authentication & RBAC
- 🧠 Core Design Decisions
A complete contest system designed to handle real-time events at scale.
- Contest Lifecycle: Staff create contests with custom problems, time limits, memory limits, and point values. Contests automatically transition between
Upcoming → Live → Endedstates. - Problem Sets: Each contest problem is a versioned snapshot with its own description, I/O format, constraints, and scoring. Problems map to a global problem bank.
- Participant Registration: Users must register before a contest starts. Registrations are closed once the contest begins.
- Contest Submission Flow: A participant submits code from within the contest editor. The submission is dispatched to Judge0, judged asynchronously via webhook, and the result is automatically linked back to the contest leaderboard.
- Base Submission Sync: Practice submissions made on the same problems during the contest window are auto-synced into contest results — so if a user solves a problem in the IDE, the verdict carries over.
The leaderboard is powered entirely by Redis Sorted Sets — no polling, no cron jobs, no database scans.
- Scores are encoded as a composite integer:
solved_count × 1,000,000 - penalty_minutes. - This single score value encodes both the number of problems solved and the time penalty, ensuring correct ordering in a single ZSET.
- Penalty is computed as
time_since_contest_start + (wrong_attempts × 10 minutes)per problem. - Rankings are fetched using
ZREVRANK—O(log N)regardless of participant count. - The
my_standingfeature uses an additionalZREVRANKcall to pinpoint the current user's exact rank without fetching the entire leaderboard. - For live contests, rankings come from Redis. For ended/upcoming contests, rankings are computed from the database and sorted in-memory.
Watch how any contestant solved a problem — event by event, keystroke by keystroke.
Capture Pipeline:
Monaco Editor → Socket.io (replay:events) → In-Memory Buffer → Background Worker → Supabase Storage
- Initialization: When a user opens a contest problem,
replay:initcreates atimeline_idand acontest_replaysDB row. - Event Capture: Every Monaco editor change is captured as
{ seq, ts, op, pos, char }. Events are batched every 500ms or 50 events and sent via Socket.io. - In-Memory Aggregation: The server holds events in a per-timeline
Map<seq, event>buffer, deduplicated by sequence number. - Background Flusher: A
setIntervalworker fires every 2 seconds (configurable). It flushes buffers that have ≥500 events or have been idle for the flush interval to Supabase Storage as named JSON chunks:contest/{contestId}/timeline/{timelineId}/chunk_{startSeq}_{endSeq}.json. - Finalization: On accepted submission or contest end, the buffer is force-flushed and the timeline is marked
is_finalized = true— making it immutable. - Playback: The frontend fetches all chunks, merges events sorted by
seq, and reconstructs the code state from scratch by applying operations in order, with play/pause/seek controls.
Guarantees: Strict sequence ordering, idempotent duplicate detection, graceful shutdown flush.
A full interview suite for staff to evaluate candidates in real-time.
- Scheduling: Staff schedule interviews with a candidate, a time window, and an optional problem. A unique
room_idis auto-generated. - WebRTC Signaling: Peer-to-peer video/audio is established over
Socket.ioas the signaling channel. The server forwardsoffer,answer, andice-candidateevents between the two sockets. - Collaborative Code Editor: The candidate's keystrokes are broadcast via
code-changeevents. The interviewer receives them in real-time as a read-only observer. Acode-sync-request/code-sync-responsehandshake enables the interviewer to request a full code snapshot at any point. - Screen Sharing: Participants can share their screen.
screen-share-started/screen-share-stoppedevents notify the other party. - Audio/Video Toggles: Mute/unmute and camera on/off state is signaled to the other participant.
- Connection Tracking:
candidate_connectedandinterviewer_connectedDB flags are kept in sync, so the UI always reflects who is in the room. - Interview Status Machine: Interviews transition
Scheduled → Ongoing → Completed. Completion can be triggered by the interviewer or automatically whenend_timehas passed. - Post-Interview Evaluation: Staff can submit
feedback,candidate_rating (1-5), andtechnical_scoreafter the interview concludes. - Code Submissions: In-session code runs are tracked per interview.
- Monaco Editor: VSCode-grade editing experience with syntax highlighting, multi-language support, minimap, and configurable themes.
- Sample Test Runner: Before submitting, users can run their code against sample inputs synchronously. The server submits to Judge0, polls for up to 60 seconds, and returns the result.
- Full Submission: All test cases are submitted to Judge0 in parallel with a 60-second overall timeout. Results arrive asynchronously via Judge0 webhooks.
- Standalone IDE (
/ide): A freeform code runner that accepts customstdin. Useful for practicing outside problem constraints. - Problem Detail View: Tabbed interface with problem statement, constraints, examples, submissions history, and an integrated editor side-by-side.
User submits → DB record created (pending) → Test cases fetched from Supabase Storage
→ All test cases submitted to Judge0 in parallel → Webhook fires per test case
→ Per-test verdict aggregated → Final submission verdict emitted
- Test case input/output files are stored in Supabase Storage and read on-demand during submission.
- Each test case gets its own Judge0 token, tracked in a
test_resultsJSONB column. - Webhooks are received at
PUT /api/webhooks/judge0. The server uses a per-submission async lock (submissionWebhookLocks) to prevent race conditions when multiple test cases finish simultaneously. - Intermediate verdicts are computed and persisted as results arrive, so the UI shows live progress.
- A Stuck Test Poller is scheduled after 15 seconds if a submission remains pending — it polls Judge0 directly for any test cases that missed their webhook.
- C++ compilation errors encoded in base64 by Judge0 are automatically decoded before being stored.
Users can upload their resume (PDF/DOC) to unlock personalized job recommendations.
- Resumes are uploaded to Supabase Storage under
resumes/{clerkUserId}/{timestamp}-{filename}. - Three external API calls are made in parallel:
/upload-resume,/analyze-resume,/get-recommendations. - Skills extracted from all three responses are de-duplicated, normalized, and persisted on the user profile.
- On subsequent visits, the latest stored resume is fetched from Supabase and re-sent to the recommendation API without re-uploading.
- Only users with
app_role = 'user'can access this feature (staff are excluded).
Judge0 runs on an Azure VM that is managed dynamically to avoid idle cloud costs.
- Auto-Start on Request: Every authenticated API request passes through
trackActivityAndStartVMmiddleware. If the VM isdeallocatedorstopped, it is started via the Azure ARM Compute SDK non-blocking in the background. - Execution Guard:
ensureVMReadyForExecution()blocks the code submission until the VM reachesrunningstate (up to 180 seconds, polling every 5 seconds). - Auto-Shutdown Cron: A
node-cronjob runs every 5 hours (0 */5 * * *). It reads the user'slast_active_attimestamp from the DB. If no activity for ≥5 hours, the VM is stopped and deallocated. - Activity Tracking: Every authenticated request fires
updateUserActivity()to refresh thelast_active_attimestamp (non-blocking, no request delay).
- Onboarding Flow: New users are routed to
/onboardingto set a username, role (userorstaff), and company name (for staff).app_rolecan only be set during onboarding when it isNULL. - Profile Stats: Problems solved, contests participated, current rating, max rating.
- Activity Heatmap: GitHub-style contribution heatmap showing daily activity.
- Rating History: Line chart of rating changes over time.
- Public Profiles: Viewable by anyone at
/profile/:username. Staff have separate public profiles at/staff/:staffId. - Skills: Extracted from resumes and displayed on the profile.
User Dashboard:
- Submission statistics (accepted, wrong answer, runtime error, etc.)
- Language breakdown pie chart
- Recent accepted submissions
- Contest participation history
Staff Dashboard:
- Overview of all managed contests
- Candidate pipeline with interview statuses
- Quick-access to schedule interviews and create contests
┌─────────────────────────────────────────────────────────────────────┐
│ CLIENT (React + Vite) │
│ React Router v7 │ Redux Toolkit │ Monaco Editor │ Socket.io Client │
└──────────────────────────────┬──────────────────────────────────────┘
│ REST + GraphQL + WebSocket
┌──────────────────────────────▼──────────────────────────────────────┐
│ SERVER (Express 5 + Node.js) │
│ │
│ ┌─────────────┐ ┌──────────────┐ ┌─────────────────────────────┐ │
│ │ REST API │ │ GraphQL │ │ Socket.io Namespaces │ │
│ │ (Express) │ │ (Apollo 5) │ │ WebRTC Signaling │ │
│ └──────┬──────┘ └──────┬───────┘ │ Contest Replay Signaling │ │
│ │ │ └─────────────┬───────────────┘ │
│ ┌──────▼────────────────▼──────────────────────▼───────────────┐ │
│ │ Service Layer │ │
│ │ contest.service │ submissions.service │ interview.service │ │
│ │ contestReplay.service │ users.service │ vm.service │ │
│ └──────┬────────────────────────────────────────────────────────┘ │
│ │ │
│ ┌──────▼────────────────────────────────────────────────────────┐ │
│ │ Repository Layer (SQL via postgres) │ │
│ └──────┬────────────────────────────────────────────────────────┘ │
└─────────┼───────────────────────────────────────────────────────────┘
│
┌──────▼──────────────────────────────────────────────┐
│ External Services │
│ ┌──────────┐ ┌───────┐ ┌───────┐ ┌───────────┐ │
│ │ Supabase │ │ Redis │ │Judge0 │ │ Azure VM │ │
│ │ Postgres │ │ ZSET │ │ API │ │ ARM SDK │ │
│ │ Storage │ │ │ │ │ │ │ │
│ └──────────┘ └───────┘ └───────┘ └───────────┘ │
└─────────────────────────────────────────────────────┘
┌──────────────┐ replay:events ┌──────────────────────┐
│ Monaco Editor│ ──────────────────────► │ Socket.io Handler │
│ (500ms batch)│ │ appendContestReplay │
└──────────────┘ │ Events() │
└──────────┬───────────┘
│ writes to
┌──────────▼───────────┐
│ In-Memory Buffer │
│ Map<seq, event> │
└──────────┬───────────┘
│ every 2s or 500 events
┌──────────▼───────────┐
│ Background Worker │
│ flushReplayBuffers │
│ Tick() │
└──────────┬───────────┘
│ uploads chunk JSON
┌──────────▼───────────┐
│ Supabase Storage │
│ chunk_000001_000500 │
│ .json │
└──────────────────────┘
POST /api/submissions
│
├── ensureVMReadyForExecution() ─► polls Azure VM state → starts if needed
│
├── createSubmission() in DB (verdict: 'pending')
│
├── fetch test cases from Supabase Storage
│
├── for each test case:
│ └── POST to Judge0 with callback_url → receives token
│
├── store all tokens in test_results JSONB
│
└── return submission to client (pending)
[Later, async]
PUT /api/webhooks/judge0
│
├── withSubmissionWebhookLock(submissionId, ...)
│ ├── find test case by token in test_results JSONB
│ ├── update test case verdict
│ ├── compute intermediate overall verdict
│ └── if all tests done → compute final verdict → mark solved
│
└── if still pending after 15s → Stuck Test Poller polls Judge0 directly
| Technology | Purpose |
|---|---|
| React 19 | UI framework with concurrent features |
| Vite 7 | Build tool with HMR |
| React Router v7 | Client-side routing with role-based guards |
| Redux Toolkit + Redux Persist | Global state management with localStorage persistence |
| Tailwind CSS v4 + DaisyUI | Utility-first styling with component library |
| @monaco-editor/react | VSCode-grade embedded code editor |
| Socket.io Client | WebSockets for real-time events (replay, interviews, leaderboard) |
| @clerk/clerk-react | Authentication UI and session management |
| Chart.js + react-chartjs-2 | Submission stats charts |
| react-calendar-heatmap | GitHub-style activity heatmap |
| axios | HTTP client for REST API calls |
| react-hot-toast | Toast notifications |
| Lucide React + React Icons | Icon libraries |
| Technology | Purpose |
|---|---|
| Node.js (ESM) | JavaScript runtime |
| Express 5 | HTTP server with async error handling |
| Apollo Server 5 | GraphQL server |
| @as-integrations/express5 | Apollo–Express integration |
| Socket.io 4 | WebSocket server for real-time features |
| @clerk/express | Clerk authentication middleware (clerkMiddleware()) |
| Supabase JS SDK | Database queries and Storage operations |
| postgres | Low-level PostgreSQL client for raw SQL queries |
| ioredis | Redis client for ZSET leaderboard and caching |
| @azure/arm-compute + @azure/identity | Azure VM power management |
| groq-sdk | Groq LLM API integration |
| node-cron | Cron job scheduler (VM auto-shutdown) |
| multer | Multipart form-data for resume uploads |
| axios | HTTP client for Judge0 and external APIs |
| dotenv | Environment variable management |
| cors, cookie-parser | HTTP middleware |
| Service | Role |
|---|---|
| Supabase (PostgreSQL) | Primary relational database |
| Supabase Storage | Blob storage for test cases, replay chunks, and resumes |
| Redis | Leaderboard ZSET, in-flight data |
| Judge0 | Sandboxed code execution engine (self-hosted on Azure VM) |
| Azure VM (ARM) | Compute host for Judge0; dynamically started/stopped |
| Clerk | Authentication, user management, webhooks for user sync |
| Groq API | LLM inference for AI features |
| Job Recommendation API | External microservice for resume parsing and job matching |
codezen/
├── client/ # React frontend (Vite)
│ └── src/
│ ├── App.jsx # Root router with RBAC guards
│ ├── main.jsx # React + Redux + Clerk bootstrap
│ ├── pages/
│ │ ├── HomePage.jsx # Landing page
│ │ ├── Dashboard.jsx # User stats dashboard
│ │ ├── ProblemsPage.jsx # Problem listing
│ │ ├── ProblemDetail.jsx # Problem view + editor
│ │ ├── CodeEditor.jsx # Full-page code editor
│ │ ├── Ide.jsx # Standalone IDE (freeform runner)
│ │ ├── Interview.jsx # Live interview room (WebRTC)
│ │ ├── AllInterviews.jsx # User's interview list
│ │ ├── JobRecommendations.jsx # Resume upload + job matching
│ │ ├── MySubmissions.jsx # Submission history
│ │ ├── Submission.jsx # Single submission detail
│ │ ├── Onboarding.jsx # New user role selection
│ │ ├── MyProfile.jsx # Authenticated user profile
│ │ ├── PublicProfile.jsx # Public user profile
│ │ ├── contest/
│ │ │ ├── AllContests.jsx # Contest listing
│ │ │ ├── Contest.jsx # Contest info / registration
│ │ │ ├── OngoingContest.jsx # Live contest dashboard
│ │ │ ├── ContestProblemDetail.jsx # Contest problem + editor + replay
│ │ │ └── CodeReplay.jsx # Replay viewer
│ │ └── staff/
│ │ ├── StaffDashboard.jsx # Staff overview
│ │ ├── StaffContests.jsx # Contest list
│ │ ├── StaffCreateContest.jsx # Create contest form
│ │ ├── StaffContestDetail.jsx # Manage contest / leaderboard
│ │ ├── StaffInterviews.jsx # Interview pipeline
│ │ ├── StaffScheduleInterview.jsx # Schedule interview form
│ │ ├── StaffInterviewDetail.jsx # View interview / evaluate
│ │ ├── StaffProfile.jsx # Staff edit profile
│ │ └── StaffPublicProfile.jsx # Staff public profile
│ ├── components/
│ ├── redux/ # Redux slices & store
│ ├── lib/ # Axios instances, helpers
│ └── utils/ # Utility functions
│
├── server/ # Express backend
│ └── src/
│ ├── server.js # Entry point: Express + Apollo + Socket.io
│ ├── config/
│ │ ├── env.config.js # Typed env variable access
│ │ ├── supabase.client.js # Supabase JS client singleton
│ │ └── redis.client.js # ioredis client with graceful close
│ ├── routes/
│ │ ├── problems.route.js # GET /api/problems (public)
│ │ ├── contests.route.js # /api/contests (CRUD + leaderboard)
│ │ ├── submissions.route.js # /api/submissions (create, run, list)
│ │ ├── users.route.js # /api/users (profile, resume, jobs)
│ │ ├── interviews.route.js # /api/interviews (CRUD)
│ │ ├── interview_problems.route.js
│ │ ├── webhooks.route.js # PUT /api/webhooks/judge0
│ │ └── judge0.route.js # GET /api/judge0 (health check)
│ ├── controllers/
│ ├── services/
│ │ ├── contest.service.js # Contest CRUD, leaderboard logic
│ │ ├── contestReplay.service.js # In-memory replay buffers + flush worker
│ │ ├── submissions.service.js # Submission pipeline, webhook aggregation
│ │ ├── interview.service.js # Interview CRUD + status machine
│ │ ├── judge0.service.js # Judge0 API wrapper
│ │ ├── users.service.js # Profile, resume, job recommendations
│ │ └── vm.service.js # Azure VM start/stop + activity tracking
│ ├── repositories/
│ │ ├── contests.repo.js
│ │ ├── submissions.repo.js
│ │ ├── interviews.repo.js
│ │ ├── users.repo.js
│ │ ├── problems.repo.js
│ │ ├── contestReplay.repo.js
│ │ └── vms.repo.js
│ ├── graphql/
│ │ └── schema.js # Apollo typeDefs + resolvers (meProfile)
│ ├── lib/
│ │ ├── webrtc.signaling.js # Socket.io WebRTC relay
│ │ └── contestReplay.signaling.js # Socket.io replay event ingestion
│ ├── middleware/
│ │ └── auth.middleware.js # trackActivityAndStartVM
│ └── jobs/
│ └── vmSync.job.js # node-cron every 5h: auto-shutdown
│
└── context/ # Engineering design docs
├── CodeReplay.md # Replay system spec
└── LiveLeaderboard.md # Leaderboard design spec
| Method | Endpoint | Auth | Description |
|---|---|---|---|
GET |
/api/health |
No | Health check |
GET |
/api/problems |
No | List all problems |
GET |
/api/problems/:id |
No | Get problem by ID |
POST |
/api/submissions |
Yes | Create a new submission |
POST |
/api/submissions/run |
Yes | Run against sample input |
GET |
/api/submissions |
Yes | Get user's submissions |
GET |
/api/submissions/:id |
Yes | Get single submission |
GET |
/api/contests |
Yes | List all contests |
POST |
/api/contests |
Staff | Create a contest |
GET |
/api/contests/:id |
Yes | Get contest details |
GET |
/api/contests/:id/problems |
Yes | Get contest problems |
POST |
/api/contests/:id/register |
Yes | Register for a contest |
GET |
/api/contests/:id/leaderboard |
Yes | Get paginated leaderboard |
POST |
/api/contests/:id/submissions |
Yes | Submit to contest problem |
POST |
/api/contests/:id/replay/init |
Yes | Initialize replay timeline |
POST |
/api/contests/:id/replay/:timelineId/finalize |
Yes | Finalize replay |
GET |
/api/users/profile |
Yes | Get own profile |
PUT |
/api/users/profile |
Yes | Update profile |
GET |
/api/users/:username |
No | Get public profile |
POST |
/api/users/resume |
Yes | Upload resume + extract skills |
GET |
/api/users/jobs/recommendations |
Yes | Get job recommendations |
GET |
/api/interviews |
Yes | Get user's interviews |
POST |
/api/interviews |
Staff | Create/schedule an interview |
GET |
/api/interviews/:id |
Yes | Get interview details |
PUT |
/api/interviews/:id |
Staff | Update interview (feedback, score) |
PUT |
/api/webhooks/judge0 |
No | Judge0 async execution callback |
GET |
/api/judge0 |
No | Judge0 health status |
| Endpoint | Query | Description |
|---|---|---|
POST /graphql |
meProfile |
Get authenticated user's full profile |
WebRTC Interview Signaling:
| Event (Emit) | Event (Listen) | Description |
|---|---|---|
join-interview |
participant-joined |
Join interview room |
offer |
offer |
Forward WebRTC SDP offer |
answer |
answer |
Forward WebRTC SDP answer |
ice-candidate |
ice-candidate |
Forward ICE candidate |
toggle-audio |
participant-audio-toggled |
Notify audio state change |
toggle-camera |
participant-camera-toggled |
Notify video state change |
code-change |
code-changed |
Broadcast code delta to interviewer |
code-sync-request |
code-sync-request |
Request full code snapshot |
code-sync-response |
code-synced |
Deliver full code snapshot |
screen-share-started |
participant-screen-sharing |
Notify screen share state |
end-call |
participant-left |
Leave interview room |
Contest Replay Signaling:
| Event (Emit) | Description |
|---|---|
replay:init |
Initialize a new replay timeline |
replay:events |
Append a batch of keystroke events |
replay:flush |
Force-flush the in-memory buffer to storage |
replay:finalize |
Finalize and lock the timeline |
# Core
PORT=3000
NODE_ENV=development
FRONTEND_URL=http://localhost:5173
# PostgreSQL (Supabase)
DATABASE_URL=postgresql://<user>:<password>@<host>:5432/<database>
# Supabase
SUPABASE_URL=https://<project-ref>.supabase.co
SUPABASE_SERVICE_ROLE_KEY=<supabase-service-role-key>
# Clerk Auth
CLERK_PUBLISHABLE_KEY=<clerk-publishable-key>
CLERK_SECRET_KEY=<clerk-secret-key>
# Judge0 Code Execution
JUDGE_SERVER_URL=http://<judge-host>:8080
JUDGE_AUTH_TOKEN=<judge-x-auth-token>
# Webhook URL for Judge0 callbacks (must be publicly reachable)
PUBLIC_BACKEND_URL=https://yourdomain.com
JUDGE_CALLBACK_URL= # optional override
# Redis
REDIS_URL=redis://localhost:6379
# Azure VM (for Judge0 host)
SUBSCRIPTION_ID=<azure-subscription-id>
TENANT_ID=<azure-tenant-id>
CLIENT_ID=<azure-client-id>
CLIENT_SECRET=<azure-client-secret>
RESOURCE_GROUP=<azure-resource-group>
VM_NAME=<azure-vm-name>
# AI
GROQ_API_KEY=<groq-api-key>
# Optional
MAX_REQUEST_BODY_LIMIT=10mb
REPLAY_FLUSH_INTERVAL_MS=2000
REPLAY_FLUSH_EVENT_THRESHOLD=500
REPLAY_STORAGE_BUCKET=contest_submission_events
RESUMES_STORAGE_BUCKET=resumes
JOB_RECOMMENDATION_API_URL=https://your-job-api.example.comVITE_CLERK_PUBLISHABLE_KEY=<clerk-publishable-key>
VITE_API_BASE_URL=http://localhost:3000
VITE_SUPABASE_URL=https://<project-ref>.supabase.co
VITE_SUPABASE_ANON_KEY=<supabase-anon-key>- Node.js v18 or higher
- Redis (local via Docker or a managed cloud instance)
- Supabase project (PostgreSQL + Storage)
- Clerk application (get publishable + secret keys)
- Judge0 instance (self-hosted or cloud) — must be reachable by the backend
git clone https://github.com/yourusername/codezen.git
cd codezencd server
cp .env.example .env
# Fill in all values in .env
npm install
npm run devThe server starts on http://localhost:3000 and automatically:
- Connects to Redis and Supabase
- Starts the Apollo GraphQL server at
/graphql - Initializes WebRTC and Replay Socket.io namespaces
- Starts the replay flush worker
- Registers the VM sync cron job (production only)
cd client
# Create .env with your Clerk and Supabase keys
npm install
npm run devVite serves the frontend at http://localhost:5173.
Create the following Storage buckets in your Supabase project:
contest_submission_events— for replay timeline JSON chunksresumes— for user-uploaded resume files- A bucket for test case files (input/output for each problem)
Ensure your Supabase Row Level Security (RLS) policies restrict replay and submission access to the owning user.
Authentication is handled by Clerk. Every authenticated request includes a JWT validated by @clerk/express's clerkMiddleware().
| Role | app_role value |
Access |
|---|---|---|
| Regular User | 'user' |
Problems, contests, IDE, interviews (as candidate), job recommendations |
| Staff | 'staff' |
All of the above + contest management, interview scheduling, candidate evaluation |
| Unauthenticated | null |
Homepage, problems listing, public profiles |
Key Rules:
app_roleis set once during onboarding (when the DB value isNULL) and is immutable afterwards.- Staff cannot participate in contests as contestants.
- Staff dashboard, contest creation, and interview management routes are guarded at both the frontend router level (
App.jsx) and backend service layer (assertStaff()).
A relational ORDER BY query over thousands of submissions is O(N log N) and degrades under high concurrency. Redis ZSET's ZADD and ZREVRANK are both O(log N) and atomic — no locks, no transactions, no cache invalidation issues. The composite score (solved × 1M) - penalty fits in a single float, making complex multi-key sorting unnecessary.
The replay system uses in-process Map buffers rather than Redis Streams to avoid the operational overhead of consumer groups and the latency of round-trips on every keystroke. The periodic flush worker is a lightweight setInterval, and graceful shutdown flushes all buffers synchronously before process.exit(). This is the right trade-off for a monolithic deployment.
Judge0 fires one webhook per test case. Multiple test cases for the same submission finish nearly simultaneously, creating a race between concurrent DB reads and writes. A JavaScript Promise-chain lock (zero overhead, no external dependency) serializes these updates, preventing torn writes to the test_results JSONB column.
Webhooks deliver results immediately with no polling overhead. But webhooks can be lost under network failures. The Stuck Test Poller is a safety net: 15 seconds after a partial update, if the submission is still pending, the server polls Judge0 directly for any tokens that went quiet. This gives both speed and reliability.
This project is licensed under the ISC License.
Built from the ground up with ❤️ — every line intentional, every feature production-ready.