Grader
The Grader is the central Go microservice responsible for managing submission queues, coordinating code evaluation across multiple Runners, and determining final verdicts. It acts as the dispatcher between the PHP frontend and the execution infrastructure.
Overview
The Grader handles the entire submission lifecycle:
- Receives submissions from the frontend
- Places them in appropriate priority queues
- Dispatches to available Runners
- Collects execution results
- Runs validators and computes scores
- Stores results and notifies clients
Architecture
flowchart TB
subgraph Frontend
PHP[PHP Application]
API[/api/Run/create/]
end
subgraph Grader
Receiver[Submission Receiver]
QueueMgr[Queue Manager]
Dispatcher[Run Dispatcher]
ResultHandler[Result Handler]
Validator[Output Validator]
Scorer[Score Calculator]
end
subgraph Runners
R1[Runner 1]
R2[Runner 2]
R3[Runner 3]
end
subgraph Storage
DB[(MySQL)]
Git[GitServer]
Broadcaster[Broadcaster]
end
PHP --> API --> Receiver
Receiver --> QueueMgr
QueueMgr --> Dispatcher
Dispatcher --> R1 & R2 & R3
R1 & R2 & R3 --> ResultHandler
ResultHandler --> Validator
Validator --> Scorer
Scorer --> DB
Scorer --> Broadcaster
Grader <--> Git
Queue System
Queue Types
The Grader maintains 8 different queues for prioritization:
| Queue | Priority | Purpose |
|---|---|---|
contest |
1 (Highest) | Active contest submissions |
urgent |
2 | High-priority contests |
contest_rejudge |
3 | Contest rejudge requests |
normal |
4 | Practice mode submissions |
rejudge |
5 | General rejudge requests |
slow_contest |
6 | Slow problems in contests |
slow_urgent |
7 | Slow problems (high-priority) |
slow |
8 (Lowest) | Slow problems (general) |
Queue Routing Logic
flowchart TD
Sub[New Submission] --> IsContest{Contest?}
IsContest -->|Yes| IsSlow1{Slow Problem?}
IsContest -->|No| IsRejudge{Rejudge?}
IsSlow1 -->|Yes| SlowContest[slow_contest]
IsSlow1 -->|No| Contest[contest]
IsRejudge -->|Yes| IsSlow2{Slow Problem?}
IsRejudge -->|No| IsSlow3{Slow Problem?}
IsSlow2 -->|Yes| Slow[slow]
IsSlow2 -->|No| Rejudge[rejudge]
IsSlow3 -->|Yes| Slow
IsSlow3 -->|No| Normal[normal]
Slow Problem Detection
Problems are marked as "slow" if they have:
- Time limit > 30 seconds
- Memory limit > 512 MB
- History of long execution times
Runner Management
Runner Registration
Runners register with the Grader on startup:
type RunnerInfo struct {
Name string
Hostname string
Port int
MaxJobs int
Features []string // e.g., ["cpp", "java", "python"]
}
Load Balancing
The Dispatcher uses weighted round-robin:
- Check runner availability
- Consider current load per runner
- Match runner capabilities to submission language
- Dispatch to least-loaded suitable runner
Health Monitoring
sequenceDiagram
participant G as Grader
participant R as Runner
loop Every 10 seconds
G->>R: Health check
alt Healthy
R-->>G: OK + stats
else Unhealthy
G->>G: Mark runner unavailable
G->>G: Reassign pending jobs
end
end
Evaluation Flow
Detailed Sequence
sequenceDiagram
participant F as Frontend
participant G as Grader
participant Q as Queue
participant R as Runner
participant V as Validator
participant DB as Database
participant B as Broadcaster
F->>G: Submit (code, problem, contest)
G->>G: Authenticate & validate
G->>Q: Enqueue submission
G->>F: Return run_id
Q->>G: Dequeue (when runner available)
G->>R: Dispatch job
R->>R: Compile code
R->>R: Execute per test case
R-->>G: Return results
G->>V: Validate outputs
V-->>G: Validation results
G->>G: Calculate score
G->>DB: Store verdict
G->>B: Notify clients
Result Structure
{
"run_id": 12345,
"verdict": "PA",
"score": 0.75,
"contest_score": 75.0,
"runtime": 0.234,
"memory": 16384,
"compile_error": null,
"cases": [
{"name": "1", "verdict": "AC", "score": 1.0, "runtime": 0.05},
{"name": "2", "verdict": "AC", "score": 1.0, "runtime": 0.08},
{"name": "3", "verdict": "WA", "score": 0.0, "runtime": 0.06},
{"name": "4", "verdict": "AC", "score": 1.0, "runtime": 0.05}
]
}
Validation
Validator Types
| Type | Description | Use Case |
|---|---|---|
token-caseless |
Tokenize, case-insensitive compare | Most problems |
token-numeric |
Compare numbers with tolerance | Floating-point |
literal |
Exact byte-by-byte match | Precise output |
custom |
User-provided validator program | Multiple answers |
Custom Validator Interface
// validator.cpp
#include <iostream>
#include <fstream>
int main(int argc, char* argv[]) {
// argv[1] = input file
// argv[2] = expected output (may be empty)
// argv[3] = contestant output
std::ifstream input(argv[1]);
std::ifstream expected(argv[2]);
std::ifstream contestant(argv[3]);
// Validation logic...
// Output score (0.0 to 1.0)
std::cout << 1.0 << std::endl;
return 0;
}
Scoring Logic
Score Calculation
def calculate_score(test_results, scoring_mode):
if scoring_mode == "all_or_nothing":
# All tests must pass
return 1.0 if all(r.verdict == "AC" for r in test_results) else 0.0
elif scoring_mode == "partial":
# Sum of individual test scores
return sum(r.score * r.weight for r in test_results)
elif scoring_mode == "max_per_group":
# Group tests, take max per group
groups = group_by(test_results, lambda r: r.group)
return sum(max(r.score for r in g) * g.weight for g in groups)
Verdict Hierarchy
When combining test case verdicts:
1. CE (Compilation Error) - if compile failed
2. JE (Judge Error) - if judge had issues
3. TLE - if any test exceeded time
4. MLE - if any test exceeded memory
5. RTE - if any test crashed
6. WA - if any test wrong answer
7. PA - if partial score > 0 but < 1
8. AC - if all tests passed
API Endpoints
Internal API (Runners)
| Endpoint | Method | Description |
|---|---|---|
/register |
POST | Runner registration |
/job |
GET | Get next job |
/result |
POST | Submit results |
/health |
GET | Health check |
Web Interface
Access at http://grader:36663/grader/:
- Queue status and lengths
- Runner status
- Recent submissions
- Error logs
Configuration
Key Settings
{
"grader": {
"port": 21680,
"web_port": 36663,
"max_queue_length": 10000,
"runner_timeout": 300,
"validation_timeout": 60,
"db_connection_pool": 20
},
"queues": {
"contest": {"workers": 4},
"normal": {"workers": 8},
"slow": {"workers": 2}
}
}
Environment Variables
| Variable | Default | Description |
|---|---|---|
GRADER_DB_HOST |
localhost | MySQL host |
GRADER_DB_NAME |
omegaup | Database name |
GRADER_LOG_LEVEL |
info | Log verbosity |
GRADER_METRICS_PORT |
6060 | Prometheus metrics |
Monitoring
Key Metrics
| Metric | Type | Description |
|---|---|---|
grader_queue_length |
Gauge | Items per queue |
grader_runs_total |
Counter | Total processed |
grader_run_duration |
Histogram | Processing time |
grader_runners_active |
Gauge | Active runners |
grader_validation_errors |
Counter | Validator failures |
Alerts
- alert: GraderQueueBacklog
expr: grader_queue_length{queue="contest"} > 50
for: 5m
annotations:
summary: "Contest queue backlog"
- alert: NoRunnersAvailable
expr: grader_runners_active == 0
for: 1m
annotations:
summary: "No runners available"
Troubleshooting
Common Issues
Submissions stuck in queue:
# Check queue status
curl http://grader:36663/grader/status/
# Check runner connectivity
curl http://runner1:port/health
Validator errors:
# Check grader logs
docker-compose logs grader | grep -i validator
Slow processing:
# Check metrics
curl http://grader:6060/metrics | grep queue
Related Documentation
- Grader Internals - Deep technical details
- Runner - Code execution system
- Verdicts - Verdict explanations
- Architecture - System overview