Commit fc53ada
committed
fix: [sc-106112] Stop one undeliverable postback from blocking the spool
The spool flush stopped at the first entry that failed, on the assumption that a
failure meant the engine was unreachable. But attemptPostback returned done=false
for any 5xx or unparseable body, not only for connectivity failures, so a single
entry the engine consistently rejected blocked every entry behind it: the flush
restarted from the same poisoned entry on every connection cycle, failed, and
returned. There was no per-entry attempt counter, so it was retried forever, and
the newer results stranded behind it were eventually discarded by the age check --
silently losing exactly the command results the spool was added in sc-97780 to
protect, and logging the loss as "expired", which reads like stale-data cleanup
rather than a delivery failure.
Split the failure into two outcomes at the one place that can tell them apart.
attemptPostback now classifies each attempt: deliveryDone (accepted, already
fulfilled, or permanently rejected), deliveryUnreachable (a transport error, or a
connection that broke mid-response), and deliveryRetryEntry (the engine answered
but would not take this request). In-line retries treat both failures alike, as
before. The flush does not: unreachable still stops it early -- attempting the
rest is pointless and that optimization is worth keeping -- while a rejection
passes over the entry and keeps delivering the ones behind it.
A rejected entry keeps its place in the queue with a persisted attempt counter
and last error, so its budget survives an agent restart, and is abandoned once
the budget is spent: removed under its own drop reason, counted separately, and
surfaced with a best-effort AgentPostbackAbandoned notification, following the
precedent set for exhausted in-line retries. Drop reasons are now counted
individually (expired / capacity / attempts_exhausted / corrupt) rather than
sharing one number, because a spool shedding entries under pressure and one
abandoning a result the engine refuses are diagnosed very differently.
Rejections count against the budget at most once every 10 minutes. An engine
failing wholesale answers 5xx for every entry, which at the HTTP layer is
indistinguishable from it rejecting each one specifically; without spacing, a
flapping connection could spend a budget in minutes and abandon results the
engine would have accepted on recovery -- worse than the behaviour being
replaced. Spacing bounds the budget in wall-clock time rather than in reconnects.
It never delays the pass-over itself: a rejected entry is skipped on every flush
regardless, and only the counting is spaced.
Entry files written by an older agent have no attempt counter and simply read as
never attempted, so an upgrade delivers them rather than discarding them.1 parent 8428a0f commit fc53ada
6 files changed
Lines changed: 1101 additions & 101 deletions
File tree
- cmd/agent_smith
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
82 | 82 | | |
83 | 83 | | |
84 | 84 | | |
85 | | - | |
| 85 | + | |
86 | 86 | | |
87 | 87 | | |
88 | 88 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
303 | 303 | | |
304 | 304 | | |
305 | 305 | | |
| 306 | + | |
| 307 | + | |
| 308 | + | |
| 309 | + | |
| 310 | + | |
| 311 | + | |
| 312 | + | |
| 313 | + | |
| 314 | + | |
| 315 | + | |
| 316 | + | |
| 317 | + | |
| 318 | + | |
| 319 | + | |
| 320 | + | |
| 321 | + | |
| 322 | + | |
| 323 | + | |
| 324 | + | |
| 325 | + | |
| 326 | + | |
| 327 | + | |
| 328 | + | |
| 329 | + | |
| 330 | + | |
| 331 | + | |
| 332 | + | |
| 333 | + | |
| 334 | + | |
| 335 | + | |
| 336 | + | |
| 337 | + | |
| 338 | + | |
| 339 | + | |
| 340 | + | |
| 341 | + | |
| 342 | + | |
| 343 | + | |
| 344 | + | |
| 345 | + | |
| 346 | + | |
306 | 347 | | |
307 | 348 | | |
308 | 349 | | |
| |||
0 commit comments