Stop losing AI messages when a deploy interrupts them - #5069
Open
elias-ba wants to merge 1 commit into
Open
Conversation
Security Review ✅
|
elias-ba
force-pushed
the
timeout-oban-lifecycle
branch
2 times, most recently
from
August 15, 2026 23:39
f742165 to
4cff9e9
Compare
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## timeout-stable-message-order #5069 +/- ##
==============================================================
- Coverage 90.6% 90.6% -0.0%
==============================================================
Files 421 421
Lines 20012 20014 +2
==============================================================
- Hits 18138 18134 -4
- Misses 1874 1880 +6 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
elias-ba
force-pushed
the
timeout-oban-lifecycle
branch
2 times, most recently
from
August 17, 2026 09:24
fe286cd to
0797f22
Compare
This was referenced Aug 17, 2026
The handler for Oban's :stop event was written but never attached, so a job cancelled mid-flight told nobody. Its message stayed :processing forever, the panel stayed locked for everyone in that session, and no error was raised. Attaching it needed three other things to be true. It gets its own handler id rather than joining the exception one. :telemetry drops a handler from every event it registered for the first time it raises, so sharing an id would let one bad :stop take Oban exception reporting down with it until the next restart. The lookups become get rather than get!, because :stop fires for every successful job in every queue - the busiest path we have - and a message deleted while its job ran would raise there and trigger exactly that. Both paths are now covered by a test that deletes the message first, since returning at all is what proves the clause is there. The two :circuit events go: Oban has not emitted them since 2.6. The drain window moves from two minutes to six. It was shorter than an AI job's own ceiling, so a deploy landing on a running job killed it after Oban had already stopped the producer that would have reported it - no telemetry at all, which is the one case attaching :stop cannot rescue. Application start now warns if that inverts again. Two log lines drop to warning. Both sat next to a Sentry capture deliberately set to warning, so the error level was raising a second and louder event for something the code had already judged not to be a fault. The new test emits the event instead of calling the handler directly, the way the others do. That is why this was covered and still broken.
elias-ba
force-pushed
the
timeout-oban-lifecycle
branch
from
August 18, 2026 07:29
0797f22 to
078cb15
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Description
A deploy cuts off whatever is running. When that was an AI chat message, nothing said so: the message stayed
:processingforever, the panel stayed locked for everyone in that session, and no error was raised. You only found it by looking at the row.Oban announces a job being stopped and we were not listening. The handler for it had been written and never attached. Attaching it needed three other things to be true.
It gets its own handler id rather than joining the exception one.
:telemetrydrops a handler from every event it registered for the first time it raises, so sharing an id would let one bad:stoptake Oban exception reporting down with it until the next restart.The two
[:oban, :circuit, _]events go with it. Oban has not emitted them since 2.6, and there was no clause for them, so one arriving would have raised and detached everything.The lookups become
getrather thanget!.:stopfires for every successful job in every queue, which is the busiest path we have, and a message deleted while its job ran would raise there and trigger exactly the detach above. Both paths now have a test that deletes the message first, since returning at all is what proves the clause is there.The drain window moves from two minutes to six. It was shorter than an AI job's own ceiling, so a deploy landing on a running job killed it after Oban had already stopped the producer that would have reported it. That is no telemetry at all, and the one case attaching
:stopcannot rescue. Application start now warns if that inverts again, and the ceiling is available on its own asjob_timeout/0for the callers that have no job in hand.Two log lines drop to warning: a timeout on the exception path, and a non-success stop. Both sat next to a Sentry capture deliberately set to warning, so the error level was raising a second and louder event for something the code had already judged not to be a fault.
The new test emits the event rather than calling the handler directly, the way the others do. That is why this was covered and still broken.
Part of #4260 (the other half is #5071).
Validation steps
Open a workflow, open the AI assistant, and send a message. While it is still thinking, stop the server (Ctrl+C twice) and start it again. Reload the page: the message should show as failed with a retry, instead of the panel sitting on "..." forever.
In IEx, check the drain window is longer than a run can take, which is what makes the above work rather than relying on the sweep:
The first should be comfortably larger than the second.
Set
APOLLO_REQUEST_TIMEOUT_MShigh enough to invert those two and restart. There should be a warning at boot naming both.Additional notes for the reviewer
The two
[:oban, :circuit, _]events are removed rather than given a clause. Oban has not emitted them since 2.6, so nothing should arrive on them; say so if you disagree.AI Usage
Please disclose whether you've used AI anywhere in this PR (it's cool, we just want to know!):
You can read more details in our Responsible AI Policy
Pre-submission checklist
/reviewwith Claude Code):owner,:admin,:editor,:viewer)