|
| 1 | +# Version Override Feature - Complete Summary |
| 2 | + |
| 3 | +## Overview |
| 4 | + |
| 5 | +Added comprehensive version override capabilities to Azure Durable Functions Python SDK: |
| 6 | + |
| 7 | +1. **Orchestration Start**: Pass version when starting orchestrations via `client.start_new()` |
| 8 | +2. **Sub-Orchestrators**: Pass version when calling sub-orchestrators via `context.call_sub_orchestrator()` |
| 9 | +3. **Sub-Orchestrators with Retry**: Pass version when calling sub-orchestrators with retry |
| 10 | + |
| 11 | +## Complete Changes |
| 12 | + |
| 13 | +### Core SDK Changes |
| 14 | + |
| 15 | +#### 1. DurableOrchestrationClient.py |
| 16 | +**Location**: `azure/durable_functions/models/DurableOrchestrationClient.py` |
| 17 | + |
| 18 | +**`start_new` method:** |
| 19 | +- Added optional `version: Optional[str] = None` parameter |
| 20 | +- Updated docstring to document version override behavior |
| 21 | +- Passes version to `_get_start_new_url()` helper |
| 22 | + |
| 23 | +**`_get_start_new_url` helper:** |
| 24 | +- Added optional `version: Optional[str] = None` parameter |
| 25 | +- Appends version as query parameter: `?version={version}` when provided |
| 26 | + |
| 27 | +#### 2. CallSubOrchestratorAction.py |
| 28 | +**Location**: `azure/durable_functions/models/actions/CallSubOrchestratorAction.py` |
| 29 | + |
| 30 | +- Added optional `version: Optional[str] = None` parameter to `__init__` |
| 31 | +- Stored `self.version` as instance attribute |
| 32 | +- Added version serialization in `to_json()` method: `add_attrib(json_dict, self, 'version', 'version')` |
| 33 | + |
| 34 | +#### 3. CallSubOrchestratorWithRetryAction.py |
| 35 | +**Location**: `azure/durable_functions/models/actions/CallSubOrchestratorWithRetryAction.py` |
| 36 | + |
| 37 | +- Added optional `version: Optional[str] = None` parameter to `__init__` |
| 38 | +- Stored `self.version` as instance attribute |
| 39 | +- Added version serialization in `to_json()` method: `add_attrib(json_dict, self, 'version', 'version')` |
| 40 | + |
| 41 | +#### 4. DurableOrchestrationContext.py |
| 42 | +**Location**: `azure/durable_functions/models/DurableOrchestrationContext.py` |
| 43 | + |
| 44 | +**`call_sub_orchestrator` method:** |
| 45 | +- Added optional `version: Optional[str] = None` parameter |
| 46 | +- Updated docstring with version documentation |
| 47 | +- Passes version to `CallSubOrchestratorAction(name, input_, instance_id, version)` |
| 48 | + |
| 49 | +**`call_sub_orchestrator_with_retry` method:** |
| 50 | +- Added optional `version: Optional[str] = None` parameter |
| 51 | +- Updated docstring with version documentation |
| 52 | +- Passes version to `CallSubOrchestratorWithRetryAction(name, retry_options, input_, instance_id, version)` |
| 53 | + |
| 54 | +### Sample Updates |
| 55 | + |
| 56 | +#### 5. Sample Function App |
| 57 | +**Location**: `samples-v2/orchestration_versioning/function_app.py` |
| 58 | + |
| 59 | +**`http_start` function:** |
| 60 | +- Reads version from query parameter: `version = req.params.get('version')` |
| 61 | +- Conditionally passes version to `client.start_new()` |
| 62 | +- Logs whether version was explicitly provided |
| 63 | + |
| 64 | +**`my_orchestrator` function:** |
| 65 | +- Demonstrates calling sub-orchestrator with explicit version: `version="1.5"` |
| 66 | +- Demonstrates calling sub-orchestrator without version (uses default) |
| 67 | +- Returns both results to show version difference |
| 68 | + |
| 69 | +#### 6. Sample README |
| 70 | +**Location**: `samples-v2/orchestration_versioning/README.md` |
| 71 | + |
| 72 | +Added comprehensive documentation: |
| 73 | +- "Overriding Version Programmatically" section for `client.start_new()` |
| 74 | +- "Passing Version to Sub-Orchestrators" section |
| 75 | +- Code examples for all use cases |
| 76 | +- Explanation of benefits and use cases |
| 77 | + |
| 78 | +### Testing |
| 79 | + |
| 80 | +#### 7. Unit Tests |
| 81 | +**Location**: `tests/models/test_DurableOrchestrationClient.py` |
| 82 | + |
| 83 | +Added `test_get_start_new_url_with_version`: |
| 84 | +- Verifies URL construction with version parameter |
| 85 | +- Validates format: `{base_url}orchestrators/{functionName}/{instanceId}?version={version}` |
| 86 | + |
| 87 | +## Usage Examples |
| 88 | + |
| 89 | +### 1. Starting Orchestration with Version |
| 90 | + |
| 91 | +```python |
| 92 | +# Via client.start_new() |
| 93 | +instance_id = await client.start_new("my_orchestrator", version="2.0") |
| 94 | + |
| 95 | +# Via HTTP query parameter |
| 96 | +# GET /api/orchestrators/my_orchestrator?version=2.0 |
| 97 | +``` |
| 98 | + |
| 99 | +### 2. Calling Sub-Orchestrator with Version |
| 100 | + |
| 101 | +```python |
| 102 | +# Without retry |
| 103 | +sub_result = yield context.call_sub_orchestrator( |
| 104 | + 'my_sub_orchestrator', |
| 105 | + input_={'key': 'value'}, |
| 106 | + version="1.5" |
| 107 | +) |
| 108 | + |
| 109 | +# With retry |
| 110 | +sub_result = yield context.call_sub_orchestrator_with_retry( |
| 111 | + 'my_sub_orchestrator', |
| 112 | + retry_options=retry_options, |
| 113 | + input_={'key': 'value'}, |
| 114 | + version="2.0" |
| 115 | +) |
| 116 | +``` |
| 117 | + |
| 118 | +### 3. Mixed Version Orchestration |
| 119 | + |
| 120 | +```python |
| 121 | +@myApp.orchestration_trigger(context_name="context") |
| 122 | +def my_orchestrator(context: df.DurableOrchestrationContext): |
| 123 | + # Parent uses version from client.start_new() or defaultVersion |
| 124 | + |
| 125 | + # Sub-orchestrator with explicit version |
| 126 | + sub_v1 = yield context.call_sub_orchestrator('processor', version="1.0") |
| 127 | + |
| 128 | + # Sub-orchestrator with different explicit version |
| 129 | + sub_v2 = yield context.call_sub_orchestrator('processor', version="2.0") |
| 130 | + |
| 131 | + # Sub-orchestrator using default version |
| 132 | + sub_default = yield context.call_sub_orchestrator('processor') |
| 133 | + |
| 134 | + return [sub_v1, sub_v2, sub_default] |
| 135 | +``` |
| 136 | + |
| 137 | +## Benefits |
| 138 | + |
| 139 | +### Testing |
| 140 | +- Test new versions without modifying `host.json` |
| 141 | +- Run integration tests with specific versions |
| 142 | +- Verify version-specific behavior |
| 143 | + |
| 144 | +### Gradual Rollout |
| 145 | +- Control which requests use which version |
| 146 | +- Perform A/B testing between versions |
| 147 | +- Gradually migrate traffic to new versions |
| 148 | + |
| 149 | +### Multi-Version Support |
| 150 | +- Run multiple orchestration versions simultaneously |
| 151 | +- Support different clients with different version requirements |
| 152 | +- Maintain backward compatibility during transitions |
| 153 | + |
| 154 | +### Sub-Orchestrator Flexibility |
| 155 | +- Mix different sub-orchestrator versions in one parent |
| 156 | +- Incrementally upgrade sub-orchestrators |
| 157 | +- Test new sub-orchestrator versions in production |
| 158 | + |
| 159 | +## Backward Compatibility |
| 160 | + |
| 161 | +All changes are fully backward compatible: |
| 162 | + |
| 163 | +✅ **Optional Parameters**: All `version` parameters are optional |
| 164 | +✅ **Default Behavior**: When not specified, uses `defaultVersion` from `host.json` |
| 165 | +✅ **Existing Code**: All existing code continues to work without modification |
| 166 | +✅ **API Stability**: No breaking changes to existing method signatures |
| 167 | + |
| 168 | +## Version Precedence |
| 169 | + |
| 170 | +The version resolution order is: |
| 171 | + |
| 172 | +1. **Explicit version parameter** (highest priority) |
| 173 | + - Passed to `client.start_new()`, `call_sub_orchestrator()`, etc. |
| 174 | +2. **defaultVersion in host.json** (fallback) |
| 175 | + - Used when no explicit version is provided |
| 176 | + |
| 177 | +## Architecture Flow |
| 178 | + |
| 179 | +``` |
| 180 | +HTTP Request with ?version=2.0 |
| 181 | + ↓ |
| 182 | +http_start function |
| 183 | + ↓ |
| 184 | +client.start_new("my_orchestrator", version="2.0") |
| 185 | + ↓ |
| 186 | +DurableOrchestrationClient._get_start_new_url() |
| 187 | + ↓ |
| 188 | +POST to: orchestrators/my_orchestrator?version=2.0 |
| 189 | + ↓ |
| 190 | +Durable Functions Extension assigns version="2.0" |
| 191 | + ↓ |
| 192 | +context.version == "2.0" in orchestrator |
| 193 | + ↓ |
| 194 | +context.call_sub_orchestrator('sub', version="1.5") |
| 195 | + ↓ |
| 196 | +CallSubOrchestratorAction with version="1.5" |
| 197 | + ↓ |
| 198 | +Sub-orchestrator runs with version="1.5" |
| 199 | +``` |
0 commit comments