forktex-flow 0.2.0__tar.gz → 0.2.1__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.
- {forktex_flow-0.2.0 → forktex_flow-0.2.1}/PKG-INFO +1 -1
- {forktex_flow-0.2.0 → forktex_flow-0.2.1}/pyproject.toml +1 -1
- {forktex_flow-0.2.0 → forktex_flow-0.2.1}/src/forktex_flow/persist/runs.py +37 -2
- {forktex_flow-0.2.0 → forktex_flow-0.2.1}/LICENSE +0 -0
- {forktex_flow-0.2.0 → forktex_flow-0.2.1}/NOTICE +0 -0
- {forktex_flow-0.2.0 → forktex_flow-0.2.1}/README.md +0 -0
- {forktex_flow-0.2.0 → forktex_flow-0.2.1}/src/forktex_flow/__init__.py +0 -0
- {forktex_flow-0.2.0 → forktex_flow-0.2.1}/src/forktex_flow/domain/__init__.py +0 -0
- {forktex_flow-0.2.0 → forktex_flow-0.2.1}/src/forktex_flow/domain/config.py +0 -0
- {forktex_flow-0.2.0 → forktex_flow-0.2.1}/src/forktex_flow/domain/definition.py +0 -0
- {forktex_flow-0.2.0 → forktex_flow-0.2.1}/src/forktex_flow/domain/filter.py +0 -0
- {forktex_flow-0.2.0 → forktex_flow-0.2.1}/src/forktex_flow/domain/node.py +0 -0
- {forktex_flow-0.2.0 → forktex_flow-0.2.1}/src/forktex_flow/domain/state.py +0 -0
- {forktex_flow-0.2.0 → forktex_flow-0.2.1}/src/forktex_flow/domain/types.py +0 -0
- {forktex_flow-0.2.0 → forktex_flow-0.2.1}/src/forktex_flow/errors.py +0 -0
- {forktex_flow-0.2.0 → forktex_flow-0.2.1}/src/forktex_flow/extension.py +0 -0
- {forktex_flow-0.2.0 → forktex_flow-0.2.1}/src/forktex_flow/flow.py +0 -0
- {forktex_flow-0.2.0 → forktex_flow-0.2.1}/src/forktex_flow/persist/__init__.py +0 -0
- {forktex_flow-0.2.0 → forktex_flow-0.2.1}/src/forktex_flow/persist/mappers.py +0 -0
- {forktex_flow-0.2.0 → forktex_flow-0.2.1}/src/forktex_flow/persist/migrations/__init__.py +0 -0
- {forktex_flow-0.2.0 → forktex_flow-0.2.1}/src/forktex_flow/persist/migrations/v0001__schema.sql +0 -0
- {forktex_flow-0.2.0 → forktex_flow-0.2.1}/src/forktex_flow/persist/models.py +0 -0
- {forktex_flow-0.2.0 → forktex_flow-0.2.1}/src/forktex_flow/persist/steps.py +0 -0
- {forktex_flow-0.2.0 → forktex_flow-0.2.1}/src/forktex_flow/persist/store.py +0 -0
- {forktex_flow-0.2.0 → forktex_flow-0.2.1}/src/forktex_flow/py.typed +0 -0
- {forktex_flow-0.2.0 → forktex_flow-0.2.1}/src/forktex_flow/read/__init__.py +0 -0
- {forktex_flow-0.2.0 → forktex_flow-0.2.1}/src/forktex_flow/read/instance.py +0 -0
- {forktex_flow-0.2.0 → forktex_flow-0.2.1}/src/forktex_flow/read/query.py +0 -0
- {forktex_flow-0.2.0 → forktex_flow-0.2.1}/src/forktex_flow/runtime/__init__.py +0 -0
- {forktex_flow-0.2.0 → forktex_flow-0.2.1}/src/forktex_flow/runtime/compiler.py +0 -0
- {forktex_flow-0.2.0 → forktex_flow-0.2.1}/src/forktex_flow/runtime/context.py +0 -0
- {forktex_flow-0.2.0 → forktex_flow-0.2.1}/src/forktex_flow/runtime/ctx.py +0 -0
- {forktex_flow-0.2.0 → forktex_flow-0.2.1}/src/forktex_flow/runtime/driver.py +0 -0
- {forktex_flow-0.2.0 → forktex_flow-0.2.1}/src/forktex_flow/runtime/execution.py +0 -0
- {forktex_flow-0.2.0 → forktex_flow-0.2.1}/src/forktex_flow/runtime/registry.py +0 -0
- {forktex_flow-0.2.0 → forktex_flow-0.2.1}/src/forktex_flow/testing.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: forktex-flow
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.1
|
|
4
4
|
Summary: Postgres-native durable workflow execution — pipelines and cron-scheduled runs, built on the forktex substrate.
|
|
5
5
|
License-Expression: AGPL-3.0-or-later OR LicenseRef-ForkTex-Commercial
|
|
6
6
|
License-File: LICENSE
|
|
@@ -23,7 +23,7 @@
|
|
|
23
23
|
|
|
24
24
|
[project]
|
|
25
25
|
name = "forktex-flow"
|
|
26
|
-
version = "0.2.
|
|
26
|
+
version = "0.2.1"
|
|
27
27
|
description = "Postgres-native durable workflow execution — pipelines and cron-scheduled runs, built on the forktex substrate."
|
|
28
28
|
authors = [
|
|
29
29
|
{name = "FORKTEX",email = "info@forktex.com"}
|
|
@@ -43,7 +43,7 @@ from sqlalchemy.orm import selectinload
|
|
|
43
43
|
|
|
44
44
|
from forktex_flow.domain.types import TERMINAL_STATUSES, RunInfo, RunStatus
|
|
45
45
|
from forktex_flow.persist.mappers import to_run_info
|
|
46
|
-
from forktex_flow.persist.models import Run, RunEvent
|
|
46
|
+
from forktex_flow.persist.models import Run, RunEvent, StepRun
|
|
47
47
|
|
|
48
48
|
if TYPE_CHECKING:
|
|
49
49
|
from sqlalchemy.ext.asyncio import AsyncConnection
|
|
@@ -210,10 +210,45 @@ async def claim_pending_runs(store: FlowStore, limit: int) -> list[tuple[UUID, s
|
|
|
210
210
|
instead of racing over it: ``with_for_update(skip_locked=True)`` on a scalar
|
|
211
211
|
subquery renders the locking clause *inside* the subselect, which is where
|
|
212
212
|
Postgres needs it for the atomicity to hold.
|
|
213
|
+
|
|
214
|
+
**Runs waiting on a retry backoff are skipped.** A step that asks to be
|
|
215
|
+
retried sets ``next_attempt_at`` and the run goes straight back to
|
|
216
|
+
``pending`` (``execution.py``, ``StepRetryRequested``). Claiming on status
|
|
217
|
+
alone therefore re-dispatched it on the very next driver tick — a second
|
|
218
|
+
later — and the backoff a step declared was never waited. `@step(backoff=(15,
|
|
219
|
+
30, 60), max_attempts=720)` reads as hours of patience and delivered twelve
|
|
220
|
+
minutes of busy-looping against whatever the step was polling.
|
|
221
|
+
|
|
222
|
+
A run is skipped only while it is *actually* blocked: it has a pending step
|
|
223
|
+
scheduled in the future and no pending step that is due. A fresh run with no
|
|
224
|
+
step rows, and a resumed run whose next step has not been created yet, both
|
|
225
|
+
still claim — the guard must not turn a backoff into a stall.
|
|
213
226
|
"""
|
|
227
|
+
blocked_until_later = (
|
|
228
|
+
select(StepRun.id)
|
|
229
|
+
.where(
|
|
230
|
+
StepRun.run_id == Run.id,
|
|
231
|
+
StepRun.status == "pending",
|
|
232
|
+
StepRun.next_attempt_at.isnot(None),
|
|
233
|
+
StepRun.next_attempt_at > sa.func.now(),
|
|
234
|
+
)
|
|
235
|
+
.exists()
|
|
236
|
+
)
|
|
237
|
+
ready_now = (
|
|
238
|
+
select(StepRun.id)
|
|
239
|
+
.where(
|
|
240
|
+
StepRun.run_id == Run.id,
|
|
241
|
+
StepRun.status == "pending",
|
|
242
|
+
sa.or_(
|
|
243
|
+
StepRun.next_attempt_at.is_(None),
|
|
244
|
+
StepRun.next_attempt_at <= sa.func.now(),
|
|
245
|
+
),
|
|
246
|
+
)
|
|
247
|
+
.exists()
|
|
248
|
+
)
|
|
214
249
|
claimable = (
|
|
215
250
|
select(Run.id)
|
|
216
|
-
.where(Run.status == "pending")
|
|
251
|
+
.where(Run.status == "pending", sa.or_(~blocked_until_later, ready_now))
|
|
217
252
|
.order_by(Run.started_at.asc())
|
|
218
253
|
.limit(limit)
|
|
219
254
|
.with_for_update(skip_locked=True)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{forktex_flow-0.2.0 → forktex_flow-0.2.1}/src/forktex_flow/persist/migrations/v0001__schema.sql
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|