Repository navigation
Distributed Judging
English | 简体中文
For full AI execution, use --role all on the web node, keeping its local judge/AI worker, then add remote judges for ordinary submissions. For complete separation, use --role web; it does not build local rootfs, so remote judges handle ordinary submissions.
flowchart LR
Browser[Browser] --> Nginx[Nginx]
Nginx --> Frontend[Frontend assets]
Nginx --> Backend[Backend]
Backend --> DB[MariaDB]
Backend --> Redis[Redis]
Backend --> MinIO[MinIO]
Judges[Local or remote judges] <-->|Authenticated WebSocket /api/socket| Backend
Judges --> Sandbox[Per-node simple-sandbox and rootfs]
Judges -->|Signed /storage/ downloads| Nginx
Backend -->|all mode: local UNIX socket| AIWorker[Local AI sandbox worker]
Judges initiate connections and need no inbound web port. They do not hold database, Redis or MinIO administrator credentials. They receive tasks, download/cache test files through signed URLs and return results. This deployment uses a single backend, not a highly available backend/database cluster.
Judge tasks are kept in the database (table judge_job), not only in Redis: a submission, Hack or Self Eval and its task are saved in one transaction, so restarting the backend or Redis loses no queued work. Redis only wakes up waiting judges; they also look for tasks every few seconds.
- A task is sent to one judge connection at a time, under a lease that the backend renews while the judge stays connected. The result is stored together with finishing the task, and a result from an older lease (for example after a reconnect) is ignored.
- When a judge disconnects, its unfinished tasks are queued again at once. When the backend itself stops, its leases expire after 90 seconds and another backend process, or the restarted one, queues them again. A task can therefore be judged more than once, but its result counts once.
- A task is given up on after 5 interruptions, or after 10 failures to prepare it (for example missing test data). Its submission then shows System Error with the reason and can be rejudged.
- Site administrators see the queue at the bottom of Judge Machine (
/judge-machine): tasks waiting or being judged, the judge machine holding each, retries, and the tasks given up on in the last 7 days. Thelibreoj_judge_jobsmetric reports waiting and running tasks. - Every lease is recorded in
judge_job_attemptwith how it ended. Finished tasks and their records are deleted after 30 days.
These are on the Judge Machine page for site administrators, and every change is recorded in the audit log (judge_client.add, delete, reset_key, drain, resume).
- Drain (pause icon) stops sending new tasks to one judge machine; it finishes the tasks it has. Its status shows Draining while it still judges, then Drained: it can now be stopped, updated or resized without interrupting judging, while the other judge machines take the queue. It stays drained after reconnecting or a backend restart until you Resume it (play icon). Draining doesn't affect local AI validation, which doesn't use the queue.
- Reset key and Delete take effect at once: every backend process disconnects the connections using the old key, and their unfinished tasks are queued again for other judge machines. Reset the key when it may have leaked, then put the new key on the judge machine.
- Capabilities: when a judge machine connects, it announces its protocol version, the task types it can run, its task threads, execution slots, slots for AI validation and memory budget; the Capacity column shows them. The backend sends it only the task types it announced. A judge machine of an older version announces nothing and is marked Legacy: it is sent only submissions, so Hacks and Self Evals wait until an updated judge machine is online.
sudo bash /tmp/llmoj-install.sh --repository houyhlouis/LLMOJ \
--role web --public-url http://oj.example.comFor public remote judging, configure the final HTTPS origin first, or use a controlled private network. HTTP sends judge credentials without transport encryption.
The installer configures WebSocket Upgrade at /api/socket and sets services.minio.forJudge.urlEndpoint to the public origin's /storage/. Nginx restores the internal Host used in MinIO signatures. Database and MinIO remain on loopback.
An administrator registers a node with POST /api/judgeClient/addJudgeClient and body {"name":"judge-01","allowedHosts":[]}.
In the browser Console on the site while logged in as admin, run this request. It uses the current session without printing or copying its token:
if (!window._appState?.currentUser?.isAdmin) throw new Error('Sign in as an administrator first');
fetch('/api/judgeClient/addJudgeClient', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': 'Bearer ' + window._appState.token
},
body: JSON.stringify({name: 'judge-01', allowedHosts: []})
}).then(response => response.json()).then(result => {
console.log(result.judgeClient ? 'Created; save the key privately from this response' : result);
});In the Network panel, save judgeClient.key from that response to this node's private key file. Register each machine separately. A new connection using the same key replaces the previous session; do not share keys between judges.
The gateway authenticates keys. Although the API stores allowedHosts, the current connection logic does not enforce source-IP checking through that field. Use a reverse proxy, VPN or firewall for source restrictions.
Follow Remote-Judge using the same source version/rootfs recipe as the web node. After “Successfully authorized as …”, query status from the administrator Console:
fetch('/api/judgeClient/listJudgeClients', {
headers: {'Authorization': 'Bearer ' + window._appState.token}
}).then(response => response.json()).then(result => {
console.table(result.judgeClients?.map(({id, name, online}) => ({id, name, online})));
});Submit a simple C++ program and confirm downloading, compilation, execution and Accepted. Test compilation errors, time/memory limits and cancellation on each node. Restart a node to inspect recovery; multiple clients can consume the queue, but recovery is not an exactly-once execution guarantee.
Backend model calls, editing, translation and search can still be configured. AI reference-solution validation and test-data generation currently use a local UNIX socket and private shared directories on the web server; there is no cross-machine AI worker protocol. Adding remote judges for ordinary submissions does not move these actions off the web server. They need the local worker retained by all and are unavailable with web. Do not replace the private socket with an open TCP service or uncontrolled shared directory.
- Connection fails: check final origin, TLS,
/api/socketUpgrade and the node's key. - Online but downloads fail: inspect signed URLs for localhost/private endpoints and check
/storage/Host handling. - Nodes replace one another: check for shared keys.
- cgroup errors: check systemd delegation, cgroup v2 and kernel support.
- AI execution fails despite remote judges: check whether the web node retains a local worker.
- Hacks or Self Evals stay waiting: check that an online judge machine that isn't drained runs the current version (not Legacy) and lists them under Capacity.
- A judge machine gets no tasks: check whether it is drained, and resume it.
Real multi-machine deployment has not yet been validated end to end; complete the on-site tests above.
See Judge configuration for every YAML field, consumers versus execution/download queues, the 8 CPU/8 GiB example, and resizing/rollback while retaining keys. Maintain and verify one node at a time. More remote slots do not increase the web node's local AI worker capacity. Do not change installer metadata or rerun with a different --judge-slots as a substitute.
简体中文
- LLMOJ Wiki
- AI 运行环境变量与固定限制
- AI 配置、权限与执行流程
- 后端配置完整参考
- 团队、题解、作废、Hack 与自测升级
- 配置总览与生效规则
- 比赛赛制
- 分布式评测与网页分离
- Docker 源
- 环境变量、构建与维护工具
- 前端与 bootstrap 配置
- HTTPS 与反向代理
- Hack
- 安装与端口配置
- 评测端完整配置与 CPU 扩缩容
- 基础服务与安装后运维配置
- 发布源码、版本与 GitHub Wiki
- 新用户注册审核
- 独立评测机安装
- Self Eval(NOI / OI 比赛预测试)
- 题解
- 评测记录 Fake
- 评测记录预览
- 团队
- 两步验证
- 保留配置与数据的一键升级
English
- LLMOJ Wiki
- AI configuration, permissions and execution
- AI runtime environment and fixed limits
- Complete backend configuration reference
- Teams, solutions, moderation, Hack and self-evaluation upgrade
- Configuration overview and activation
- Contest rules
- Distributed judging and web/judge separation
- Docker sources
- Environment, builds, and maintenance tools
- Frontend and bootstrap configuration
- HTTPS and reverse proxies
- Hacks
- Installation and ports
- Complete judge configuration and CPU resizing
- Infrastructure and post-install operations
- Source, release and GitHub Wiki publishing
- Registration approval
- Independent judge installation
- Self Eval for NOI / OI contests
- Solutions
- Fake submission moderation
- Submission previews
- Teams
- Two-factor sign-in
- Upgrading while preserving configuration and data