Skip to main content

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

TypeDescriptionCreated By
single_characterSim one character's current gearPOST /characters/:id/sim
group_batchSim multiple characters in one jobPOST /organizations/:id/sim-group
build_classificationRun 3 fight profiles to classify a buildPOST /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

  1. Validate x-sim-callback-key against SIM_CALLBACK_API_KEY
  2. Update SimJob status, startedAt, completedCount, completedAt
  3. On completion:
    • storeSimResults() — create SimResult rows, update Character.simDps/simHps/simmedAt
    • For build_classification: finalizeClassification() — compute build type, upsert BuildClassification
  4. Emit WebSocket events to /sim-sync namespace

WebSocket Events

Namespace: /sim-sync

EventPayloadWhen
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:

FieldDescription
simDpsLatest simulated DPS
simHpsLatest simulated HPS
simmedAtTimestamp of latest simulation

API Endpoints

User-Facing

MethodPathDescription
POST/characters/:id/simQueue character sim
POST/organizations/:id/sim-groupQueue group sim
POST/characters/:id/classify-buildClassify one build
POST/characters/:id/classify-all-buildsClassify all builds
GET/sim-jobs/mineList my jobs
GET/sim-jobs/:idGet job details
GET/sim-jobs/:id/resultsGet job results
GET/characters/:id/sim-historyCharacter sim history
GET/characters/:id/sim-configGear + loadouts for UI
GET/characters/:id/talent-comparisonBuild comparison data

Internal

MethodPathAuthDescription
POST/internal/sim-callbackx-sim-callback-keyWorker callback

Frontend Hooks

  • useSimSync(jobId) — subscribe to /sim-sync for a single job's progress, completion, and errors
  • useBuildClassification(jobId) — subscribe for build-classification events on a single job
  • useBatchClassification(jobIds) — track multiple classification jobs with aggregate progress and onComplete callback