Skip to content

Commit 728f36c

Browse files
committed
schema
1 parent 6e0741d commit 728f36c

14 files changed

Lines changed: 777 additions & 56 deletions

README.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -464,8 +464,14 @@ export conductor.worker.all.domain=production
464464
export conductor.worker.all.poll_interval_millis=250
465465
export conductor.worker.all.thread_count=20
466466

467+
# Task registration configuration
468+
export conductor.worker.all.register_task_def=true # Auto-register task definitions
469+
export conductor.worker.all.overwrite_task_def=true # Overwrite existing (default)
470+
export conductor.worker.all.strict_schema=false # Lenient schema validation (default)
471+
467472
# Worker-specific configuration (overrides global)
468473
export conductor.worker.greetings.thread_count=50
474+
export conductor.worker.validate_order.strict_schema=true # Strict validation for this worker
469475

470476
# Runtime control (pause/resume workers without code changes)
471477
export conductor.worker.all.paused=true # Maintenance mode

WORKER_CONFIGURATION.md

Lines changed: 68 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,14 +26,18 @@ The following properties can be configured via environment variables:
2626
| `worker_id` | string | Unique worker identifier | `worker-1` | ✅ Yes |
2727
| `thread_count` | int | Max concurrent executions (threads for sync, coroutines for async) | `10` | ✅ Yes |
2828
| `register_task_def` | bool | Auto-register task definition with JSON schemas on startup | `true` | ✅ Yes |
29+
| `overwrite_task_def` | bool | Overwrite existing task definitions when registering (default: true) | `false` | ✅ Yes |
30+
| `strict_schema` | bool | Enforce strict schema validation - additionalProperties=false (default: false) | `true` | ✅ Yes |
2931
| `poll_timeout` | int | Poll request timeout in milliseconds | `100` | ✅ Yes |
3032
| `lease_extend_enabled` | bool | ⚠️ **Not implemented** - reserved for future use | `false` | ✅ Yes |
3133
| `paused` | bool | Pause worker from polling/executing tasks | `true` |**Environment-only** |
3234

3335
**Notes**:
3436
- 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.
3537
- The `lease_extend_enabled` parameter is accepted but **not currently implemented**. For lease extension, use manual `TaskInProgress` returns (see below).
36-
- The `register_task_def` parameter automatically registers task definitions with JSON Schema (draft-07) generated from Python type hints. Does not overwrite existing definitions.
38+
- The `register_task_def` parameter automatically registers task definitions with JSON Schema (draft-07) generated from Python type hints.
39+
- The `overwrite_task_def` parameter controls whether to overwrite existing task definitions (default: true).
40+
- The `strict_schema` parameter controls JSON schema validation strictness (default: false for lenient validation).
3741

3842
### Understanding `thread_count`
3943

@@ -97,6 +101,69 @@ def long_task(job_id: str) -> Union[dict, TaskInProgress]:
97101

98102
**For detailed patterns**, see [Long-Running Tasks & Lease Extension](docs/design/WORKER_DESIGN.md#long-running-tasks--lease-extension).
99103

104+
### Understanding `overwrite_task_def`
105+
106+
Controls whether to overwrite existing task definitions when `register_task_def=True`:
107+
108+
**Overwrite Mode (default, overwrite_task_def=true):**
109+
- Always calls `update_task_def()` to overwrite existing definitions
110+
- Ensures server always has latest configuration from code
111+
- **Use when:** Task configuration changes frequently, development environments
112+
113+
**No-Overwrite Mode (overwrite_task_def=false):**
114+
- Checks if task exists before registering
115+
- Only creates new task if it doesn't exist
116+
- Preserves manual changes made on server
117+
- **Use when:** Tasks managed outside code, production with manual config
118+
119+
```bash
120+
# Global: Never overwrite any task definitions
121+
export conductor.worker.all.overwrite_task_def=false
122+
123+
# Specific: Allow overwrite for this worker only
124+
export conductor.worker.dynamic_task.overwrite_task_def=true
125+
```
126+
127+
### Understanding `strict_schema`
128+
129+
Controls JSON Schema validation strictness when `register_task_def=True`:
130+
131+
**Lenient Mode (default, strict_schema=false):**
132+
- Sets `additionalProperties=true` in schemas
133+
- Allows extra fields beyond defined schema
134+
- **Use when:** Backward compatibility, flexible integrations, development
135+
136+
**Strict Mode (strict_schema=true):**
137+
- Sets `additionalProperties=false` in schemas
138+
- Rejects inputs with extra fields
139+
- **Use when:** Strict contract enforcement, production validation
140+
141+
```bash
142+
# Global: Strict validation for all workers
143+
export conductor.worker.all.strict_schema=true
144+
145+
# Specific: Lenient for this worker (overrides global)
146+
export conductor.worker.flexible_task.strict_schema=false
147+
```
148+
149+
**Example Schemas:**
150+
151+
```json
152+
// strict_schema=false (default)
153+
{
154+
"type": "object",
155+
"properties": {"name": {"type": "string"}},
156+
"additionalProperties": true // ← Extra fields allowed
157+
}
158+
159+
// strict_schema=true
160+
{
161+
"type": "object",
162+
"properties": {"name": {"type": "string"}},
163+
"additionalProperties": false // ← Extra fields rejected
164+
}
165+
```
166+
100167
## Environment Variable Format
101168

102169
### Global Configuration (All Workers)

docs/design/WORKER_DESIGN.md

Lines changed: 88 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -410,6 +410,8 @@ async def __get_authentication_headers(self):
410410
| `poll_timeout` | int | 100 | Poll timeout (ms) |
411411
| `lease_extend_enabled` | bool | False | ⚠️ **Not implemented** - use `TaskInProgress` instead |
412412
| `register_task_def` | bool | False | Auto-register task definition with JSON schemas (draft-07) |
413+
| `overwrite_task_def` | bool | True | Overwrite existing task definitions (when register_task_def=True) |
414+
| `strict_schema` | bool | False | Enforce strict schema validation (additionalProperties=false) |
413415
| `paused` | bool | False | Pause worker (env-only, not in decorator) |
414416

415417
### Examples
@@ -514,6 +516,80 @@ export conductor.worker.all.register_task_def=true
514516

515517
# Enable for specific worker
516518
export conductor.worker.process_order.register_task_def=true
519+
520+
# Control overwrite behavior
521+
export conductor.worker.all.overwrite_task_def=false # Don't overwrite existing
522+
523+
# Enable strict schema validation
524+
export conductor.worker.process_order.strict_schema=true # No extra fields allowed
525+
```
526+
527+
### Schema Validation Modes
528+
529+
The `strict_schema` flag controls JSON Schema validation strictness:
530+
531+
**Lenient Mode (default, strict_schema=False):**
532+
```json
533+
{
534+
"type": "object",
535+
"properties": {...},
536+
"additionalProperties": true ← Extra fields allowed
537+
}
538+
```
539+
540+
**Strict Mode (strict_schema=True):**
541+
```json
542+
{
543+
"type": "object",
544+
"properties": {...},
545+
"additionalProperties": false ← Extra fields rejected
546+
}
547+
```
548+
549+
**Use Cases:**
550+
- **Lenient (default):** Development, backward compatibility, flexible integrations
551+
- **Strict:** Production, strict validation, contract enforcement
552+
553+
**Example:**
554+
```python
555+
@worker_task(
556+
task_definition_name='validate_order',
557+
register_task_def=True,
558+
strict_schema=True # Enforce strict validation
559+
)
560+
def validate_order(order_id: str, amount: float) -> dict:
561+
return {}
562+
563+
# Generated schema will reject inputs with extra fields
564+
```
565+
566+
### Task Definition Overwrite Control
567+
568+
The `overwrite_task_def` flag controls update behavior:
569+
570+
**Overwrite Mode (default, overwrite_task_def=True):**
571+
- Always calls `update_task_def()` (overwrites existing)
572+
- Ensures server has latest configuration from code
573+
- Use in development or when task config changes frequently
574+
575+
**No-Overwrite Mode (overwrite_task_def=False):**
576+
- Checks if task exists first
577+
- Only creates if doesn't exist
578+
- Preserves manual changes on server
579+
- Use when tasks are managed outside code
580+
581+
**Example:**
582+
```python
583+
@worker_task(
584+
task_definition_name='stable_task',
585+
register_task_def=True,
586+
overwrite_task_def=False # Don't overwrite existing config
587+
)
588+
def stable_worker(data: dict) -> dict:
589+
return {}
590+
591+
# If task exists on server, keeps existing configuration
592+
# If task doesn't exist, registers new one
517593
```
518594

519595
### Startup Configuration Logging
@@ -1435,7 +1511,7 @@ Expected improvements for I/O-bound async workers:
14351511

14361512
## Changelog
14371513

1438-
### Version 4.1 (2025-11-28)
1514+
### Version 4.1 (2025-11-30)
14391515
- Enhanced Worker Discovery section with comprehensive WorkerLoader documentation
14401516
- Expanded Long-Running Tasks section with detailed lease extension patterns
14411517
- Added practical examples for checkpointing and external system polling
@@ -1451,16 +1527,26 @@ Expected improvements for I/O-bound async workers:
14511527
- Generates JSON Schema (draft-07) from Python type hints
14521528
- Supports dataclasses, Optional, List, Dict, Union types
14531529
- Creates schemas named {task_name}_input and {task_name}_output
1454-
- Does not overwrite existing definitions or schemas
14551530
- Works for both TaskRunner and AsyncTaskRunner
1531+
- Added task_def parameter for advanced configuration (retry, timeout, rate limits)
1532+
- **Added overwrite_task_def and strict_schema flags:**
1533+
- `overwrite_task_def` (default: True) - Controls whether to overwrite existing task definitions
1534+
- `strict_schema` (default: False) - Controls additionalProperties in JSON schemas
1535+
- Both configurable via environment variables
1536+
- Applies to both sync and async workers
14561537
- **Added TaskUpdateFailure event:**
14571538
- Published when task update fails after all retry attempts (4 retries with exponential backoff: 10s/20s/30s)
14581539
- Contains TaskResult for recovery/logging
14591540
- Enables external handling of critical update failures
14601541
- Event count: 7 total events (was 6)
1542+
- **Fixed Optional[T] handling:**
1543+
- Optional[T] parameters are NOT required in schema
1544+
- Optional[T] parameters are marked nullable
1545+
- Works correctly with nested dataclasses
14611546
- Added detailed polling loop with dynamic batch sizing examples
14621547
- Improved troubleshooting guidance
14631548
- Fixed class-based worker support in TaskHandler async detection
1549+
- Fixed task_def_template passing in scan_for_annotated_workers path
14641550

14651551
### Version 4.0 (2025-11-28)
14661552
- AsyncTaskRunner: Pure async/await execution (zero thread overhead)

examples/workers_e2e.py

Lines changed: 37 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,12 @@
2020
* Optional fields (Optional[str], default values)
2121
* Generates JSON Schema draft-07 automatically
2222
* Registers schemas as {task_name}_input and {task_name}_output
23+
- ⭐ CONFIGURATION FLAGS:
24+
* overwrite_task_def: Control whether to overwrite existing task definitions
25+
* strict_schema: Control JSON schema validation strictness (additionalProperties)
26+
* Both flags support environment variable override
27+
* Global: conductor.worker.all.<property>
28+
* Worker-specific: conductor.worker.<task_name>.<property>
2329
2430
Usage:
2531
export CONDUCTOR_SERVER_URL="http://localhost:8080/api"
@@ -85,7 +91,9 @@
8591
task_definition_name='calculate',
8692
thread_count=100, # High concurrency - async workers can handle it!
8793
poll_timeout=10,
88-
register_task_def=True
94+
register_task_def=True, # Auto-register task definition with JSON schema
95+
overwrite_task_def=True, # Always update definition on server (default: true)
96+
strict_schema=False # Allow additional properties in schema (default: false)
8997
)
9098
async def calculate_fibonacci(n: int) -> int:
9199
"""
@@ -101,6 +109,11 @@ async def calculate_fibonacci(n: int) -> int:
101109
- Concurrency: Up to 100 concurrent tasks
102110
- Memory: ~3-6 MB per process
103111
112+
Configuration:
113+
- register_task_def=True: Auto-registers task definition + JSON schema
114+
- overwrite_task_def=True: Always updates server (default behavior)
115+
- strict_schema=False: Allows extra fields in input (lenient, default)
116+
104117
Note: This is a CPU-bound task (fibonacci calculation), which isn't
105118
ideal for async workers. Use this pattern for I/O-bound operations
106119
(HTTP calls, database queries, file I/O).
@@ -161,17 +174,18 @@ class OrderRequest:
161174

162175

163176
# Create TaskDef with advanced configuration for the complex order worker
177+
# This demonstrates using task_def parameter to specify retry, timeout, and rate limiting
164178
complex_order_task_def = TaskDef(
165179
name='process_complex_order', # Will be overridden by task_definition_name
166180
description='Process customer orders with complex validation and retry logic',
167181
retry_count=3, # Retry up to 3 times on failure
168182
retry_logic='EXPONENTIAL_BACKOFF', # Use exponential backoff between retries
169183
retry_delay_seconds=10, # Start with 10 second delay
170-
backoff_scale_factor=3, # Double delay each retry (10s, 20s, 40s)
184+
backoff_scale_factor=3, # Triple delay each retry (10s, 30s, 90s)
171185
timeout_seconds=600, # Task must complete within 10 minutes
172186
response_timeout_seconds=120, # Each execution attempt has 2 minutes
173187
timeout_policy='RETRY', # Retry on timeout
174-
concurrent_exec_limit=30, # Max 5 concurrent executions
188+
concurrent_exec_limit=30, # Max 30 concurrent executions
175189
rate_limit_per_frequency=100, # Max 100 executions
176190
rate_limit_frequency_in_seconds=60, # Per 60 seconds
177191
poll_timeout_seconds=30 # Long poll timeout for efficiency
@@ -180,8 +194,13 @@ class OrderRequest:
180194
@worker_task(
181195
task_definition_name='process_complex_order',
182196
thread_count=10,
183-
register_task_def=True, # Will auto-generate and register JSON schema!
184-
task_def=complex_order_task_def # Advanced task configuration
197+
register_task_def=True, # Auto-register task definition with JSON schemas
198+
task_def=complex_order_task_def, # Advanced task configuration (retry, timeout, rate limits)
199+
overwrite_task_def=True, # Always update task definition on server (default: true)
200+
strict_schema=False # Lenient validation - allows extra fields (default: false)
201+
# Can be overridden via env:
202+
# export conductor.worker.process_complex_order.overwrite_task_def=false
203+
# export conductor.worker.process_complex_order.strict_schema=true
185204
)
186205
async def process_complex_order(
187206
order: OrderRequest,
@@ -203,19 +222,30 @@ async def process_complex_order(
203222
- Schema registered as: process_complex_order_input (v1)
204223
205224
2. ADVANCED TASK CONFIGURATION via task_def parameter:
206-
- Retry policy: 3 retries with EXPONENTIAL_BACKOFF (10s, 20s, 40s)
225+
- Retry policy: 3 retries with EXPONENTIAL_BACKOFF (10s, 30s, 90s)
207226
- Timeouts: 10 min total, 2 min per execution
208227
- Rate limiting: Max 100 executions per 60 seconds
209-
- Concurrency: Max 5 concurrent executions
228+
- Concurrency: Max 30 concurrent executions
210229
- All configured via TaskDef object passed to @worker_task
211230
231+
3. CONFIGURATION FLAGS:
232+
- overwrite_task_def=True: Always updates task definition on server
233+
* Use False to preserve manual server changes
234+
* Configurable: export conductor.worker.process_complex_order.overwrite_task_def=false
235+
236+
- strict_schema=False: Lenient JSON schema validation (allows extra fields)
237+
* Use True for strict validation (rejects extra fields)
238+
* Configurable: export conductor.worker.process_complex_order.strict_schema=true
239+
212240
Benefits:
213241
- Input validation in Conductor UI
214242
- Type-safe workflow design
215243
- Auto-completion in workflow editor
216244
- Runtime validation of task inputs
217245
- Production-ready retry and timeout policies
218246
- Rate limiting to protect downstream services
247+
- Flexible schema validation modes
248+
- Environment-based configuration control
219249
"""
220250
# Simulate order processing
221251
ctx = get_task_context()

0 commit comments

Comments
 (0)