Skip to main content

Jobnik Concepts & Data Structures

This document covers the core concepts of Jobnik: the three-level hierarchy, the structure of each entity, and how data flows through the system.

For state machine behaviour, see State Transitions.


The Jobnik Hierarchy​

Jobnik uses a three-level hierarchy to organise work:

  • Job: The root entity representing an entire high-level business process (e.g., "Create Map")
  • Stage: A sequential phase of that process (e.g., "Process Tiles", "Upload Tiles")
  • Task: An atomic unit of work within a stage (e.g., "Process tile_001")

Key Concepts:

  • Each Job can have multiple Stages
  • Stages are ordered sequentially — each stage has an order field (1, 2, 3, etc.)
  • A stage cannot transition to PENDING until the previous stage is COMPLETED
  • Each Stage can have multiple Tasks
  • Tasks within a stage are processed independently and in parallel by Workers
  • The Manager enforces all valid state transitions

Job Structure​

A Job is the root entity that represents an entire workflow.

const job = await producer.createJob({
name: 'create-map', // Job type name
data: { // Business data for the job
mapId: 'map-123',
region: 'north',
},
userMetadata: { // Additional metadata
requestedBy: 'user-456',
requestTimestamp: '2026-02-17T10:00:00Z',
},
priority: 'HIGH', // VERY_HIGH | HIGH | MEDIUM | LOW | VERY_LOW
});

Job Fields:

FieldTypeDescription
idstringAuto-generated UUID.
namestringJob type identifier, must match a defined job type.
statusenumCREATED → PENDING → IN_PROGRESS → COMPLETED / FAILED / ABORTED
dataobjectImmutable business payload needed throughout the job lifecycle.
userMetadataobjectAdditional context (audit trails, debug info). Not used for task execution.
priorityenumDetermines task dequeue order: VERY_HIGH, HIGH, MEDIUM, LOW, VERY_LOW.
createdAtstringISO 8601 timestamp. Set automatically.
updatedAtstringISO 8601 timestamp. Updated on any change.

Stage Structure​

A Stage represents a sequential phase within a job. It holds configuration shared across all of its tasks.

Stage Ordering

Stages are sequentially ordered. Each stage gets an order number (1, 2, 3, …) on creation. A stage can only become PENDING once the previous stage is COMPLETED.

const stage = await producer.createStage(job.id, {
type: 'process-tiles', // Stage type name
data: { // Configuration shared by all tasks
resolution: 'high',
outputFormat: 'png',
},
userMetadata: { // Stage-level metadata (mutable during processing)
processedCount: 0,
},
});

Stage Fields:

FieldTypeDescription
idstringAuto-generated UUID.
jobIdstringParent job reference.
typestringStage type identifier. Workers subscribe to specific stage types.
ordernumberAuto-assigned execution sequence (1, 2, 3, …).
statusenumCREATED → PENDING → IN_PROGRESS → COMPLETED / FAILED / ABORTED / WAITING
dataobjectImmutable configuration shared by all tasks in this stage.
userMetadataobjectMutable metadata. Can be updated by workers via context.updateStageUserMetadata().
summaryobjectAggregated task counts by status (pending, inProgress, completed, failed, created, retried, total). Auto-maintained by the system.
percentagenumberfloor((completed / total) * 100). Auto-updated.
startTimestring | nullWhen the first task started. null until then.
endTimestring | nullWhen the stage reached a terminal state. null until then.
createdAtstringISO 8601 timestamp. Set automatically.
updatedAtstringISO 8601 timestamp. Updated on any change.

Task Structure​

A Task is an atomic unit of work executed by a worker.

await producer.createTasks(stage.id, [
{
data: { // Task-specific data
tileId: 'tile-001',
sourceUrl: 's3://raw/tile-001.tif',
targetPath: 's3://processed/tile-001.png',
},
userMetadata: {
batchId: 'batch-001',
},
maxAttempts: 3, // Optional, default: 3
},
]);

Task Fields:

FieldTypeDescription
idstringAuto-generated UUID.
stageIdstringParent stage reference.
statusenumCREATED → PENDING → IN_PROGRESS → COMPLETED / FAILED / RETRIED / ABORTED
dataobjectImmutable, task-specific payload. What makes each task unique within a stage.
userMetadataobjectAdditional task context. Set at creation, not updatable afterwards.
attemptsnumberHow many times this task has been attempted. Auto-incremented.
maxAttemptsnumberRetry limit. When attempts >= maxAttempts and task fails, it transitions to FAILED. Default: 3.
createdAtstringISO 8601 timestamp. Set automatically.
updatedAtstringISO 8601 timestamp. Updated on status change.

data vs userMetadata​

Key Distinction

data is your immutable business payload — the parameters needed to execute the work.
userMetadata is mutable helper data — context for tracking, debugging, and monitoring.

AspectdatauserMetadata
PurposeCore parameters for task executionTracking, audit trails, debug context
MutabilityImmutable after creationMutable for stages (via updateStageUserMetadata)
Example (Job){ mapId, region }{ requestedBy, requestTimestamp }
Example (Stage){ resolution, outputFormat }{ processedCount, failedCount }
Example (Task){ tileId, sourceUrl, targetPath }{ batchId, priority }

Use data for: Essential parameters that define what the task does — input/output locations, processing settings, business identifiers.

Use userMetadata for: Non-essential context — who requested it, when, progress counters, audit info, or anything you might want to update as the stage progresses.


See Also​