flowstash-runtime 0.8.2__tar.gz → 0.8.3__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.
Files changed (25) hide show
  1. {flowstash_runtime-0.8.2 → flowstash_runtime-0.8.3}/PKG-INFO +3 -3
  2. {flowstash_runtime-0.8.2 → flowstash_runtime-0.8.3}/pyproject.toml +3 -3
  3. {flowstash_runtime-0.8.2 → flowstash_runtime-0.8.3}/src/flowstash/runtime/worker/backends/managed/http_entrypoint.py +27 -3
  4. {flowstash_runtime-0.8.2 → flowstash_runtime-0.8.3}/src/flowstash/runtime/__init__.py +0 -0
  5. {flowstash_runtime-0.8.2 → flowstash_runtime-0.8.3}/src/flowstash/runtime/ingress/__init__.py +0 -0
  6. {flowstash_runtime-0.8.2 → flowstash_runtime-0.8.3}/src/flowstash/runtime/ingress/app.py +0 -0
  7. {flowstash_runtime-0.8.2 → flowstash_runtime-0.8.3}/src/flowstash/runtime/ingress/router.py +0 -0
  8. {flowstash_runtime-0.8.2 → flowstash_runtime-0.8.3}/src/flowstash/runtime/wiring/__init__.py +0 -0
  9. {flowstash_runtime-0.8.2 → flowstash_runtime-0.8.3}/src/flowstash/runtime/wiring/runtime.py +0 -0
  10. {flowstash_runtime-0.8.2 → flowstash_runtime-0.8.3}/src/flowstash/runtime/worker/__init__.py +0 -0
  11. {flowstash_runtime-0.8.2 → flowstash_runtime-0.8.3}/src/flowstash/runtime/worker/backends/__init__.py +0 -0
  12. {flowstash_runtime-0.8.2 → flowstash_runtime-0.8.3}/src/flowstash/runtime/worker/backends/dramatiq/__init__.py +0 -0
  13. {flowstash_runtime-0.8.2 → flowstash_runtime-0.8.3}/src/flowstash/runtime/worker/backends/dramatiq/bootstrap.py +0 -0
  14. {flowstash_runtime-0.8.2 → flowstash_runtime-0.8.3}/src/flowstash/runtime/worker/backends/dramatiq/consumer_middleware.py +0 -0
  15. {flowstash_runtime-0.8.2 → flowstash_runtime-0.8.3}/src/flowstash/runtime/worker/backends/dramatiq/dramatiq_backend.py +0 -0
  16. {flowstash_runtime-0.8.2 → flowstash_runtime-0.8.3}/src/flowstash/runtime/worker/backends/dramatiq/dramatiq_consumer.py +0 -0
  17. {flowstash_runtime-0.8.2 → flowstash_runtime-0.8.3}/src/flowstash/runtime/worker/backends/dramatiq/entrypoint.py +0 -0
  18. {flowstash_runtime-0.8.2 → flowstash_runtime-0.8.3}/src/flowstash/runtime/worker/backends/managed/README.md +0 -0
  19. {flowstash_runtime-0.8.2 → flowstash_runtime-0.8.3}/src/flowstash/runtime/worker/backends/managed/__init__.py +0 -0
  20. {flowstash_runtime-0.8.2 → flowstash_runtime-0.8.3}/src/flowstash/runtime/worker/backends/managed/drain.py +0 -0
  21. {flowstash_runtime-0.8.2 → flowstash_runtime-0.8.3}/src/flowstash/runtime/worker/backends/managed/feed_consumer.py +0 -0
  22. {flowstash_runtime-0.8.2 → flowstash_runtime-0.8.3}/src/flowstash/runtime/worker/backends/managed/main.py +0 -0
  23. {flowstash_runtime-0.8.2 → flowstash_runtime-0.8.3}/src/flowstash/runtime/worker/backends/managed/managed_consumer.py +0 -0
  24. {flowstash_runtime-0.8.2 → flowstash_runtime-0.8.3}/src/flowstash/runtime/worker/backends/managed/task_resolver.py +0 -0
  25. {flowstash_runtime-0.8.2 → flowstash_runtime-0.8.3}/src/flowstash/runtime/worker/runner.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: flowstash-runtime
3
- Version: 0.8.2
3
+ Version: 0.8.3
4
4
  Summary: Actual runtime engine and bootstrap layer for the flowstash platform.
5
5
  Author: juraj.bezdek@gmail.com
6
6
  Author-email: juraj.bezdek@gmail.com
@@ -12,7 +12,7 @@ Classifier: Programming Language :: Python :: 3.13
12
12
  Requires-Dist: apscheduler (>=3.11.0)
13
13
  Requires-Dist: dramatiq (>=1.16.0)
14
14
  Requires-Dist: fastapi (>=0.110.0)
15
- Requires-Dist: flowstash-clients (>=0.8.2,<0.9.0)
16
- Requires-Dist: flowstash-lib (>=0.8.2,<0.9.0)
15
+ Requires-Dist: flowstash-clients (>=0.8.3,<0.9.0)
16
+ Requires-Dist: flowstash-lib (>=0.8.3,<0.9.0)
17
17
  Requires-Dist: pyyaml (>=6.0.1)
18
18
  Requires-Dist: uvicorn (>=0.29.0)
@@ -1,12 +1,12 @@
1
1
  [project]
2
2
  name = "flowstash-runtime"
3
- version = "0.8.2"
3
+ version = "0.8.3"
4
4
  description = "Actual runtime engine and bootstrap layer for the flowstash platform."
5
5
  authors = [{name = "juraj.bezdek@gmail.com", email = "juraj.bezdek@gmail.com"}]
6
6
  requires-python = ">=3.11"
7
7
  dependencies = [
8
- "flowstash-clients>=0.8.2,<0.9.0",
9
- "flowstash-lib>=0.8.2,<0.9.0",
8
+ "flowstash-clients>=0.8.3,<0.9.0",
9
+ "flowstash-lib>=0.8.3,<0.9.0",
10
10
  "fastapi>=0.110.0",
11
11
  "uvicorn>=0.29.0",
12
12
  "dramatiq>=1.16.0",
@@ -12,6 +12,7 @@ Feed endpoints:
12
12
 
13
13
  import logging
14
14
  import os
15
+ import uuid
15
16
  from contextlib import asynccontextmanager
16
17
  from datetime import datetime, UTC
17
18
  from typing import Any, Dict, List, Optional
@@ -81,9 +82,12 @@ class TaskPayload(BaseModel):
81
82
  pipeline: Optional[str] = None
82
83
  triggered_by: Optional[str] = None
83
84
  cron: Optional[str] = None
84
- # run_id is kept for backward compatibility with older payloads that carry it,
85
- # but it is NOT used as the execution run_id. The execution side always allocates
86
- # a fresh run_id. Causal metadata is carried in the `delegation` field instead.
85
+ # run_id is pre-allocated by the platform at submit time and travels in BOTH the
86
+ # payload and the request URL (?run_id=...). The execution side USES it as the run_id
87
+ # so the id is known up-front and is STABLE across Cloud Tasks redeliveries — that lets
88
+ # Cloud Run failure logs (which include the URL's run_id) be joined back to this run.
89
+ # Older payloads that omit it fall back to a freshly generated id. Causal metadata is
90
+ # carried in the `delegation` field instead.
87
91
  run_id: Optional[str] = None
88
92
  tags: Optional[Dict[str, Any]] = None
89
93
  delegation: Optional[DelegationMetadataModel] = None
@@ -207,9 +211,17 @@ async def _execute_managed_task(payload: TaskPayload) -> dict:
207
211
  kwargs = payload.effective_kwargs()
208
212
  raw_args = {"args": args, "kwargs": kwargs}
209
213
 
214
+ # Use the run_id pre-allocated by the platform (carried in the payload and the
215
+ # request URL). It is known up-front and STABLE across Cloud Tasks redeliveries, so
216
+ # all retries of one task share a run_id (collapsing to a single run) and Cloud Run
217
+ # failure logs — which include the URL's run_id — can be joined back to this run.
218
+ # Fall back to a fresh id for older payloads that don't carry one.
219
+ execution_run_id = payload.run_id or str(uuid.uuid4())
220
+
210
221
  with integration_context(
211
222
  integration=integration,
212
223
  integration_pipeline=pipeline,
224
+ run_id=execution_run_id,
213
225
  parent_run_id=parent_run_id,
214
226
  operation_id=operation_id,
215
227
  tags=tags,
@@ -254,6 +266,10 @@ async def _execute_managed_task(payload: TaskPayload) -> dict:
254
266
  correlation=ctx.corelation,
255
267
  attrs={"error": error, "traceback": tb},
256
268
  )
269
+ # Re-raise so the run ends as FAILED only (not ALSO SUCCEEDED) and the handler
270
+ # returns 500 → Cloud Tasks retries the delivery. Without this, a raising task
271
+ # was recorded as both FAILED and SUCCEEDED and the worker returned 200.
272
+ raise
257
273
 
258
274
  await record_run_ended(
259
275
  status="SUCCEEDED",
@@ -301,6 +317,14 @@ async def handle_task(request: Request, payload: TaskPayload):
301
317
  status_code=status.HTTP_404_NOT_FOUND,
302
318
  content={"status": "TASK_NOT_FOUND", "detail": str(e)},
303
319
  )
320
+ except Exception as e:
321
+ # Task execution failed (already recorded as FAILED on the run). Return 500 so
322
+ # Cloud Tasks retries the delivery; the request scope still releases its slot
323
+ # and flushes observability in its finally block.
324
+ return JSONResponse(
325
+ status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
326
+ content={"detail": {"status": "FAILED", "error": str(e)}},
327
+ )
304
328
 
305
329
 
306
330
  # ─── Feed: shared helpers ────────────────────────────────────────────