Recreate the generator only when it actually latched - #461
Open
creslinux wants to merge 1 commit into
Open
Conversation
generate_gen's exception handler unconditionally scheduled create_generator(), which cancels every in-flight job via wait_for_jobs(skip_wait=True). Any per-request generation error therefore killed all concurrent requests, whose consumers returned partial or empty completions with HTTP 200. With exllamav3 containing start/prefill failures per job (turboderp-org/exllamav3#319) the generator stays healthy after such errors, so recreating it is pure damage. - Recreate, and record the unhealthy event, only when AsyncGenerator.error is set, i.e. the wrapper actually latched. Contained errors log a warning and re-raise for that request only. Engines without the latch attribute keep the historical behaviour. On current engines every handler-visible exception arrives via the latch, so behaviour there is unchanged. - Cancel the failed job when not recreating. Recreation used to do this as a side effect; without it an error raised by this consumer leaves the job generating into a queue nobody drains. cancel() is a no-op for a job the engine already reaped. - Retire the previous generator with close() before building its replacement. Cancelling the jobs alone left the old iteration task parked forever on an emptied job condition, keeping the old sync Generator reachable for the life of the process.
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.
generate_gen's exception handler unconditionally schedules
create_generator(), which cancels every in-flight job viawait_for_jobs(skip_wait=True). Any per-request generation error therefore kills all concurrent requests — and their consumers return partial or empty completions with HTTP 200, because cancellation ends the stream cleanly rather than as an error.On the current pin (exllamav3 1.4.6) every engine exception that reaches this handler arrives via the AsyncGenerator latch, so recreation is the only recovery there and this change leaves that path untouched. exllamav3 dev now contains start/prefill failures per job (turboderp-org/exllamav3#319, merged, next release): those errors are delivered to the failing request alone and the generator stays healthy — at which point unconditional recreation tears down concurrent requests for failures the engine already handled.
Change
AsyncGenerator.erroris set, i.e. the wrapper actually latched. Engines without the latch attribute keep the historical always-recreate behaviour.cancel()is a no-op for a job the engine has already reaped. Note this also applies today: a consumer-side exception on 1.4.6 now cancels its own job instead of recreating the generator and cancelling everyone else's.create_generator()nowclose()s the previous generator before building its replacement. Cancelling the jobs alone left the old iteration task parked forever on an emptied job condition, keeping the old sync Generator reachable for the life of the process. After a latch the task has already exited, so this is a no-op on that path.Evidence
Fault-injection A/B on a 4-slot recurrent pool, three-layer matrix in turboderp-org/exllamav3#318:
The guard is certified live by that matrix.
close()on a latched generator andcancel()on an already-reaped job are verified against the real classes rather than in a live incident, since the certified incident is engine-contained and never latches. The script below runs against the installed wheel — no model or GPU needed:verify_close_cancel.py
Deliberate choice worth a maintainer's eye
Contained errors no longer feed HealthManager. That's correct for a healthy generator, but it means a device that OOMs on routine requests becomes invisible to
/healthwhile each affected request 503s. Happy to add a lighter "degraded" signal if you'd prefer that visibility kept.