How a submission moves through CodeMas

Six actors hand one submission along in nine steps, from the student pressing Submit to the result on their screen. Postgres stays the single source of truth; the queue only ever carries a small reference (job ID, kind, HMAC token). Locally, a thread pool inside the relay stands in for SQS and Lambda and runs the same handler code.

Stage 3 (current): SQS and Lambda in production; local transport in dev. Languages on this pipeline: Python and JavaScript. Celery + Redis remain only for plagiarism, AI tasks and scheduled jobs.
Accept Student submits code Step 1 of 9
Browser student Django API + internal API Postgres submission + job Relay run_relay SQS codemas-exec Lambda codemas-exec POST code one transaction submission + job: pending 201 submission_id pending jobs SKIP LOCKED {job_id, kind, token} job: queued trigger claim(job_id, token) lease + token job: running, read code code + tests + token run tests in sandbox complete(token, outputs) grade if token owns job job: succeeded GET /status/, 1 s → 10 s passed / failed
Solid arrows are requests or writes; dashed arrows are replies. The highlighted arrows belong to the current step. Lambda never touches Postgres: it reads the code and writes the results through Django.

Student submits code

Handed over


    

Why it works this way

If something goes wrong

Job row in Postgres

A job gets three claims. If it still has no result, it becomes dead and its submission error, which doesn't count against the student's attempts. codemas-exec-dlq holds messages whose delivery wasn't acknowledged 3 times. Causes: crashes and timeouts; Django unreachable; a claim refused with 401, 404 or busy; a failed /fail/ call; a complete answered with anything but 200 or 409; and claims skipped for lack of time. A job released through /fail/ has its message acknowledged, so it dies in Postgres with no message in the DLQ.

POST-EXAM — runs once when exam closes

Plagiarism Detection Flow

14 steps · 3 phases · triggered once at exam close, runs entirely in background

—
Press Play or → to begin
Trainer closes exam → Django pre_save signal fires → Celery batch task orchestrates Phase 1 (behavioural, sync) then Phase 2 (similarity, async per question).
👤
Trainer
Exam report
⚙️
Django
API Server
⚡
Redis
Queue + Cache
🔧
Celery
Worker
🗃️
Postgres
Permanent Store
Step — of —
Press Play or → to begin
Trainer closes exam → pre_save signal fires → Celery batch task orchestrates Phase 1 (behavioural scoring, synchronous) then Phase 2 (TF-IDF similarity, async per question with 30s stagger).
Data / Command
—
Speed: