flowstash-runtime 0.9.2__tar.gz → 0.9.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 (26) hide show
  1. {flowstash_runtime-0.9.2 → flowstash_runtime-0.9.3}/PKG-INFO +3 -3
  2. {flowstash_runtime-0.9.2 → flowstash_runtime-0.9.3}/pyproject.toml +3 -3
  3. {flowstash_runtime-0.9.2 → flowstash_runtime-0.9.3}/src/flowstash/runtime/worker/backends/managed/lease_client.py +18 -4
  4. {flowstash_runtime-0.9.2 → flowstash_runtime-0.9.3}/src/flowstash/runtime/__init__.py +0 -0
  5. {flowstash_runtime-0.9.2 → flowstash_runtime-0.9.3}/src/flowstash/runtime/ingress/__init__.py +0 -0
  6. {flowstash_runtime-0.9.2 → flowstash_runtime-0.9.3}/src/flowstash/runtime/ingress/app.py +0 -0
  7. {flowstash_runtime-0.9.2 → flowstash_runtime-0.9.3}/src/flowstash/runtime/ingress/router.py +0 -0
  8. {flowstash_runtime-0.9.2 → flowstash_runtime-0.9.3}/src/flowstash/runtime/wiring/__init__.py +0 -0
  9. {flowstash_runtime-0.9.2 → flowstash_runtime-0.9.3}/src/flowstash/runtime/wiring/runtime.py +0 -0
  10. {flowstash_runtime-0.9.2 → flowstash_runtime-0.9.3}/src/flowstash/runtime/worker/__init__.py +0 -0
  11. {flowstash_runtime-0.9.2 → flowstash_runtime-0.9.3}/src/flowstash/runtime/worker/backends/__init__.py +0 -0
  12. {flowstash_runtime-0.9.2 → flowstash_runtime-0.9.3}/src/flowstash/runtime/worker/backends/dramatiq/__init__.py +0 -0
  13. {flowstash_runtime-0.9.2 → flowstash_runtime-0.9.3}/src/flowstash/runtime/worker/backends/dramatiq/bootstrap.py +0 -0
  14. {flowstash_runtime-0.9.2 → flowstash_runtime-0.9.3}/src/flowstash/runtime/worker/backends/dramatiq/consumer_middleware.py +0 -0
  15. {flowstash_runtime-0.9.2 → flowstash_runtime-0.9.3}/src/flowstash/runtime/worker/backends/dramatiq/dramatiq_backend.py +0 -0
  16. {flowstash_runtime-0.9.2 → flowstash_runtime-0.9.3}/src/flowstash/runtime/worker/backends/dramatiq/dramatiq_consumer.py +0 -0
  17. {flowstash_runtime-0.9.2 → flowstash_runtime-0.9.3}/src/flowstash/runtime/worker/backends/dramatiq/entrypoint.py +0 -0
  18. {flowstash_runtime-0.9.2 → flowstash_runtime-0.9.3}/src/flowstash/runtime/worker/backends/managed/README.md +0 -0
  19. {flowstash_runtime-0.9.2 → flowstash_runtime-0.9.3}/src/flowstash/runtime/worker/backends/managed/__init__.py +0 -0
  20. {flowstash_runtime-0.9.2 → flowstash_runtime-0.9.3}/src/flowstash/runtime/worker/backends/managed/drain.py +0 -0
  21. {flowstash_runtime-0.9.2 → flowstash_runtime-0.9.3}/src/flowstash/runtime/worker/backends/managed/feed_consumer.py +0 -0
  22. {flowstash_runtime-0.9.2 → flowstash_runtime-0.9.3}/src/flowstash/runtime/worker/backends/managed/http_entrypoint.py +0 -0
  23. {flowstash_runtime-0.9.2 → flowstash_runtime-0.9.3}/src/flowstash/runtime/worker/backends/managed/main.py +0 -0
  24. {flowstash_runtime-0.9.2 → flowstash_runtime-0.9.3}/src/flowstash/runtime/worker/backends/managed/managed_consumer.py +0 -0
  25. {flowstash_runtime-0.9.2 → flowstash_runtime-0.9.3}/src/flowstash/runtime/worker/backends/managed/task_resolver.py +0 -0
  26. {flowstash_runtime-0.9.2 → flowstash_runtime-0.9.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.9.2
3
+ Version: 0.9.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,8 +12,8 @@ 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.9.2,<0.10.0)
16
- Requires-Dist: flowstash-lib (>=0.9.2,<0.10.0)
15
+ Requires-Dist: flowstash-clients (>=0.9.3,<0.10.0)
16
+ Requires-Dist: flowstash-lib (>=0.9.3,<0.10.0)
17
17
  Requires-Dist: pyyaml (>=6.0.1)
18
18
  Requires-Dist: uvicorn (>=0.29.0)
19
19
  Requires-Dist: websockets (>=14.0)
@@ -1,12 +1,12 @@
1
1
  [project]
2
2
  name = "flowstash-runtime"
3
- version = "0.9.2"
3
+ version = "0.9.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.9.2,<0.10.0",
9
- "flowstash-lib>=0.9.2,<0.10.0",
8
+ "flowstash-clients>=0.9.3,<0.10.0",
9
+ "flowstash-lib>=0.9.3,<0.10.0",
10
10
  "fastapi>=0.110.0",
11
11
  "uvicorn>=0.29.0",
12
12
  "dramatiq>=1.16.0",
@@ -20,6 +20,8 @@ import asyncio
20
20
  import json
21
21
  import logging
22
22
  import os
23
+ import random
24
+ import uuid
23
25
  from dataclasses import dataclass
24
26
  from typing import Dict, List, Optional, Set
25
27
 
@@ -83,6 +85,9 @@ class LeaseBrokerClient:
83
85
  self._acquire_timeout = acquire_timeout
84
86
  self._reconnect_min = reconnect_min
85
87
  self._reconnect_max = reconnect_max
88
+ # Stable per-process identity so a reconnecting worker reclaims (transfers)
89
+ # its own leases instead of conflicting with its not-yet-reaped old conn.
90
+ self._worker_id = os.getenv("CLOUD_RUN_EXECUTION") or uuid.uuid4().hex
86
91
 
87
92
  self._held: Set[str] = set()
88
93
  self._waiters: Dict[str, List[asyncio.Future]] = {}
@@ -110,13 +115,17 @@ class LeaseBrokerClient:
110
115
  except Exception:
111
116
  pass
112
117
 
118
+ def _connect_url(self) -> str:
119
+ sep = "&" if "?" in self._url else "?"
120
+ return f"{self._url}{sep}worker_id={self._worker_id}"
121
+
113
122
  async def _run(self) -> None:
114
123
  backoff = self._reconnect_min
115
124
  headers = {"Authorization": f"Bearer {self._token}"}
116
125
  while not self._closing:
117
126
  try:
118
127
  async with websockets.connect(
119
- self._url, additional_headers=headers, open_timeout=10
128
+ self._connect_url(), additional_headers=headers, open_timeout=10
120
129
  ) as ws:
121
130
  self._ws = ws
122
131
  backoff = self._reconnect_min
@@ -138,7 +147,9 @@ class LeaseBrokerClient:
138
147
  self._ws = None
139
148
  if self._closing:
140
149
  break
141
- await asyncio.sleep(backoff)
150
+ # Jittered backoff so a broker restart doesn't trigger a synchronized
151
+ # reconnect storm from every worker at once.
152
+ await asyncio.sleep(backoff * (0.5 + 0.5 * random.random()))
142
153
  backoff = min(backoff * 2, self._reconnect_max)
143
154
 
144
155
  # ── messaging ──────────────────────────────────────────────────────────
@@ -250,8 +261,11 @@ def get_lease_client() -> Optional[LeaseBrokerClient]:
250
261
  return _client
251
262
  _resolved = True
252
263
 
253
- if os.getenv("LEASE_BROKER_ENABLED", "true").lower() in ("0", "false", "no"):
254
- logger.info("lease broker disabled via LEASE_BROKER_ENABLED")
264
+ # Default OFF: deploying the worker code is inert until ops explicitly enables
265
+ # the guard (after the broker is deployed and validated). Otherwise an
266
+ # unreachable broker would fail every task closed (503).
267
+ if os.getenv("LEASE_BROKER_ENABLED", "false").lower() not in ("1", "true", "yes"):
268
+ logger.info("lease broker disabled (set LEASE_BROKER_ENABLED=true to enable)")
255
269
  return None
256
270
  if websockets is None:
257
271
  logger.warning("lease broker disabled: 'websockets' not installed")