Commit 7828eb5
authored
feat: orphan reconciler for stuck batch jobs (llm-d#452)
* feat: orphan reconciler, NonTerminal query, GC wiring, heartbeat logging
Add an orphan reconciler that periodically cross-references non-terminal
jobs (DB), queued jobs (priority queue), and in-flight entries to detect
and recover orphaned batch jobs. Triage logic transitions orphans based
on their current status and SLO expiry using CAS updates to prevent
overwriting concurrent processor decisions.
Wire the reconciler into batch-gc alongside the existing GC collector
via errgroup. Add NonTerminal query support to BatchQuery for both
PostgreSQL (raw SQL condition) and Redis (new Lua script). Add
heartbeat start/refresh/stop logging to the processor worker.
Closes llm-d#434
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Signed-off-by: Lior Aronovich <lioraronpr@gmail.com>
* fix: address code review findings for orphan reconciler
- Add NewReconciler input validation (nil clients, non-positive interval)
- Separate CAS conflicts from errors in Result (new Conflicts field)
- Extract TerminalStatuses() into shared openai package
- Convert extractSLO/isSLOExpired to package-level functions
- Fix unreachable code and missing error handling in triage paths
- Add comprehensive tests: edge cases, validation, terminal status sync
- Add Redis NonTerminal query integration test
- Add MockInFlightClient.SetLastSeen test helper for stale entry testing
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Signed-off-by: Lior Aronovich <lioraronpr@gmail.com>
* feat: add dry-run mode to orphan reconciler
When dry-run is enabled, the reconciler detects and counts orphans but
skips all mutating operations (DB transitions, queue enqueues, in-flight
deletes). The reconciler inherits the GC's existing dry_run config flag.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Signed-off-by: Lior Aronovich <lioraronpr@gmail.com>
* feat: heartbeat DB status check to detect reconciler actions
On each heartbeat tick, the processor now reads the job's DB status.
If the reconciler has acted (terminal status or reverted to validating),
the heartbeat calls requestAbortFn to stop all in-flight requests.
The processor's subsequent CAS write fails with ErrConflict, and it
yields cleanly. This cuts wasted processing from up to 60 minutes
(full reconciler interval) to ~5 minutes (heartbeat interval).
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Signed-off-by: Lior Aronovich <lioraronpr@gmail.com>
* refactor: precompute NonTerminal SQL condition, move NonTerminal into dbGet
- PostgreSQL: compute the NOT IN clause once at package init via
buildNonTerminalCondition() instead of rebuilding on every DBGet call
- Redis: move NonTerminal Lua script branch into dbGet alongside the
existing purpose/expiry/tenant branches for consistency
- Add comment on TerminalStatuses() explaining per-call allocation
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Signed-off-by: Lior Aronovich <lioraronpr@gmail.com>
* refactor: rename NonTerminal Lua script to align with naming convention
Rename redis_get_non_terminal.lua → redis_get_by_non_terminal.lua and
update Go variable names (getNonTerminalLua → getByNonTerminalLua,
redisScriptGetNonTerminal → redisScriptGetByNonTerminal) to match the
existing redisScriptGetBy* pattern.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Signed-off-by: Lior Aronovich <lioraronpr@gmail.com>
* refactor: replace goto/label with nested conditionals in NonTerminal Lua script
Consistent with the other Redis Lua scripts in this package which use
nested if blocks instead of goto.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Signed-off-by: Lior Aronovich <lioraronpr@gmail.com>
* refactor: rename IsFinal to IsTerminal, consistent heartbeat log prefix
- Rename BatchStatus.IsFinal() → IsTerminal() to align with
TerminalStatuses() naming
- Use logr.WithValues for jobId in heartbeat logger
- Consistent "Heartbeat: ..." prefix on all heartbeat log messages
- Pass logger to checkReconcilerActed to avoid redundant WithValues
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Signed-off-by: Lior Aronovich <lioraronpr@gmail.com>
* refactor: add Reconciler: log prefix, remove unused run() return, flip dry-run guard
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Signed-off-by: Lior Aronovich <lioraronpr@gmail.com>
* fix: add batchDB nil check in validate(), skip in-flight delete on failed triage
- Processor.validate() now checks batchDB for nil, preventing a panic
in checkReconcilerActed during heartbeat.
- transitionOrphan and reEnqueueOrphan now return bool to signal
success. triageOrphan only deletes the in-flight entry when the
action succeeded, avoiding removal of a legitimate processor's
heartbeat tracking after a CAS conflict.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Signed-off-by: Lior Aronovich <lioraronpr@gmail.com>
* refactor: use shared terminalStatuses array in IsTerminal and TerminalStatuses
Single source of truth for terminal statuses. Declared as an array so
it cannot be appended to or resliced. IsTerminal ranges over it
directly (no allocation). TerminalStatuses returns a copy so callers
cannot mutate the canonical list.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Signed-off-by: Lior Aronovich <lioraronpr@gmail.com>
* refactor: skip queue query when no non-terminal jobs exist
When fetchNonTerminalJobs returns zero jobs, skip PQGetIDs and the
triage loop. InFlightGetAll and cleanupStaleInflight still run so
stale entries for already-terminal jobs are cleaned up.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Signed-off-by: Lior Aronovich <lioraronpr@gmail.com>
* refactor: simplify batch-gc main to always use errgroup
Removes the duplicated gc.RunLoop path when reconciler is disabled.
The errgroup always runs the GC collector; the reconciler is
conditionally added when enabled.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Signed-off-by: Lior Aronovich <lioraronpr@gmail.com>
---------
Signed-off-by: Lior Aronovich <lioraronpr@gmail.com>1 parent 657d95e commit 7828eb5
21 files changed
Lines changed: 1585 additions & 25 deletions
File tree
- cmd/batch-gc
- internal
- database
- api
- mock
- postgresql
- redis
- gc
- config
- reconciler
- processor/worker
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
29 | 29 | | |
30 | 30 | | |
31 | 31 | | |
| 32 | + | |
| 33 | + | |
32 | 34 | | |
33 | 35 | | |
| 36 | + | |
34 | 37 | | |
35 | 38 | | |
36 | 39 | | |
| |||
66 | 69 | | |
67 | 70 | | |
68 | 71 | | |
69 | | - | |
| 72 | + | |
70 | 73 | | |
71 | 74 | | |
72 | | - | |
| 75 | + | |
| 76 | + | |
| 77 | + | |
| 78 | + | |
| 79 | + | |
| 80 | + | |
73 | 81 | | |
74 | 82 | | |
75 | 83 | | |
76 | 84 | | |
77 | 85 | | |
78 | 86 | | |
79 | 87 | | |
80 | | - | |
81 | | - | |
| 88 | + | |
| 89 | + | |
| 90 | + | |
| 91 | + | |
| 92 | + | |
| 93 | + | |
| 94 | + | |
| 95 | + | |
| 96 | + | |
| 97 | + | |
| 98 | + | |
| 99 | + | |
| 100 | + | |
82 | 101 | | |
83 | 102 | | |
84 | 103 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
25 | 25 | | |
26 | 26 | | |
27 | 27 | | |
| 28 | + | |
28 | 29 | | |
29 | 30 | | |
30 | 31 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
78 | 78 | | |
79 | 79 | | |
80 | 80 | | |
| 81 | + | |
| 82 | + | |
| 83 | + | |
| 84 | + | |
| 85 | + | |
| 86 | + | |
| 87 | + | |
| 88 | + | |
| 89 | + | |
81 | 90 | | |
82 | 91 | | |
83 | 92 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
20 | 20 | | |
21 | 21 | | |
22 | 22 | | |
| 23 | + | |
23 | 24 | | |
24 | 25 | | |
| 26 | + | |
25 | 27 | | |
| 28 | + | |
26 | 29 | | |
27 | 30 | | |
28 | 31 | | |
29 | 32 | | |
30 | 33 | | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
31 | 47 | | |
32 | 48 | | |
33 | 49 | | |
| |||
83 | 99 | | |
84 | 100 | | |
85 | 101 | | |
| 102 | + | |
| 103 | + | |
| 104 | + | |
| 105 | + | |
| 106 | + | |
86 | 107 | | |
87 | | - | |
| 108 | + | |
88 | 109 | | |
89 | 110 | | |
90 | 111 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
247 | 247 | | |
248 | 248 | | |
249 | 249 | | |
| 250 | + | |
| 251 | + | |
| 252 | + | |
| 253 | + | |
| 254 | + | |
| 255 | + | |
| 256 | + | |
| 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 | + | |
| 282 | + | |
| 283 | + | |
| 284 | + | |
| 285 | + | |
| 286 | + | |
| 287 | + | |
| 288 | + | |
| 289 | + | |
| 290 | + | |
| 291 | + | |
| 292 | + | |
| 293 | + | |
| 294 | + | |
| 295 | + | |
| 296 | + | |
| 297 | + | |
| 298 | + | |
| 299 | + | |
| 300 | + | |
| 301 | + | |
| 302 | + | |
| 303 | + | |
| 304 | + | |
| 305 | + | |
| 306 | + | |
| 307 | + | |
| 308 | + | |
| 309 | + | |
| 310 | + | |
250 | 311 | | |
251 | 312 | | |
252 | 313 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
173 | 173 | | |
174 | 174 | | |
175 | 175 | | |
| 176 | + | |
176 | 177 | | |
177 | 178 | | |
178 | | - | |
| 179 | + | |
179 | 180 | | |
180 | 181 | | |
181 | 182 | | |
| |||
223 | 224 | | |
224 | 225 | | |
225 | 226 | | |
| 227 | + | |
| 228 | + | |
226 | 229 | | |
227 | 230 | | |
228 | 231 | | |
| |||
301 | 304 | | |
302 | 305 | | |
303 | 306 | | |
| 307 | + | |
304 | 308 | | |
305 | 309 | | |
306 | | - | |
| 310 | + | |
307 | 311 | | |
308 | 312 | | |
309 | 313 | | |
310 | 314 | | |
311 | 315 | | |
312 | | - | |
| 316 | + | |
313 | 317 | | |
314 | 318 | | |
315 | 319 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
160 | 160 | | |
161 | 161 | | |
162 | 162 | | |
163 | | - | |
| 163 | + | |
164 | 164 | | |
165 | 165 | | |
166 | 166 | | |
| |||
183 | 183 | | |
184 | 184 | | |
185 | 185 | | |
186 | | - | |
| 186 | + | |
187 | 187 | | |
188 | 188 | | |
189 | 189 | | |
| |||
202 | 202 | | |
203 | 203 | | |
204 | 204 | | |
205 | | - | |
| 205 | + | |
206 | 206 | | |
207 | 207 | | |
208 | 208 | | |
| |||
232 | 232 | | |
233 | 233 | | |
234 | 234 | | |
235 | | - | |
| 235 | + | |
236 | 236 | | |
237 | 237 | | |
238 | 238 | | |
| |||
265 | 265 | | |
266 | 266 | | |
267 | 267 | | |
268 | | - | |
| 268 | + | |
269 | 269 | | |
270 | 270 | | |
271 | 271 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
96 | 96 | | |
97 | 97 | | |
98 | 98 | | |
99 | | - | |
| 99 | + | |
100 | 100 | | |
101 | 101 | | |
102 | 102 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
233 | 233 | | |
234 | 234 | | |
235 | 235 | | |
236 | | - | |
| 236 | + | |
237 | 237 | | |
238 | 238 | | |
239 | 239 | | |
| |||
293 | 293 | | |
294 | 294 | | |
295 | 295 | | |
| 296 | + | |
| 297 | + | |
| 298 | + | |
| 299 | + | |
| 300 | + | |
| 301 | + | |
| 302 | + | |
| 303 | + | |
| 304 | + | |
| 305 | + | |
| 306 | + | |
296 | 307 | | |
297 | 308 | | |
298 | 309 | | |
| |||
325 | 336 | | |
326 | 337 | | |
327 | 338 | | |
328 | | - | |
| 339 | + | |
329 | 340 | | |
330 | 341 | | |
331 | 342 | | |
| |||
356 | 367 | | |
357 | 368 | | |
358 | 369 | | |
359 | | - | |
| 370 | + | |
360 | 371 | | |
361 | 372 | | |
362 | 373 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
88 | 88 | | |
89 | 89 | | |
90 | 90 | | |
| 91 | + | |
| 92 | + | |
| 93 | + | |
| 94 | + | |
91 | 95 | | |
92 | 96 | | |
93 | 97 | | |
| |||
0 commit comments