Runners¶
Reference for all Pynenc runner implementations, their configuration, and execution models.
Runner Hierarchy¶
BaseRunner (ABC)
├── ThreadRunner
├── MultiThreadRunner
├── ProcessRunner
├── PersistentProcessRunner
├── DummyRunner
│ └── ExternalRunner
All runners are in pynenc.runner and can be selected via configuration:
[tool.pynenc]
runner_cls = "MultiThreadRunner"
Or the builder:
PynencBuilder().multi_thread_runner(min_threads=2, max_threads=8).build()
Common Configuration¶
These fields apply to all runner types via ConfigRunner:
Field |
Type |
Default |
Description |
|---|---|---|---|
|
|
|
Delay between polls when waiting for dependent invocation results |
|
|
|
Sleep between main loop iterations |
|
|
|
Minimum number of concurrent execution slots |
Choosing a Runner¶
The timeline above shows all four runners processing identical short-lived tasks. Orange = pending (waiting), green = completed (ran successfully). See Frequently Asked Questions for a detailed breakdown of each runner’s behavior in this scenario.
Development/testing:
ThreadRunnerwith memory backends, ordev_mode_force_sync_tasks=Truewith no runnerProduction I/O-bound:
MultiThreadRunner— scales processes and threadsProduction CPU-bound:
PersistentProcessRunner— avoids process spawn overhead per taskFine-grained process isolation:
ProcessRunner— one process per invocation with OS-level pause/resume
See Configuration System for full configuration details. See PynencBuilder Reference for programmatic runner selection with the builder API.
ThreadRunner¶
Executes tasks in threads within a single process. Suitable for I/O-bound workloads and development.
mem_compatible: True — works with in-memory backends.
Configuration¶
Field |
Type |
Default |
Description |
|---|---|---|---|
|
|
|
Minimum thread count |
|
|
|
Maximum thread count ( |
Behavior¶
Spawns daemon threads for each invocation
When a task waits on another invocation’s result, the waiting thread is tracked separately so it does not consume a concurrency slot
On shutdown, alive threads are killed and their invocations rerouted
Usage¶
[tool.pynenc]
runner_cls = "ThreadRunner"
[tool.pynenc.runner]
min_threads = 2
max_threads = 8
PynencBuilder().thread_runner(min_threads=2, max_threads=8).build()
MultiThreadRunner¶
Spawns separate processes, each running a ThreadRunner internally. Scales processes up and down based on broker queue depth.
mem_compatible: False — requires a distributed backend.
Configuration¶
Field |
Type |
Default |
Description |
|---|---|---|---|
|
|
|
Threads per child process |
|
|
|
Threads per child process |
|
|
|
Minimum worker processes |
|
|
|
Maximum worker processes ( |
|
|
|
Seconds idle before a process is terminated |
|
|
|
Always maintain |
Behavior¶
Parent process manages child
ThreadRunnerprocessesChildren report active thread count and idle state via shared memory
When
enforce_max_processesisTrue, always runsmax_processesworkersWhen
False, scales based on pending invocation count in the brokerIdle processes beyond
min_processesare terminated afteridle_timeout_process_secDead processes are automatically cleaned up
Usage¶
[tool.pynenc]
runner_cls = "MultiThreadRunner"
[tool.pynenc.runner]
min_processes = 2
max_processes = 8
min_threads = 1
max_threads = 4
PynencBuilder().multi_thread_runner(min_threads=1, max_threads=4).build()
ProcessRunner¶
Executes each invocation in its own process. Uses OS signals (SIGSTOP/SIGCONT) to pause and resume processes waiting on dependency results.
mem_compatible: False — requires a distributed backend.
Configuration¶
Uses the base ConfigRunner fields only. Maximum parallelism equals CPU count.
Behavior¶
One process per invocation, up to
cpu_count()concurrentWhen an invocation depends on another’s result, the process is paused with
SIGSTOPWhen the dependency resolves, the process is resumed with
SIGCONTOn shutdown, child processes are killed with
SIGKILLand invocations rerouted
Usage¶
[tool.pynenc]
runner_cls = "ProcessRunner"
PynencBuilder().process_runner().build()
PersistentProcessRunner¶
Maintains a fixed pool of long-lived worker processes. Each worker loops, fetching and executing invocations sequentially.
mem_compatible: False — requires a distributed backend.
Configuration¶
Field |
Type |
Default |
Description |
|---|---|---|---|
|
|
|
Number of worker processes ( |
Also inherits ConfigThreadRunner fields.
Behavior¶
Spawns
num_processespersistent workers at startupEach worker runs a loop: fetch one invocation → execute → repeat
Dead workers are automatically respawned to maintain pool size
On shutdown, workers receive
SIGTERM, thenSIGKILLafter 5 secondsWhen waiting on dependencies, the worker sleeps (no pause/resume mechanism)
Forces
spawnstart method on macOS
Usage¶
[tool.pynenc]
runner_cls = "PersistentProcessRunner"
[tool.pynenc.runner]
num_processes = 4
PynencBuilder().persistent_process_runner(num_processes=4).build()
DummyRunner¶
Non-executable placeholder used when the app is defined outside a runner context (scripts that route tasks but don’t execute them). All execution methods raise RunnerNotExecutableError.
ExternalRunner¶
Extends DummyRunner with hostname and PID tracking. Generates a runner_id of ExternalRunner@{hostname}-{pid}. Used to track which external process registered an invocation.
Comparison¶
Runner |
Execution Model |
Memory Compatible |
Parallelism |
Auto-Scaling |
Wait Strategy |
|---|---|---|---|---|---|
ThreadRunner |
Threads in one process |
Yes |
|
No |
Free thread slot |
MultiThreadRunner |
Processes × threads |
No |
Processes × threads |
Yes |
Delegated to child |
ProcessRunner |
One process per task |
No |
CPU count |
No |
SIGSTOP/SIGCONT |
PersistentProcessRunner |
Fixed process pool |
No |
|
No (respawns dead) |
Sleep polling |
DummyRunner |
None |
N/A |
N/A |
N/A |
N/A |