Skip to main content
Version: 8.9 (unreleased)

Job Workers

Job workers long-poll for available jobs, execute a callback, and automatically complete or fail the job based on the return value. Workers are available on CamundaAsyncClient.

import asyncio
from camunda_orchestration_sdk import CamundaAsyncClient, WorkerConfig
from camunda_orchestration_sdk.runtime.job_worker import JobContext

async def handle_job(job_context: JobContext) -> dict:
variables = job_context.variables.to_dict()
job_context.log.info(f"Processing job {job_context.job_key}: {variables}")
# Return a dict to set output variables
return {"result": "processed"}

async def main():
async with CamundaAsyncClient() as client:
config = WorkerConfig(
job_type="my-service-task",
job_timeout_milliseconds=30_000,
)
client.create_job_worker(config=config, callback=handle_job)

# Keep workers running until cancelled
await client.run_workers()

asyncio.run(main())

Job Logger

Each JobContext exposes a log property — a scoped logger automatically bound with the job's context (job type, worker name, and job key). Use it inside your handler for structured, per-job log output:

async def handler(job: JobContext) -> dict:
job.log.info(f"Starting work on {job.job_key}")
# ... do work ...
job.log.debug("Work completed successfully")
return {"done": True}

The job logger inherits the SDK's logger configuration (loguru by default, or whatever you passed via logger=). If you injected a custom logger into the client, job handlers will use a child of that same logger.

Note: When using the "process" execution strategy, the job logger silently degrades to a no-op (NullLogger) because loggers cannot be pickled across process boundaries. The worker's main-process logger still records all job lifecycle events (activation, completion, failure, errors). If you need per-job logging from a process-isolated handler, configure a logger inside the handler itself.

Execution Strategies

Job workers support multiple execution strategies to match your workload type. Set execution_strategy in WorkerConfig or let the SDK auto-detect.

StrategyHow it runs your handlerBest for
"auto" (default)Auto-detects: "async" for async def handlers, "thread" for sync handlersMost use cases — sensible defaults without configuration
"async"Runs on a dedicated asyncio event loopI/O-bound async work (HTTP calls, database queries)
"thread"Runs in a ThreadPoolExecutorBlocking I/O (file system, synchronous HTTP libraries)
"process"Runs in a ProcessPoolExecutorCPU-bound work that needs to escape the GIL (image processing, ML inference)

Auto-detection logic: If your handler is an async def, the strategy defaults to "async". If it's a regular def, the strategy defaults to "thread". You can override this explicitly:

# Force thread pool for a sync handler
config = WorkerConfig(
job_type="io-bound-task",
job_timeout_milliseconds=30_000,
execution_strategy="thread",
)

# Force process pool for CPU-heavy work
config = WorkerConfig(
job_type="image-processing",
job_timeout_milliseconds=120_000,
execution_strategy="process",
)

Process strategy caveats: The "process" strategy serialises (pickles) your handler and JobContext to send them to a worker process. This means:

  • Your handler function and its closure must be picklable (top-level functions work; lambdas and closures over unpicklable objects do not).
  • job.log degrades to a silent no-op logger in the child process (see Job Logger).
  • There is additional overhead per job from serialisation and inter-process communication.

Worker Configuration

WorkerConfig supports:

ParameterDefaultDescription
job_type(required)The BPMN service task type to poll for
job_timeout_milliseconds(required)How long the worker has to complete the job
request_timeout_milliseconds0Long-poll request timeout (0 = server default)
max_concurrent_jobs10Maximum jobs executing concurrently
execution_strategy"auto""auto", "async", "thread", or "process"
fetch_variablesNoneList of variable names to fetch (None = all)
worker_name"camunda-python-sdk-worker"Identifier for this worker in Camunda