perf(events): SSE dirty-flag + tear-proof job serialization - #305
Merged
Conversation
Adds Job.version, bumped by _set() on every field write. The SSE stream now compares versions instead of re-serializing + string-diffing on every 0.2s tick -- idle connections drop from a full to_state()+json.dumps per tick to one int compare, eliminating ~1,000 serializations/s at the 200-connection cap. Also closes #285 for real: if job.version changes while to_state() is mid-call, the snapshot may mix pre- and post-write fields (a torn read). The stream loop now detects that (version read before vs. after serializing) and discards the snapshot instead of yielding it, retrying immediately. Already-terminal jobs (done/error/cancelled) now close the stream right after the initial snapshot instead of idling. Closes #289
5 tasks
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.
Summary
Job.version, bumped by_set()on every field write. Internal-only (excluded fromto_state()andto_record(), same ascancel_requested).job_events's SSE loop now comparesjob.versionto the last-sent version instead of re-serializing + string-diffingto_state()on every 0.2s tick — idle connections do one int compare per tick instead of a fullto_state()+json.dumps(). At the 200-connection cap that removes ~1,000 serializations/s.job.versionchanges whileto_state()is mid-call (a concurrent_set()from the pipeline thread), the snapshot may mix pre- and post-write fields — a torn read. The loop now detects that (version read before vs. after serializing) and discards the snapshot instead of yielding it, retrying immediately rather than sleeping.Closes #289
Test plan
tests/test_events_stream.pydrives the SSE generator directly: initial snapshot always sent, no duplicate frames while state is static, a frame is emitted on_set()and the stream closes after a terminal transition, a simulated torn read (monkeypatchedto_state()bumping version mid-call) is discarded and re-serialized consistently, an already-terminal job closes after one frame.188 passed, 12 skippedruff check/ruff format --checkclean