SimC Orchestration
How the NestJS API coordinates with the Rust SimC worker for simulation jobs. For the Rust worker internals, see SimC Service.
Job Lifecycle
sequenceDiagram
participant API as NestJS API
participant SQS as SQS Queue
participant W as Rust Worker
API->>API: Create SimJob (queued)
API->>SQS: Send SQS message
API-->>API: Return jobId to client
W->>SQS: Poll & receive message
W->>API: POST /sim-callback (running)
API->>API: Update SimJob → running
API->>API: Emit sim-progress WS
loop Per character
W->>W: Run SimC binary
W->>W: Upload raw output to S3
W->>API: POST /sim-callback (completedCount++)
API->>API: Emit sim-progress WS
end
W->>API: POST /sim-callback (completed + results)
API->>API: storeSimResults
API->>API: Update Character cache
API->>API: Emit sim-complete WS
W->>SQS: Delete SQS message
Job Types
| Type | Description | Created By |
|---|---|---|
single_character | Sim one character's current gear | POST /characters/:id/sim |
group_batch | Sim multiple characters in one job | POST /organizations/:id/sim-group |
build_classification | Run 3 fight profiles to classify a build | POST /characters/:id/classify-build |
SQS Message Format
{
"jobId": "uuid",
"jobType": "single_character",
"characters": [
{
"id": "uuid",
"name": "Thrall",
"realm": "area-52",
"region": "us",
"profile": null
}
],
"s3ResultPrefix": "sims/uuid/",
"callbackUrl": "https://api.example.com/internal/sim-callback",
"callbackApiKey": "secret",
"fightStyles": [
{ "fightStyle": "Patchwerk", "maxTime": 300 },
{ "fightStyle": "DungeonSlice" }
]
}
fightStyles is only set for build_classification jobs. Single-character and group jobs use the worker's default profile.
Callback Handling
The worker sends HTTP callbacks to POST /internal/sim-callback with the x-sim-callback-key header for authentication.
Callback Payloads
Running:
{ "jobId": "uuid", "status": "running", "completedCount": 2 }
Completed:
{
"jobId": "uuid",
"status": "completed",
"completedCount": 5,
"results": [
{
"characterId": "uuid",
"dps": 142500.0,
"hps": 0.0,
"fightStyle": "Patchwerk",
"maxTime": 300,
"simDuration": 300.12,
"rawOutputKey": "sims/uuid/character-uuid.json"
}
]
}
Processing Steps
- Validate
x-sim-callback-keyagainstSIM_CALLBACK_API_KEY - Update
SimJobstatus,startedAt,completedCount,completedAt - On completion:
storeSimResults()— createSimResultrows, updateCharacter.simDps/simHps/simmedAt- For
build_classification:finalizeClassification()— compute build type, upsertBuildClassification
- Emit WebSocket events to
/sim-syncnamespace
WebSocket Events
Namespace: /sim-sync
| Event | Payload | When |
|---|---|---|
sim-progress | { jobId, completedCount, characterCount } | Worker reports progress |
sim-complete | { jobId, stats } | Job finished successfully |
sim-error | { jobId, error } | Job failed |
build-classification | { jobId, characterId, buildType, isBurst } | Classification computed |
Clients subscribe via emit('subscribe-sim', { jobId }) and unsubscribe via emit('unsubscribe-sim', { jobId }).
Result Storage
SimResult Table
One row per character per job. Stores DPS, HPS, fight style, duration, and S3 key for raw SimC JSON output.
Character Cache Fields
On completion, the latest results are cached directly on the Character model:
| Field | Description |
|---|---|
simDps | Latest simulated DPS |
simHps | Latest simulated HPS |
simmedAt | Timestamp of latest simulation |
API Endpoints
User-Facing
| Method | Path | Description |
|---|---|---|
| POST | /characters/:id/sim | Queue character sim |
| POST | /organizations/:id/sim-group | Queue group sim |
| POST | /characters/:id/classify-build | Classify one build |
| POST | /characters/:id/classify-all-builds | Classify all builds |
| GET | /sim-jobs/mine | List my jobs |
| GET | /sim-jobs/:id | Get job details |
| GET | /sim-jobs/:id/results | Get job results |
| GET | /characters/:id/sim-history | Character sim history |
| GET | /characters/:id/sim-config | Gear + loadouts for UI |
| GET | /characters/:id/talent-comparison | Build comparison data |
Internal
| Method | Path | Auth | Description |
|---|---|---|---|
| POST | /internal/sim-callback | x-sim-callback-key | Worker callback |
Frontend Hooks
useSimSync(jobId)— subscribe to/sim-syncfor a single job's progress, completion, and errorsuseBuildClassification(jobId)— subscribe forbuild-classificationevents on a single jobuseBatchClassification(jobIds)— track multiple classification jobs with aggregate progress andonCompletecallback