@@ -21,16 +21,80 @@ The following properties can be configured via environment variables:
2121
2222| Property | Type | Description | Example | Decorator? |
2323| ----------| ------| -------------| ---------| ------------|
24- | ` poll_interval ` | float | Polling interval in milliseconds | ` 1000 ` | ✅ Yes |
24+ | ` poll_interval_millis ` | int | Polling interval in milliseconds | ` 1000 ` | ✅ Yes |
2525| ` domain ` | string | Worker domain for task routing | ` production ` | ✅ Yes |
2626| ` worker_id ` | string | Unique worker identifier | ` worker-1 ` | ✅ Yes |
27- | ` thread_count ` | int | Number of concurrent threads/ coroutines | ` 10 ` | ✅ Yes |
27+ | ` thread_count ` | int | Max concurrent executions ( threads for sync, coroutines for async) | ` 10 ` | ✅ Yes |
2828| ` register_task_def ` | bool | Auto-register task definition | ` true ` | ✅ Yes |
2929| ` poll_timeout ` | int | Poll request timeout in milliseconds | ` 100 ` | ✅ Yes |
30- | ` lease_extend_enabled ` | bool | Enable automatic lease extension | ` false ` | ✅ Yes |
30+ | ` lease_extend_enabled ` | bool | ⚠️ ** Not implemented ** - reserved for future use | ` false ` | ✅ Yes |
3131| ` paused ` | bool | Pause worker from polling/executing tasks | ` true ` | ❌ ** Environment-only** |
3232
33- ** Note** : The ` paused ` property is intentionally ** not available** in the ` @worker_task ` decorator. It can only be controlled via environment variables, allowing operators to pause/resume workers at runtime without code changes or redeployment.
33+ ** Notes** :
34+ - The ` paused ` property is intentionally ** not available** in the ` @worker_task ` decorator. It can only be controlled via environment variables, allowing operators to pause/resume workers at runtime without code changes or redeployment.
35+ - The ` lease_extend_enabled ` parameter is accepted but ** not currently implemented** . For lease extension, use manual ` TaskInProgress ` returns (see below).
36+
37+ ### Understanding ` thread_count `
38+
39+ The ` thread_count ` parameter has different meanings depending on worker type (automatically detected from function signature):
40+
41+ ** Sync Workers (` def ` ):**
42+ - Controls ThreadPoolExecutor size
43+ - Each task consumes one thread
44+ - Recommended: 1-4 for CPU-bound, 10-50 for I/O-bound
45+
46+ ** Async Workers (` async def ` ):**
47+ - Controls max concurrent async tasks (semaphore limit)
48+ - All tasks share single event loop
49+ - Recommended: 50-200 for I/O-bound (event loop handles thousands)
50+
51+ ** Example:**
52+ ``` python
53+ # Sync worker - thread_count = thread pool size
54+ @worker_task (task_definition_name = ' cpu_task' , thread_count = 4 )
55+ def cpu_task (data : dict ) -> dict :
56+ return expensive_computation(data)
57+
58+ # Async worker - thread_count = concurrency limit (not threads!)
59+ @worker_task (task_definition_name = ' api_task' , thread_count = 100 )
60+ async def api_task (url : str ) -> dict :
61+ async with httpx.AsyncClient() as client:
62+ return await client.get(url)
63+ # Only 1 thread, but 100 concurrent tasks!
64+ ```
65+
66+ ** For more details** , see [ Worker Design Documentation] ( docs/design/WORKER_DESIGN.md ) .
67+
68+ ### Lease Extension for Long-Running Tasks
69+
70+ ** Current Implementation** : Only manual lease extension via ` TaskInProgress ` is supported.
71+
72+ ``` python
73+ from conductor.client.context.task_context import TaskInProgress, get_task_context
74+ from typing import Union
75+
76+ @worker_task (task_definition_name = ' long_running_task' )
77+ def long_task (job_id : str ) -> Union[dict , TaskInProgress]:
78+ ctx = get_task_context()
79+ poll_count = ctx.get_poll_count()
80+
81+ # Process chunk of work
82+ processed = process_chunk(job_id, poll_count)
83+
84+ if not is_complete(job_id):
85+ # More work to do - extend lease by returning TaskInProgress
86+ return TaskInProgress(
87+ callback_after_seconds = 60 , # Return to queue after 60s
88+ output = {' progress' : processed}
89+ )
90+ else :
91+ # Done - return final result
92+ return {' status' : ' completed' , ' result' : processed}
93+ ```
94+
95+ ** ⚠️ Note** : The ` lease_extend_enabled=True ` configuration parameter does ** not** provide automatic lease extension. You must explicitly return ` TaskInProgress ` to extend the lease.
96+
97+ ** For detailed patterns** , see [ Long-Running Tasks & Lease Extension] ( docs/design/WORKER_DESIGN.md#long-running-tasks--lease-extension ) .
3498
3599## Environment Variable Format
36100
@@ -52,7 +116,7 @@ from conductor.client.worker.worker_task import worker_task
52116
53117@worker_task (
54118 task_definition_name = ' process_order' ,
55- poll_interval = 1000 ,
119+ poll_interval_millis = 1000 ,
56120 domain = ' dev' ,
57121 thread_count = 5
58122)
@@ -62,30 +126,30 @@ def process_order(order_id: str) -> dict:
62126
63127### Without Environment Variables
64128Worker uses code-level defaults:
65- - ` poll_interval =1000`
129+ - ` poll_interval_millis =1000`
66130- ` domain='dev' `
67131- ` thread_count=5 `
68132
69133### With Global Override
70134``` bash
71- export conductor.worker.all.poll_interval =500
135+ export conductor.worker.all.poll_interval_millis =500
72136export conductor.worker.all.domain=production
73137```
74138
75139Worker now uses:
76- - ` poll_interval =500` (from global env)
140+ - ` poll_interval_millis =500` (from global env)
77141- ` domain='production' ` (from global env)
78142- ` thread_count=5 ` (from code)
79143
80144### With Worker-Specific Override
81145``` bash
82- export conductor.worker.all.poll_interval =500
146+ export conductor.worker.all.poll_interval_millis =500
83147export conductor.worker.all.domain=production
84148export conductor.worker.process_order.thread_count=20
85149```
86150
87151Worker now uses:
88- - ` poll_interval =500` (from global env)
152+ - ` poll_interval_millis =500` (from global env)
89153- ` domain='production' ` (from global env)
90154- ` thread_count=20 ` (from worker-specific env)
91155
@@ -98,37 +162,36 @@ Override all workers to use production domain and optimized settings:
98162``` bash
99163# Global production settings
100164export conductor.worker.all.domain=production
101- export conductor.worker.all.poll_interval=250
102- export conductor.worker.all.lease_extend_enabled=true
165+ export conductor.worker.all.poll_interval_millis=250
103166
104167# Critical worker needs more resources
105168export conductor.worker.process_payment.thread_count=50
106- export conductor.worker.process_payment.poll_interval =50
169+ export conductor.worker.process_payment.poll_interval_millis =50
107170```
108171
109172``` python
110173# Code remains unchanged
111- @worker_task (task_definition_name = ' process_order' , poll_interval = 1000 , domain = ' dev' , thread_count = 5 )
174+ @worker_task (task_definition_name = ' process_order' , poll_interval_millis = 1000 , domain = ' dev' , thread_count = 5 )
112175def process_order (order_id : str ):
113176 ...
114177
115- @worker_task (task_definition_name = ' process_payment' , poll_interval = 1000 , domain = ' dev' , thread_count = 5 )
178+ @worker_task (task_definition_name = ' process_payment' , poll_interval_millis = 1000 , domain = ' dev' , thread_count = 5 )
116179def process_payment (payment_id : str ):
117180 ...
118181```
119182
120183Result:
121- - ` process_order ` : domain=production, poll_interval =250, thread_count=5
122- - ` process_payment ` : domain=production, poll_interval =50, thread_count=50
184+ - ` process_order ` : domain=production, poll_interval_millis =250, thread_count=5
185+ - ` process_payment ` : domain=production, poll_interval_millis =50, thread_count=50
123186
124187### Development/Debug Mode
125188
126189Slow down polling for easier debugging:
127190
128191``` bash
129- export conductor.worker.all.poll_interval =10000 # 10 seconds
130- export conductor.worker.all.thread_count=1 # Single-threaded
131- export conductor.worker.all.poll_timeout=5000 # 5 second timeout
192+ export conductor.worker.all.poll_interval_millis =10000 # 10 seconds
193+ export conductor.worker.all.thread_count=1 # Single concurrent task
194+ export conductor.worker.all.poll_timeout=5000 # 5 second timeout
132195```
133196
134197All workers will use these debug-friendly settings without code changes.
@@ -143,6 +206,39 @@ export conductor.worker.all.domain=staging
143206
144207All workers use staging domain, but keep their code-defined poll intervals, thread counts, etc.
145208
209+ ### High-Concurrency Async Workers
210+
211+ For async I/O-bound workers, increase concurrency significantly:
212+
213+ ``` bash
214+ # Global settings for async workers
215+ export conductor.worker.all.domain=production
216+ export conductor.worker.all.poll_interval_millis=100 # Lower polling delay for async
217+
218+ # Async worker - high concurrency (event loop can handle it!)
219+ export conductor.worker.fetch_api_data.thread_count=200
220+
221+ # Sync worker - keep moderate thread count
222+ export conductor.worker.process_cpu_task.thread_count=10
223+ ```
224+
225+ ``` python
226+ # Async worker - high concurrency with single event loop
227+ @worker_task (task_definition_name = ' fetch_api_data' )
228+ async def fetch_api_data (url : str ):
229+ async with httpx.AsyncClient() as client:
230+ return await client.get(url)
231+
232+ # Sync worker - traditional thread pool
233+ @worker_task (task_definition_name = ' process_cpu_task' )
234+ def process_cpu_task (data : dict ):
235+ return expensive_computation(data)
236+ ```
237+
238+ ** Result** :
239+ - ` fetch_api_data ` : 200 concurrent async tasks in 1 thread!
240+ - ` process_cpu_task ` : 10 threads for CPU-bound work
241+
146242### Pausing Workers
147243
148244Temporarily disable workers without stopping the process:
@@ -201,7 +297,7 @@ Test new configuration on one worker before rolling out to all:
201297``` bash
202298# Production settings for all workers
203299export conductor.worker.all.domain=production
204- export conductor.worker.all.poll_interval =200
300+ export conductor.worker.all.poll_interval_millis =200
205301
206302# Canary worker uses staging domain for testing
207303export conductor.worker.canary_worker.domain=staging
@@ -231,7 +327,7 @@ services:
231327 image : my-conductor-worker
232328 environment :
233329 - conductor.worker.all.domain=production
234- - conductor.worker.all.poll_interval =250
330+ - conductor.worker.all.poll_interval_millis =250
235331 - conductor.worker.critical_task.thread_count=50
236332` ` `
237333
@@ -244,7 +340,7 @@ metadata:
244340 name : worker-config
245341data :
246342 conductor.worker.all.domain : " production"
247- conductor.worker.all.poll_interval : " 250"
343+ conductor.worker.all.poll_interval_millis : " 250"
248344 conductor.worker.critical_task.thread_count : " 50"
249345---
250346apiVersion : v1
@@ -277,7 +373,7 @@ spec:
277373 env :
278374 - name : conductor.worker.all.domain
279375 value : " production"
280- - name : conductor.worker.all.poll_interval
376+ - name : conductor.worker.all.poll_interval_millis
281377 value : " 250"
282378---
283379apiVersion : apps/v1
@@ -294,7 +390,7 @@ spec:
294390 env :
295391 - name : conductor.worker.all.domain
296392 value : " staging"
297- - name : conductor.worker.all.poll_interval
393+ - name : conductor.worker.all.poll_interval_millis
298394 value : " 500"
299395` ` `
300396
@@ -308,19 +404,19 @@ from conductor.client.worker.worker_config import resolve_worker_config, get_wor
308404# Resolve configuration for a worker
309405config = resolve_worker_config(
310406 worker_name='process_order',
311- poll_interval =1000,
407+ poll_interval_millis =1000,
312408 domain='dev',
313409 thread_count=5
314410)
315411
316412print(config)
317- # {'poll_interval ': 500.0 , 'domain': 'production', 'thread_count': 5, ...}
413+ # {'poll_interval_millis ': 500, 'domain': 'production', 'thread_count': 5, ...}
318414
319415# Get human-readable summary
320416summary = get_worker_config_summary('process_order', config)
321417print(summary)
322418# Worker 'process_order' configuration:
323- # poll_interval : 500.0 (from conductor.worker.all.poll_interval )
419+ # poll_interval_millis : 500 (from conductor.worker.all.poll_interval_millis )
324420# domain: production (from conductor.worker.all.domain)
325421# thread_count: 5 (from code)
326422```
@@ -342,11 +438,11 @@ export conductor.worker.worker3.domain=production
342438``` bash
343439# Global settings for most workers
344440export conductor.worker.all.thread_count=10
345- export conductor.worker.all.poll_interval =250
441+ export conductor.worker.all.poll_interval_millis =250
346442
347443# Exception: High-priority worker needs more resources
348444export conductor.worker.critical_task.thread_count=50
349- export conductor.worker.critical_task.poll_interval =50
445+ export conductor.worker.critical_task.poll_interval_millis =50
350446```
351447
352448### 3. Keep Code Defaults Sensible
@@ -355,10 +451,9 @@ Use sensible defaults in code so workers work without environment variables:
355451``` python
356452@worker_task (
357453 task_definition_name = ' process_order' ,
358- poll_interval = 1000 , # Reasonable default
359- domain = ' dev' , # Safe default domain
360- thread_count = 5 , # Moderate concurrency
361- lease_extend_enabled = False # Default: disabled
454+ poll_interval_millis = 1000 , # Reasonable default (1 second)
455+ domain = ' dev' , # Safe default domain
456+ thread_count = 5 # Moderate concurrency
362457)
363458def process_order (order_id : str ):
364459 ...
@@ -374,12 +469,12 @@ Maintain a README or wiki documenting required environment variables for each de
374469- ` conductor.worker.all.domain=production `
375470
376471## Optional (Recommended)
377- - ` conductor.worker.all.poll_interval =250 `
378- - ` conductor.worker.all.lease_extend_enabled=true `
472+ - ` conductor.worker.all.poll_interval_millis =250 `
473+ - ` conductor.worker.all.thread_count=20 `
379474
380475## Worker-Specific Overrides
381476- ` conductor.worker.critical_task.thread_count=50 `
382- - ` conductor.worker.critical_task.poll_interval =50 `
477+ - ` conductor.worker.critical_task.poll_interval_millis =50 `
383478```
384479
385480### 5. Use Infrastructure as Code
@@ -397,8 +492,12 @@ resource "kubernetes_deployment" "worker" {
397492 value = var.environment_name
398493 }
399494 env {
400- name = "conductor.worker.all.poll_interval"
401- value = var.worker_poll_interval
495+ name = "conductor.worker.all.poll_interval_millis"
496+ value = var.worker_poll_interval_millis
497+ }
498+ env {
499+ name = "conductor.worker.all.thread_count"
500+ value = var.worker_thread_count
402501 }
403502 }
404503 }
@@ -467,5 +566,47 @@ The hierarchical worker configuration system provides flexibility to:
467566- ** Override at runtime** : No code changes needed for environment-specific settings
468567- ** Fine-tune per worker** : Optimize critical workers without affecting others
469568- ** Simplify management** : Use global settings for common configurations
569+ - ** Pause/resume at runtime** : Control worker execution without redeployment
570+
571+ ** Configuration priority** : Worker-specific > Global > Code defaults
572+
573+ ### Key Configuration Patterns
574+
575+ ** Sync Workers (CPU-bound):**
576+ ``` bash
577+ export conductor.worker.cpu_task.thread_count=4 # Thread pool size
578+ export conductor.worker.cpu_task.poll_interval_millis=500 # Moderate polling
579+ ```
580+
581+ ** Async Workers (I/O-bound):**
582+ ``` bash
583+ export conductor.worker.api_task.thread_count=100 # High concurrency
584+ export conductor.worker.api_task.poll_interval_millis=100 # Fast polling
585+ ```
586+
587+ ** Long-Running Tasks:**
588+ ``` bash
589+ # Note: Use TaskInProgress for lease extension (lease_extend_enabled not implemented)
590+ export conductor.worker.ml_training.thread_count=2 # Limit concurrent long tasks
591+ export conductor.worker.ml_training.poll_interval_millis=500
592+ ```
593+
594+ ---
595+
596+ ## Additional Resources
597+
598+ - ** [ Worker Design Documentation] ( docs/design/WORKER_DESIGN.md ) ** - Complete worker architecture guide
599+ - AsyncTaskRunner vs TaskRunner
600+ - Automatic runner selection (` def ` vs ` async def ` )
601+ - Performance comparison and best practices
602+ - Worker discovery and metrics
603+
604+ - ** [ Examples] ( examples/ ) ** - Working examples with configuration
605+ - ` examples/worker_configuration_example.py ` - Hierarchical configuration demo
606+ - ` examples/workers_e2e.py ` - End-to-end example
607+ - ` examples/asyncio_workers.py ` - Mixed sync/async workers
608+
609+ ---
470610
471- Configuration priority: ** Worker-specific** > ** Global** > ** Code defaults**
611+ ** Last Updated** : 2025-11-28
612+ ** SDK Version** : 1.3.0+
0 commit comments