Repository navigation
fix(scores): make flush() and shutdown() work under gevent - #2034
Conversation
Co-authored-by: hassieb.pakzad <hassieb.pakzad@clickhouse.com>
|
@claude review |
There was a problem hiding this comment.
Claude Code Review
No review was started: this request came from a bot account. Manual reviews can only be requested by someone with write access to this repository. Ask a maintainer to comment @claude review, or have your automation post the comment from a user account with write access.
Tip: disable this comment in your organization's Code Review settings.
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
There was a problem hiding this comment.
Code review found no new issues
No new issues were found in this update; 1 finding from earlier reviews is still open above.
Still open from earlier reviews (1):
- 🔴
langfuse/_client/resource_manager.py:625—On Python builds that refuse new threads during interpreter shutdown, the automatic exit flush can now raise and drop q…
If you have decided not to act on one of these findings, resolve its thread (a reply alone leaves it open) and the next review stops counting it. To review this commit again now, use Re-run on its "Claude Code Review" check.
Co-authored-by: hassieb.pakzad <hassieb.pakzad@clickhouse.com>
… plain join() Co-authored-by: hassieb.pakzad <hassieb.pakzad@clickhouse.com>
|
@claude review |
Co-authored-by: hassieb.pakzad <hassieb.pakzad@clickhouse.com>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: c47bea06f8
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
What does this PR do?
Since 5.0.3,
flush()andshutdown()crash under gevent monkey-patching:#2025 added
_join_score_queue, which reads the standard library queue's internals (all_tasks_done,unfinished_tasks). It needed them for one reason:flush()puts a wake signal on the queue, and if the consumer stops before taking it, that signal stays counted and a plainQueue.join()blocks forever.This PR stops counting the wake signal instead:
wake()puts the signal and immediately callstask_done()for it, but only if theput()succeeded. When the queue is full nothing is put, and nothing is acknowledged.task_done()when it takes a wake signal.flush()is back to 5.0.2's plainself._score_queue.join(), preceded by the wake._join_score_queueis deleted.An orphaned wake signal is therefore never an unfinished task and cannot block
join(). Only the public queue API is used (put,get,task_done,join), so this works with the standard library, gevent and eventlet queues.Credit to @DavidTraina: #2032 found and diagnosed this bug, and its gevent check script and scenarios were used to verify this PR. The pre-acknowledged signal follows the pattern in his #2033 for media shutdown signals.
Behavior notes:
flush()blocks, exactly as in 5.0.2. fix(scores): send pending scores immediately on flush() #2025 had made it return in that case; that is dropped here to avoid queue internals.flush()from gevent's native hub threadpool: raises gevent'sLoopExit, exactly as in 5.0.2 (verified against the 5.0.2 code).shutdown(): dropped with a warning instead of enqueued. No consumer is left to send it, so on 5.0.2 it was never sent either, and a laterflush()would block on it.wake()finds the queue full it skips the signal, so thatflush()may wait up toflush_interval(default 1s), as in 5.0.3.Not in this PR: the batch-fragmentation follow-up from #2025.
Type of change
Verification
Tests added in
tests/unit/test_score_consumer.py:test_flush_and_shutdown_work_under_gevent_monkey_patchingruns a real geventmonkey.patch_all()subprocess:flush(), thenshutdown(), thenflush(). It fails on 5.0.3 with the AttributeError above.test_wake_on_full_queue_does_not_acknowledge_a_scorecoverswake()on a full queue, where it must not calltask_done(). It fails if thetask_done()also runs onFull.test_consumer_never_acknowledges_wake_signalsputs a wake signal after each real score and drives every consumer path:pause()/shutdown drain.It counts every
task_done(): exactly one per real score plus one per successful wake. It fails withValueError: task_done() called too many timesif the consumer acknowledges a signal.test_concurrent_flush_returns_only_after_own_score_is_sent[roomy|often-full]has 8 threads, each callingcreate_score()thenflush()25 times (5 times with a 1-slot queue). After eachflush()it checks that the thread's own score has been POSTed, if it was enqueued. Any exception in the consumer thread fails the test. The 1-slot variant fails whenwake()acknowledges onFull.test_score_after_shutdown_is_dropped_and_flush_returns:shutdown(),create_score(), thenflush()returns promptly, nothing is put on the queue and nothing is sent. Without the guardflush()hangs.test_flush_racing_shutdown_does_not_hang: 4flush()threads and ashutdown()thread on one client.test_flush_does_not_wait_for_dropped_scoresandtest_exit_without_shutdown_sends_pending_scores. The second covers theatexitflush, which the earlier review threads asked for.The existing
test_flush_after_shutdown_returnsandtest_flush_returns_when_consumer_stops_after_liveness_checkstill pass. The latter now also checks that its orphaned signal stays queued without being an unfinished task.Manual checks:
flush()andshutdown()both return;flush(): returns immediately;LoopExit, the same as 5.0.2.monkey_patch():flush()sends the score in about 13ms.flush()latency on the standard library queue. Medians of 3 runs, mock transport, Python 3.12 on Linux:main(5.0.3)flush()flush()with one score queuedChecklist
code_review.md.The PR still needs the automatic-shutdown fix before merging.
Summary
Replaces reads of private queue fields with
Queue.join()in a daemon helper thread.Reviews (2) · Last reviewed commit: "fix(scores): use only the public Queue A..." · Reviewed by Greptile