Commit 8580069
Avoid redis based locking by using atomic redis primitives (#1150)
* fix: PSv2 follow-up fixes from integration tests (#1135)
* fix: prevent NATS connection flooding and stale job task fetching
- Add connect_timeout=5, allow_reconnect=False to NATS connections to
prevent leaked reconnection loops from blocking Django's event loop
- Guard /tasks endpoint against terminal-status jobs (return empty tasks
instead of attempting NATS reserve)
- IncompleteJobFilter now excludes jobs by top-level status in addition
to progress JSON stages
- Add stale worker cleanup to integration test script
Found during PSv2 integration testing where stale ADC workers with
default DataLoader parallelism overwhelmed the single uvicorn worker
thread by flooding /tasks with concurrent NATS reserve requests.
Co-Authored-By: Claude <noreply@anthropic.com>
* docs: PSv2 integration test session notes and NATS flooding findings
Session notes from 2026-02-16 integration test including root cause
analysis of stale worker task competition and NATS connection issues.
Findings doc tracks applied fixes and remaining TODOs with priorities.
Co-Authored-By: Claude <noreply@anthropic.com>
* docs: update session notes with successful test run #3
PSv2 integration test passed end-to-end (job 1380, 20/20 images).
Identified ack_wait=300s as cause of ~5min idle time when GPU
processes race for NATS tasks.
Co-Authored-By: Claude <noreply@anthropic.com>
* fix: batch NATS task fetch to prevent HTTP timeouts
Replace N×1 reserve_task() calls with single reserve_tasks() batch
fetch. The previous implementation created a new pull subscription per
message (320 NATS round trips for batch=64), causing the /tasks endpoint
to exceed HTTP client timeouts. The new approach uses one psub.fetch()
call for the entire batch.
Co-Authored-By: Claude <noreply@anthropic.com>
* docs: add next session prompt
* feat: add pipeline__slug__in filter for multi-pipeline job queries
Workers that handle multiple pipelines can now fetch jobs for all of them
in a single request: ?pipeline__slug__in=slug1,slug2
Co-Authored-By: Claude <noreply@anthropic.com>
* chore: remove local-only docs and scripts from branch
These files are session notes, planning docs, and test scripts that
should stay local rather than be part of the PR.
Co-Authored-By: Claude <noreply@anthropic.com>
* feat: set job dispatch_mode at creation time based on project feature flags
ML jobs with a pipeline now get dispatch_mode set during setup() instead
of waiting until run() is called by the Celery worker. This lets the UI
show the correct mode immediately after job creation.
Co-Authored-By: Claude <noreply@anthropic.com>
* fix: add timeouts to all JetStream operations and restore reconnect policy
Add NATS_JETSTREAM_TIMEOUT (10s) to all JetStream metadata operations
via asyncio.wait_for() so a hung NATS connection fails fast instead of
blocking the caller's thread indefinitely. Also restore the intended
reconnect policy (2 attempts, 1s wait) that was lost in a prior force push.
Co-Authored-By: Claude <noreply@anthropic.com>
* fix: propagate NATS timeouts as 503 instead of swallowing them
asyncio.TimeoutError from _ensure_stream() and _ensure_consumer() was
caught by the broad `except Exception` in reserve_tasks(), silently
returning [] and making NATS outages indistinguishable from empty queues.
Workers would then poll immediately, recreating the flooding problem.
- Add explicit `except asyncio.TimeoutError: raise` in reserve_tasks()
- Catch TimeoutError and OSError in the /tasks view, return 503
- Restore allow_reconnect=False (fail-fast on connection issues)
- Add return type annotation to get_connection()
Co-Authored-By: Claude <noreply@anthropic.com>
* fix: address review comments (log level, fetch timeout, docstring)
- Downgrade reserve_tasks log to DEBUG when zero tasks reserved (avoid
log spam from frequent polling)
- Pass timeout=0.5 from /tasks endpoint to avoid blocking the worker
for 5s on empty queues
- Fix docstring examples using string 'job123' for int-typed job_id
Co-Authored-By: Claude <noreply@anthropic.com>
* fix: catch nats.errors.Error in /tasks endpoint for proper 503 responses
NoServersError, ConnectionClosedError, and other NATS exceptions inherit
from nats.errors.Error (not OSError), so they escaped the handler and
returned 500 instead of 503.
Co-Authored-By: Claude <noreply@anthropic.com>
---------
Co-authored-by: Claude <noreply@anthropic.com>
* PSv2: Improve task fetching & web worker concurrency configuration (#1142)
* feat: configurable NATS tuning and gunicorn worker management
Rebase onto main after #1135 merge. Keep only the additions unique to
this branch:
- Make TASK_TTR configurable via NATS_TASK_TTR Django setting (default 30s)
- Make max_ack_pending configurable via NATS_MAX_ACK_PENDING setting (default 100)
- Local dev: switch to gunicorn+UvicornWorker by default for production
parity, with USE_UVICORN=1 escape hatch for raw uvicorn
- Production: auto-detect WEB_CONCURRENCY from CPU cores (capped at 8)
when not explicitly set in the environment
Co-Authored-By: Claude <noreply@anthropic.com>
* fix: address PR review comments
- Fix max_ack_pending falsy-zero guard (use `is not None` instead of `or`)
- Update TaskQueueManager docstring with Args section
- Simplify production WEB_CONCURRENCY fallback (just use nproc)
Co-Authored-By: Claude <noreply@anthropic.com>
---------
Co-authored-by: Michael Bunsen <notbot@gmail.com>
Co-authored-by: Claude <noreply@anthropic.com>
* fix: include pipeline_slug in MinimalJobSerializer (#1148)
* fix: include pipeline_slug in MinimalJobSerializer (ids_only response)
The ADC worker fetches jobs with ids_only=1 and expects pipeline_slug in
the response to know which pipeline to run. Without it, Pydantic
validation fails and the worker skips the job.
Co-Authored-By: Claude <noreply@anthropic.com>
* Update ami/jobs/serializers.py
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
---------
Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
* Avoid redis based locking by using atomic updates
* Test concurrency
* Increase max ack pending
* update comment
* CR feedback
* CR feedback
* CR 2
* fix: OrderedEnum comparisons now override str MRO in subclasses
JobState(str, OrderedEnum) was using str's lexicographic __gt__
instead of OrderedEnum's definition-order __gt__, because str
comes first in the MRO. This caused max(FAILURE, SUCCESS) to
return SUCCESS, silently discarding failure state in concurrent
job progress updates.
Fix: __init_subclass__ injects comparison methods directly onto
each subclass so they take MRO priority over data-type mixins.
Also preserve FAILURE status through the progress ternary when
progress < 1.0, so early failure detection isn't overwritten.
Co-Authored-By: Claude <noreply@anthropic.com>
* fix: correct misleading error log about NATS redelivery
The NATS message is ACK'd at line 145, before update_state() and
_update_job_progress(). If either of those raises, the except
block was logging "NATS will redeliver" when it won't.
Co-Authored-By: Claude <noreply@anthropic.com>
* Use job.logger
---------
Co-authored-by: Carlos Garcia Jurado Suarez <carlos@irreverentlabs.com>
Co-authored-by: Michael Bunsen <notbot@gmail.com>
Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>1 parent b01f01b commit 8580069
7 files changed
Lines changed: 255 additions & 199 deletions
File tree
- ami
- jobs
- ml
- orchestration
- tests
- utils
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
84 | 84 | | |
85 | 85 | | |
86 | 86 | | |
87 | | - | |
88 | | - | |
89 | | - | |
| 87 | + | |
90 | 88 | | |
91 | | - | |
92 | | - | |
93 | | - | |
94 | | - | |
95 | | - | |
| 89 | + | |
| 90 | + | |
| 91 | + | |
| 92 | + | |
| 93 | + | |
96 | 94 | | |
97 | 95 | | |
98 | 96 | | |
| |||
126 | 124 | | |
127 | 125 | | |
128 | 126 | | |
| 127 | + | |
129 | 128 | | |
130 | 129 | | |
131 | 130 | | |
| |||
145 | 144 | | |
146 | 145 | | |
147 | 146 | | |
| 147 | + | |
148 | 148 | | |
149 | 149 | | |
150 | 150 | | |
151 | 151 | | |
152 | 152 | | |
153 | | - | |
154 | 153 | | |
155 | 154 | | |
156 | 155 | | |
157 | | - | |
158 | | - | |
159 | | - | |
160 | | - | |
161 | | - | |
| 156 | + | |
| 157 | + | |
| 158 | + | |
162 | 159 | | |
163 | 160 | | |
164 | 161 | | |
| |||
176 | 173 | | |
177 | 174 | | |
178 | 175 | | |
179 | | - | |
180 | | - | |
181 | | - | |
| 176 | + | |
| 177 | + | |
| 178 | + | |
| 179 | + | |
| 180 | + | |
182 | 181 | | |
183 | 182 | | |
184 | 183 | | |
| |||
256 | 255 | | |
257 | 256 | | |
258 | 257 | | |
| 258 | + | |
| 259 | + | |
| 260 | + | |
| 261 | + | |
| 262 | + | |
| 263 | + | |
| 264 | + | |
| 265 | + | |
| 266 | + | |
| 267 | + | |
| 268 | + | |
| 269 | + | |
| 270 | + | |
| 271 | + | |
| 272 | + | |
| 273 | + | |
| 274 | + | |
| 275 | + | |
| 276 | + | |
| 277 | + | |
| 278 | + | |
| 279 | + | |
| 280 | + | |
| 281 | + | |
259 | 282 | | |
260 | 283 | | |
261 | | - | |
| 284 | + | |
262 | 285 | | |
263 | 286 | | |
264 | 287 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
6 | 6 | | |
7 | 7 | | |
8 | 8 | | |
| 9 | + | |
9 | 10 | | |
10 | 11 | | |
11 | 12 | | |
12 | | - | |
| 13 | + | |
13 | 14 | | |
14 | 15 | | |
15 | 16 | | |
16 | 17 | | |
17 | 18 | | |
18 | 19 | | |
19 | 20 | | |
20 | | - | |
| 21 | + | |
21 | 22 | | |
22 | 23 | | |
23 | 24 | | |
24 | 25 | | |
25 | 26 | | |
26 | 27 | | |
27 | | - | |
| 28 | + | |
28 | 29 | | |
29 | 30 | | |
30 | 31 | | |
| |||
237 | 238 | | |
238 | 239 | | |
239 | 240 | | |
240 | | - | |
| 241 | + | |
241 | 242 | | |
242 | | - | |
| 243 | + | |
243 | 244 | | |
244 | | - | |
245 | | - | |
| 245 | + | |
| 246 | + | |
246 | 247 | | |
247 | | - | |
248 | | - | |
249 | | - | |
250 | | - | |
251 | | - | |
252 | | - | |
253 | | - | |
| 248 | + | |
254 | 249 | | |
255 | | - | |
256 | | - | |
257 | | - | |
| 250 | + | |
| 251 | + | |
| 252 | + | |
| 253 | + | |
| 254 | + | |
| 255 | + | |
| 256 | + | |
| 257 | + | |
| 258 | + | |
| 259 | + | |
258 | 260 | | |
259 | | - | |
260 | | - | |
| 261 | + | |
| 262 | + | |
| 263 | + | |
261 | 264 | | |
262 | 265 | | |
263 | | - | |
264 | | - | |
265 | | - | |
| 266 | + | |
| 267 | + | |
| 268 | + | |
266 | 269 | | |
267 | 270 | | |
268 | | - | |
| 271 | + | |
| 272 | + | |
| 273 | + | |
| 274 | + | |
269 | 275 | | |
270 | 276 | | |
271 | | - | |
| 277 | + | |
| 278 | + | |
| 279 | + | |
| 280 | + | |
272 | 281 | | |
273 | 282 | | |
274 | 283 | | |
| |||
0 commit comments