dex-python-sdk 1.14.1__tar.gz → 1.15.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {dex_python_sdk-1.14.1 → dex_python_sdk-1.15.0}/PKG-INFO +35 -1
- {dex_python_sdk-1.14.1 → dex_python_sdk-1.15.0}/README.md +34 -0
- {dex_python_sdk-1.14.1 → dex_python_sdk-1.15.0}/dex/_pull_worker.py +28 -2
- {dex_python_sdk-1.14.1 → dex_python_sdk-1.15.0}/dex/async_worker.py +17 -0
- dex_python_sdk-1.15.0/dex/dexpb/dex_pb2.py +549 -0
- {dex_python_sdk-1.14.1 → dex_python_sdk-1.15.0}/dex/dexpb/dex_pb2.pyi +73 -26
- {dex_python_sdk-1.14.1 → dex_python_sdk-1.15.0}/dex/worker.py +17 -0
- {dex_python_sdk-1.14.1 → dex_python_sdk-1.15.0}/dex/worker_options.py +29 -2
- {dex_python_sdk-1.14.1 → dex_python_sdk-1.15.0}/pyproject.toml +1 -1
- {dex_python_sdk-1.14.1 → dex_python_sdk-1.15.0}/sdk-rust/Cargo.lock +1 -0
- {dex_python_sdk-1.14.1 → dex_python_sdk-1.15.0}/sdk-rust/Cargo.toml +1 -0
- dex_python_sdk-1.14.1/dex/dexpb/dex_pb2.py +0 -545
- {dex_python_sdk-1.14.1 → dex_python_sdk-1.15.0}/LEGACY_NOTICES.md +0 -0
- {dex_python_sdk-1.14.1 → dex_python_sdk-1.15.0}/LICENSE +0 -0
- {dex_python_sdk-1.14.1 → dex_python_sdk-1.15.0}/dex/__init__.py +0 -0
- {dex_python_sdk-1.14.1 → dex_python_sdk-1.15.0}/dex/_async_value_hydrator.py +0 -0
- {dex_python_sdk-1.14.1 → dex_python_sdk-1.15.0}/dex/_async_worker_dispatcher.py +0 -0
- {dex_python_sdk-1.14.1 → dex_python_sdk-1.15.0}/dex/_async_worker_service.py +0 -0
- {dex_python_sdk-1.14.1 → dex_python_sdk-1.15.0}/dex/_credential_transport.py +0 -0
- {dex_python_sdk-1.14.1 → dex_python_sdk-1.15.0}/dex/_grpc_errors.py +0 -0
- {dex_python_sdk-1.14.1 → dex_python_sdk-1.15.0}/dex/_invocation_context.py +0 -0
- {dex_python_sdk-1.14.1 → dex_python_sdk-1.15.0}/dex/_native.pyi +0 -0
- {dex_python_sdk-1.14.1 → dex_python_sdk-1.15.0}/dex/_server_protocol.py +0 -0
- {dex_python_sdk-1.14.1 → dex_python_sdk-1.15.0}/dex/_utils.py +0 -0
- {dex_python_sdk-1.14.1 → dex_python_sdk-1.15.0}/dex/_value_hydrator.py +0 -0
- {dex_python_sdk-1.14.1 → dex_python_sdk-1.15.0}/dex/_value_mapper.py +0 -0
- {dex_python_sdk-1.14.1 → dex_python_sdk-1.15.0}/dex/_worker_dispatcher.py +0 -0
- {dex_python_sdk-1.14.1 → dex_python_sdk-1.15.0}/dex/_worker_service.py +0 -0
- {dex_python_sdk-1.14.1 → dex_python_sdk-1.15.0}/dex/async_client.py +0 -0
- {dex_python_sdk-1.14.1 → dex_python_sdk-1.15.0}/dex/attribute.py +0 -0
- {dex_python_sdk-1.14.1 → dex_python_sdk-1.15.0}/dex/attribute_match.py +0 -0
- {dex_python_sdk-1.14.1 → dex_python_sdk-1.15.0}/dex/blob_cache.py +0 -0
- {dex_python_sdk-1.14.1 → dex_python_sdk-1.15.0}/dex/channel.py +0 -0
- {dex_python_sdk-1.14.1 → dex_python_sdk-1.15.0}/dex/client.py +0 -0
- {dex_python_sdk-1.14.1 → dex_python_sdk-1.15.0}/dex/client_options.py +0 -0
- {dex_python_sdk-1.14.1 → dex_python_sdk-1.15.0}/dex/codec.py +0 -0
- {dex_python_sdk-1.14.1 → dex_python_sdk-1.15.0}/dex/condition.py +0 -0
- {dex_python_sdk-1.14.1 → dex_python_sdk-1.15.0}/dex/context.py +0 -0
- {dex_python_sdk-1.14.1 → dex_python_sdk-1.15.0}/dex/credential.py +0 -0
- {dex_python_sdk-1.14.1 → dex_python_sdk-1.15.0}/dex/dexpb/__init__.py +0 -0
- {dex_python_sdk-1.14.1 → dex_python_sdk-1.15.0}/dex/dexpb/dex_pb2_grpc.py +0 -0
- {dex_python_sdk-1.14.1 → dex_python_sdk-1.15.0}/dex/flow.py +0 -0
- {dex_python_sdk-1.14.1 → dex_python_sdk-1.15.0}/dex/flow_config.py +0 -0
- {dex_python_sdk-1.14.1 → dex_python_sdk-1.15.0}/dex/flow_info.py +0 -0
- {dex_python_sdk-1.14.1 → dex_python_sdk-1.15.0}/dex/flow_options.py +0 -0
- {dex_python_sdk-1.14.1 → dex_python_sdk-1.15.0}/dex/flow_result.py +0 -0
- {dex_python_sdk-1.14.1 → dex_python_sdk-1.15.0}/dex/pull_registration.py +0 -0
- {dex_python_sdk-1.14.1 → dex_python_sdk-1.15.0}/dex/py.typed +0 -0
- {dex_python_sdk-1.14.1 → dex_python_sdk-1.15.0}/dex/rpc_invoke_options.py +0 -0
- {dex_python_sdk-1.14.1 → dex_python_sdk-1.15.0}/dex/runtime_errors.py +0 -0
- {dex_python_sdk-1.14.1 → dex_python_sdk-1.15.0}/dex/step.py +0 -0
- {dex_python_sdk-1.14.1 → dex_python_sdk-1.15.0}/dex/step_execution.py +0 -0
- {dex_python_sdk-1.14.1 → dex_python_sdk-1.15.0}/dex/stream.py +0 -0
- {dex_python_sdk-1.14.1 → dex_python_sdk-1.15.0}/dex/subflow.py +0 -0
- {dex_python_sdk-1.14.1 → dex_python_sdk-1.15.0}/dex/timer.py +0 -0
- {dex_python_sdk-1.14.1 → dex_python_sdk-1.15.0}/dex/wait.py +0 -0
- {dex_python_sdk-1.14.1 → dex_python_sdk-1.15.0}/dex/wait_options.py +0 -0
- {dex_python_sdk-1.14.1 → dex_python_sdk-1.15.0}/sdk-rust/crates/dex-blob-cache/Cargo.toml +0 -0
- {dex_python_sdk-1.14.1 → dex_python_sdk-1.15.0}/sdk-rust/crates/dex-blob-cache/LICENSE +0 -0
- {dex_python_sdk-1.14.1 → dex_python_sdk-1.15.0}/sdk-rust/crates/dex-blob-cache/src/config.rs +0 -0
- {dex_python_sdk-1.14.1 → dex_python_sdk-1.15.0}/sdk-rust/crates/dex-blob-cache/src/entry.rs +0 -0
- {dex_python_sdk-1.14.1 → dex_python_sdk-1.15.0}/sdk-rust/crates/dex-blob-cache/src/error.rs +0 -0
- {dex_python_sdk-1.14.1 → dex_python_sdk-1.15.0}/sdk-rust/crates/dex-blob-cache/src/format.rs +0 -0
- {dex_python_sdk-1.14.1 → dex_python_sdk-1.15.0}/sdk-rust/crates/dex-blob-cache/src/lib.rs +0 -0
- {dex_python_sdk-1.14.1 → dex_python_sdk-1.15.0}/sdk-rust/crates/dex-blob-cache/src/policy.rs +0 -0
- {dex_python_sdk-1.14.1 → dex_python_sdk-1.15.0}/sdk-rust/crates/dex-blob-cache/src/store.rs +0 -0
- {dex_python_sdk-1.14.1 → dex_python_sdk-1.15.0}/sdk-rust/crates/dex-blob-cache/tests/blob_cache_integration.rs +0 -0
- {dex_python_sdk-1.14.1 → dex_python_sdk-1.15.0}/sdk-rust/crates/dex-blob-cache-python/Cargo.toml +0 -0
- {dex_python_sdk-1.14.1 → dex_python_sdk-1.15.0}/sdk-rust/crates/dex-blob-cache-python/LICENSE +0 -0
- {dex_python_sdk-1.14.1 → dex_python_sdk-1.15.0}/sdk-rust/crates/dex-blob-cache-python/src/lib.rs +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: dex-python-sdk
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.15.0
|
|
4
4
|
Requires-Dist: grpcio>=1.83.0
|
|
5
5
|
Requires-Dist: grpcio-status>=1.83.0
|
|
6
6
|
Requires-Dist: protobuf>=7.35.1
|
|
@@ -157,6 +157,40 @@ applies before and after readiness. Another registered stream for that queue
|
|
|
157
157
|
prevents shutdown. The default is False and retries indefinitely. Always call
|
|
158
158
|
`stop()` or `close()` afterward; both remain safe after `start()` returns.
|
|
159
159
|
|
|
160
|
+
### Reserved RPC capacity
|
|
161
|
+
|
|
162
|
+
Set `reserved_rpc_tasks` when synchronous RPC latency must stay responsive
|
|
163
|
+
behind long-running Steps. When unset, it reserves one slot if the resolved task
|
|
164
|
+
limit is at least two, otherwise zero. With the CPU-count default, Steps get one
|
|
165
|
+
fewer slot than before. Set it explicitly to zero to opt out. With one slot and
|
|
166
|
+
no explicit reserve, Worker start logs one warning: RPCs may wait behind
|
|
167
|
+
long-running Steps; use a limit of at least two or set the reserve explicitly.
|
|
168
|
+
Flows must stay responsive; Dex cannot prioritize synchronous RPCs like workflow
|
|
169
|
+
engines prioritize reads. The value must be nonnegative and less than
|
|
170
|
+
`max_concurrent_tasks` after its CPU-count default is resolved; invalid values
|
|
171
|
+
fail construction. RPC and Step handlers count together: WaitFor and Execute
|
|
172
|
+
tasks, including local attempts, are declined with `NO_CAPACITY` when the busy
|
|
173
|
+
count reaches the task limit minus the reserve. API-direct and transactional
|
|
174
|
+
InvokeWorkerRPC tasks can use the full limit.
|
|
175
|
+
|
|
176
|
+
```python
|
|
177
|
+
pull = PullWorkerOptions(max_concurrent_tasks=8, reserved_rpc_tasks=2)
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
A decline lets Dex try another Worker without consuming an attempt. If all
|
|
181
|
+
Workers remain at their Step limit, a Step can wait until the dispatch wait
|
|
182
|
+
expires, then receive `NO_WORKER_AVAILABLE`, consume an attempt, and follow its
|
|
183
|
+
retry policy, as when all Workers are full. Local attempts wait the local
|
|
184
|
+
dispatch wait, then fall back according to their policy. Each queue still keeps
|
|
185
|
+
its configured idle streams; a reserve decline does not close a stream or change
|
|
186
|
+
registration readiness.
|
|
187
|
+
|
|
188
|
+
Each reserve decline records the Flow type through the dex._pull_worker logger
|
|
189
|
+
at DEBUG level. Export the cumulative `pull_reserved_rpc_declines` Worker value
|
|
190
|
+
to distinguish reserve pressure from full-capacity rejections. The count
|
|
191
|
+
excludes full-capacity rejections and unknown names, never resets, and remains
|
|
192
|
+
available after shutdown. Listening Workers return zero.
|
|
193
|
+
|
|
160
194
|
## Flow state I/O
|
|
161
195
|
|
|
162
196
|
Applications read and write Flow state through typed RPCs. Client and AsyncClient
|
|
@@ -141,6 +141,40 @@ applies before and after readiness. Another registered stream for that queue
|
|
|
141
141
|
prevents shutdown. The default is False and retries indefinitely. Always call
|
|
142
142
|
`stop()` or `close()` afterward; both remain safe after `start()` returns.
|
|
143
143
|
|
|
144
|
+
### Reserved RPC capacity
|
|
145
|
+
|
|
146
|
+
Set `reserved_rpc_tasks` when synchronous RPC latency must stay responsive
|
|
147
|
+
behind long-running Steps. When unset, it reserves one slot if the resolved task
|
|
148
|
+
limit is at least two, otherwise zero. With the CPU-count default, Steps get one
|
|
149
|
+
fewer slot than before. Set it explicitly to zero to opt out. With one slot and
|
|
150
|
+
no explicit reserve, Worker start logs one warning: RPCs may wait behind
|
|
151
|
+
long-running Steps; use a limit of at least two or set the reserve explicitly.
|
|
152
|
+
Flows must stay responsive; Dex cannot prioritize synchronous RPCs like workflow
|
|
153
|
+
engines prioritize reads. The value must be nonnegative and less than
|
|
154
|
+
`max_concurrent_tasks` after its CPU-count default is resolved; invalid values
|
|
155
|
+
fail construction. RPC and Step handlers count together: WaitFor and Execute
|
|
156
|
+
tasks, including local attempts, are declined with `NO_CAPACITY` when the busy
|
|
157
|
+
count reaches the task limit minus the reserve. API-direct and transactional
|
|
158
|
+
InvokeWorkerRPC tasks can use the full limit.
|
|
159
|
+
|
|
160
|
+
```python
|
|
161
|
+
pull = PullWorkerOptions(max_concurrent_tasks=8, reserved_rpc_tasks=2)
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
A decline lets Dex try another Worker without consuming an attempt. If all
|
|
165
|
+
Workers remain at their Step limit, a Step can wait until the dispatch wait
|
|
166
|
+
expires, then receive `NO_WORKER_AVAILABLE`, consume an attempt, and follow its
|
|
167
|
+
retry policy, as when all Workers are full. Local attempts wait the local
|
|
168
|
+
dispatch wait, then fall back according to their policy. Each queue still keeps
|
|
169
|
+
its configured idle streams; a reserve decline does not close a stream or change
|
|
170
|
+
registration readiness.
|
|
171
|
+
|
|
172
|
+
Each reserve decline records the Flow type through the dex._pull_worker logger
|
|
173
|
+
at DEBUG level. Export the cumulative `pull_reserved_rpc_declines` Worker value
|
|
174
|
+
to distinguish reserve pressure from full-capacity rejections. The count
|
|
175
|
+
excludes full-capacity rejections and unknown names, never resets, and remains
|
|
176
|
+
available after shutdown. Listening Workers return zero.
|
|
177
|
+
|
|
144
178
|
## Flow state I/O
|
|
145
179
|
|
|
146
180
|
Applications read and write Flow state through typed RPCs. Client and AsyncClient
|
|
@@ -94,6 +94,15 @@ class PullWorkerRuntime:
|
|
|
94
94
|
self._permanent_error: PullRegistrationError | None = None
|
|
95
95
|
self._rejection_stop_task: asyncio.Task[None] | None = None
|
|
96
96
|
self.max_concurrent_tasks = options.max_concurrent_tasks or os.cpu_count() or 1
|
|
97
|
+
self.reserved_rpc_tasks = (
|
|
98
|
+
options.reserved_rpc_tasks
|
|
99
|
+
if options.reserved_rpc_tasks is not None
|
|
100
|
+
else int(self.max_concurrent_tasks >= 2)
|
|
101
|
+
)
|
|
102
|
+
self._should_warn_rpc_capacity = (
|
|
103
|
+
options.reserved_rpc_tasks is None and self.max_concurrent_tasks == 1
|
|
104
|
+
)
|
|
105
|
+
self.reserved_rpc_declines = 0
|
|
97
106
|
self.idle_streams_per_queue = options.idle_streams_per_queue or 1
|
|
98
107
|
self.stop_drain_timeout = options.stop_drain_timeout.total_seconds() or 30
|
|
99
108
|
self._dispatcher = dispatcher
|
|
@@ -132,6 +141,10 @@ class PullWorkerRuntime:
|
|
|
132
141
|
return None
|
|
133
142
|
|
|
134
143
|
async def run(self) -> None:
|
|
144
|
+
if self._should_warn_rpc_capacity:
|
|
145
|
+
_LOGGER.warning(
|
|
146
|
+
"RPCs may wait behind long-running Steps; use max_concurrent_tasks >= 2 or set reserved_rpc_tasks explicitly"
|
|
147
|
+
)
|
|
135
148
|
self._loop = asyncio.get_running_loop()
|
|
136
149
|
self._loop_thread_id = threading.get_ident()
|
|
137
150
|
self._wake = asyncio.Event()
|
|
@@ -193,11 +206,24 @@ class PullWorkerRuntime:
|
|
|
193
206
|
_LOGGER.error("Worker task stream failed", exc_info=failure)
|
|
194
207
|
self.signal()
|
|
195
208
|
|
|
196
|
-
def accept_task(self) -> bool:
|
|
209
|
+
def accept_task(self, task: pb.PollWorkerTask | None = None) -> bool:
|
|
197
210
|
if self._stopping is None or self._stopping.is_set():
|
|
198
211
|
return False
|
|
199
212
|
if self._busy_tasks >= self.max_concurrent_tasks:
|
|
200
213
|
return False
|
|
214
|
+
if (
|
|
215
|
+
task is not None
|
|
216
|
+
and task.WhichOneof("request") in ("wait_for", "execute")
|
|
217
|
+
and self.reserved_rpc_tasks > 0
|
|
218
|
+
and self._busy_tasks >= self.max_concurrent_tasks - self.reserved_rpc_tasks
|
|
219
|
+
and self.unknown_task_reason(task) is None
|
|
220
|
+
):
|
|
221
|
+
self.reserved_rpc_declines += 1
|
|
222
|
+
request = task.wait_for if task.HasField("wait_for") else task.execute
|
|
223
|
+
_LOGGER.debug(
|
|
224
|
+
"decline Step task for reserved RPC capacity: %s", request.flow_type
|
|
225
|
+
)
|
|
226
|
+
return False
|
|
201
227
|
self._busy_tasks += 1
|
|
202
228
|
self.signal()
|
|
203
229
|
return True
|
|
@@ -524,7 +550,7 @@ class PullStream:
|
|
|
524
550
|
if unknown_reason is not None:
|
|
525
551
|
await self._reject_task(task, unknown_reason)
|
|
526
552
|
return
|
|
527
|
-
if self.is_busy or self.is_draining or not self.runtime.accept_task():
|
|
553
|
+
if self.is_busy or self.is_draining or not self.runtime.accept_task(task):
|
|
528
554
|
await self._reject_task(task)
|
|
529
555
|
return
|
|
530
556
|
self.is_busy = True
|
|
@@ -221,6 +221,23 @@ class AsyncWorker:
|
|
|
221
221
|
raise RuntimeError("wait_pull_registered requires a pull Worker")
|
|
222
222
|
await self._pull.registration.wait_async(timeout)
|
|
223
223
|
|
|
224
|
+
@property
|
|
225
|
+
def pull_reserved_rpc_declines(self) -> int:
|
|
226
|
+
"""Return cumulative Step declines caused by reserved RPC capacity.
|
|
227
|
+
|
|
228
|
+
Full-capacity rejections and unknown names are excluded. The count never
|
|
229
|
+
resets during this Worker's lifetime and stays available after stop.
|
|
230
|
+
Reading it alongside handlers is safe. Listening Workers return zero.
|
|
231
|
+
Export this value to a monitoring counter to identify reserve pressure.
|
|
232
|
+
|
|
233
|
+
Returns:
|
|
234
|
+
The cumulative reserve-decline count, or zero for a listening Worker.
|
|
235
|
+
|
|
236
|
+
Examples:
|
|
237
|
+
>>> declines = worker.pull_reserved_rpc_declines
|
|
238
|
+
"""
|
|
239
|
+
return 0 if self._pull is None else self._pull.reserved_rpc_declines
|
|
240
|
+
|
|
224
241
|
def pull_registration_state(self) -> PullRegistrationState:
|
|
225
242
|
"""Return an immutable snapshot of pull queue registrations.
|
|
226
243
|
|