-
-
Notifications
You must be signed in to change notification settings - Fork 638
Add client disconnect handling and documentation to concurrent streaming #2026
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
Improves error handling and documentation for concurrent component streaming: 1. Client Disconnect Handling - Added IOError and Errno::EPIPE exception handling in producer tasks - Added response.stream.closed? check before expensive operations - Added exception handling in writer task to stop gracefully on disconnect - Prevents wasted resources when clients disconnect mid-stream 2. Documentation Enhancements - Added detailed comments explaining producer-consumer pattern - Documented ordering guarantees for concurrent streaming - Clarified that chunks from same component maintain order - Clarified that chunks from different components may interleave - Added memory management documentation 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
WalkthroughAdds a producer–consumer streaming implementation using an Async::LimitedQueue to coordinate multiple producers with a single writer, adds client-disconnect logging and handling (IOError, Errno::EPIPE), and updates tests to stub Changes
Sequence Diagram(s)sequenceDiagram
autonumber
participant Controller as Caller
participant Producer as Producer Fiber(s)
participant Queue as Async::LimitedQueue
participant Writer as Writer Task
participant Stream as response.stream
Controller->>Producer: spawn producers (render component chunks)
Producer->>Stream: check `closed?`
alt stream open
Producer->>Queue: push chunk (may block when full)
else closed
Producer->>Producer: break early
end
Controller->>Writer: spawn writer task
Writer->>Queue: pop chunk (blocks if empty)
Queue-->>Writer: chunk
Writer->>Stream: write chunk
alt IOError / EPIPE
Writer->>Writer: rescue and log client disconnect
Writer->>Queue: drain remaining items
end
Writer->>Queue: close (end-of-stream signal)
Writer->>Stream: finalize / complete response
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes
Possibly related PRs
Suggested labels
Suggested reviewers
Poem
Pre-merge checks and finishing touches❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✨ Finishing touches
🧪 Generate unit tests (beta)
📜 Recent review detailsConfiguration used: CodeRabbit UI Review profile: CHILL Plan: Pro 📒 Files selected for processing (1)
🧰 Additional context used🧠 Learnings (3)📚 Learning: 2024-10-08T20:53:47.076ZApplied to files:
📚 Learning: 2025-02-12T16:38:06.537ZApplied to files:
📚 Learning: 2025-04-26T21:55:55.874ZApplied to files:
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
🔇 Additional comments (1)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Code Review: PR #2026 - Client Disconnect Handling and DocumentationOverviewThis PR adds client disconnect handling and comprehensive documentation to the concurrent streaming feature. Overall, this is a well-executed improvement that addresses a real resource leak issue and adds valuable documentation. ✅ Strengths1. Addresses Real Resource WasteThe client disconnect handling prevents unnecessary processing when clients disconnect mid-stream, which is a legitimate production concern. The implementation correctly handles both 2. Excellent DocumentationThe added comments are clear, concise, and highly valuable:
This documentation will significantly help future maintainers understand the concurrent streaming architecture. 3. Minimal, Targeted ChangesThe PR adds only what is necessary - no scope creep, no refactoring. This makes it easy to review and reduces risk. 🔍 Issues & Recommendations1. Missing Test Coverage (HIGH PRIORITY)The PR claims "No functional changes" but it does add functional error handling. The existing tests in
Recommendation: Add test cases for disconnect scenarios to verify the error handling works correctly and tasks stop gracefully. 2. Race Condition Risk (MEDIUM PRIORITY)The check at line 98 has a potential TOCTOU (time-of-check-time-of-use) race. The stream could close between the check and the enqueue operation. However, the 3. Inconsistent Error Handling Return Values (LOW PRIORITY)
The asymmetry is minor but potentially confusing. Consider using consistent returns for clarity. 4. Missing Logging (LOW PRIORITY)When clients disconnect, there is no visibility into this event. For production debugging, it would be helpful to add debug-level logging when disconnect exceptions are caught. 5.
|
Code ReviewI've reviewed PR #2026 and have the following feedback: ✅ Strengths
🔍 Potential Issues & Suggestions1. Missing test coverage for disconnect scenarios (Priority: High)The PR adds error handling for client disconnects, but I don't see any tests verifying this behavior. Consider adding tests like: it "stops producing when client disconnects" do
queues, controller, stream = setup_stream_test
# Simulate client disconnect
allow(stream).to receive(:closed?).and_return(true)
run_stream(controller) do |_parent|
queues[0].enqueue("Chunk1")
queues[0].close
sleep 0.1
end
# Verify production stopped gracefully
expect(stream).not_to have_received(:write)
end
it "handles IOError in producer task" do
queues, controller, stream = setup_stream_test
run_stream(controller) do |_parent|
allow(stream).to receive(:closed?).and_raise(IOError)
queues[0].enqueue("Chunk1")
queues[0].close
sleep 0.1
end
# Should not raise - error should be caught
end
it "handles EPIPE in writer task" do
queues, controller, stream = setup_stream_test
allow(stream).to receive(:write).and_raise(Errno::EPIPE)
run_stream(controller) do |_parent|
queues[0].enqueue("Chunk1")
queues[0].close
sleep 0.1
end
# Should not raise - error should be caught
endLocation: 2. Redundant error handling (Priority: Low)The code checks both
This is actually fine as-is, but you might add a comment explaining why both are needed: # Check if client disconnected before expensive operations
# Note: Stream could close between this check and fiber.resume,
# so we also catch IOError/EPIPE below as a safety net
break if response.stream.closed?Location: 3. Consider logging disconnect events (Priority: Medium)For debugging and monitoring, it might be helpful to log when client disconnects occur: rescue IOError, Errno::EPIPE => e
# Client disconnected - stop producing
Rails.logger.debug("Client disconnected during component streaming: #{e.class}")
breakThis would help diagnose issues in production where clients frequently disconnect. Location: 4. Error handling in writer could be more specific (Priority: Low)The writer task catches exceptions but returns
Current code is safe but possibly too silent: rescue IOError, Errno::EPIPE => e
# Client disconnected - stop writing
Rails.logger.debug("Client disconnected, stopping writer task: #{e.class}")
nil
rescue StandardError => e
# Unexpected error - log and re-raise
Rails.logger.error("Unexpected error in writer task: #{e.class} - #{e.message}")
raiseLocation: 5. Initial template write has no error handling (Priority: Low)The begin
response.stream.write(template_string)
rescue IOError, Errno::EPIPE
# Client disconnected before streaming started
return
endLocation: 🎯 Best Practices✅ Follows Ruby style conventions 📊 Performance ConsiderationsThe
🔐 Security ConsiderationsNo security concerns identified. The error handling properly isolates client-side issues (disconnects) from server-side state. 📝 Overall AssessmentThis is a solid PR that improves error handling and documentation. The main gap is missing test coverage for the new error handling paths. I recommend adding tests for:
The code changes themselves are sound and follow Ruby best practices. Recommendation: Approve with suggestion to add tests for the new error handling paths before merging. 🤖 Review generated with Claude Code |
Code Review - PR #2026SummaryThis PR adds important client disconnect handling and comprehensive documentation to the concurrent streaming feature introduced in #2015. The changes are well-focused and improve both robustness and maintainability. ✅ Strengths1. Excellent Resource ManagementThe addition of disconnect detection prevents wasted CPU cycles when clients drop connections:
2. Outstanding DocumentationThe new comments provide critical understanding of the system:
This documentation transforms complex concurrent code into maintainable code. 3. Minimal Surface Area
🔍 Code Quality AssessmentException Handling ✅The exception handling is correct and follows best practices:
Performance Considerations ✅The
Thread Safety ✅The implementation correctly handles concurrency:
💡 Suggestions for Consideration1. Logging for Production Debugging (Optional)Consider adding logging when clients disconnect: rescue IOError, Errno::EPIPE => e
Rails.logger.debug { "Client disconnected during streaming: #{e.class}" }
breakRationale: In production, knowing how often clients disconnect mid-stream helps with:
Counter-argument: Adds noise to logs if disconnects are frequent and expected. Could be behind a feature flag. 2. Test Coverage for Disconnect Scenarios (Nice-to-have)The existing test suite in it "stops producing when client disconnects" do
queues, controller, stream = setup_stream_test
allow(stream).to receive(:closed?).and_return(false, false, true)
run_stream(controller) do |_parent|
queues[0].enqueue("Chunk1")
queues[0].enqueue("Chunk2")
queues[0].enqueue("Chunk3") # Should not be processed
queues[0].close
sleep 0.1
end
# Should have stopped before processing all chunks
expect(stream).to have_received(:write).at_most(2).times
endNote: Testing the exception path might be harder and potentially not worth the complexity. 3. Documentation Enhancement (Minor)The comment on line 97 could be slightly more specific: # Check if client disconnected before expensive fiber.resume operationThis makes it clearer why we check at this specific point. 🔒 Security AssessmentNo security concerns identified.
📊 Performance ImpactPositive impact:
Negligible cost:
✅ Best Practices CompliancePer
🎯 RecommendationAPPROVE with optional enhancements This PR is production-ready as-is. The suggested improvements are truly optional and wouldn't block merging:
The disconnect handling is particularly important for production environments where clients may:
Great work! This is exactly the kind of defensive programming that makes production systems robust. 🚀 📝 Minor Notes
🤖 Generated with Claude Code |
Improvements: - Add debug logging when client disconnects during streaming (producer/consumer) - Remove unnecessary explicit nil return in rescue block - Add test support for closed? method on mocked stream - Only logs when logging_on_server is enabled These changes improve observability for production debugging without affecting normal operation. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
Code Review - PR #2026SummaryThis PR adds client disconnect handling and documentation for concurrent component streaming. The changes are well-focused and improve resource management when clients disconnect mid-stream. ✅ Strengths
🔍 Code Quality ObservationsGood Practices
🤔 Potential Concerns & Questions1. Race Condition in
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
🧹 Nitpick comments (1)
react_on_rails_pro/spec/react_on_rails_pro/stream_spec.rb (1)
391-391: Necessary stub for closed? checks, but consider testing disconnect scenarios.The stub correctly returns
falseto support the newresponse.stream.closed?checks in the production code. This allows existing tests to continue working under normal (non-disconnect) conditions.However, the PR adds significant disconnect handling logic, but no tests verify this behavior. Consider adding tests that:
- Mock
closed?returningtrueto verify producers stop gracefully- Simulate
IOError/Errno::EPIPEexceptions to verify rescue blocks- Verify logging occurs when disconnects are detected
Would you like me to generate example test cases for disconnect scenarios?
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (2)
react_on_rails_pro/lib/react_on_rails_pro/concerns/stream.rb(4 hunks)react_on_rails_pro/spec/react_on_rails_pro/stream_spec.rb(1 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
react_on_rails_pro/lib/react_on_rails_pro/concerns/stream.rb (2)
react_on_rails_pro/lib/react_on_rails_pro/configuration.rb (1)
configuration(9-38)lib/react_on_rails/react_component/render_options.rb (1)
logging_on_server(94-96)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
- GitHub Check: build-dummy-app-webpack-test-bundles
- GitHub Check: lint-js-and-ruby
- GitHub Check: build-dummy-app-webpack-test-bundles
- GitHub Check: claude-review
🔇 Additional comments (4)
react_on_rails_pro/lib/react_on_rails_pro/concerns/stream.rb (4)
50-63: Excellent documentation of the concurrent streaming model.The documentation clearly explains the producer-consumer pattern, ordering guarantees, and memory management strategy. This will help maintainers understand the concurrency model and backpressure mechanism.
123-127: Correct disconnect handling for the writer task.The exception handling properly detects client disconnects during write operations. Exiting immediately without draining the remaining queue is the right choice—there's no point continuing to process chunks when the client is gone.
129-135: Clean logging implementation with appropriate level.The debug level is suitable for client disconnects, which are expected events rather than errors. The context parameter helps distinguish between producer and consumer disconnects during troubleshooting.
96-109: Good disconnect detection—queue properly unblocks on close.The combination of
response.stream.closed?checks and exception handling provides effective disconnect detection. Theclosed?check at line 98 optimizes by avoiding expensive fiber operations when the client is gone.The original concern about producers blocking on
queue.enqueuewhen the queue is full during writer disconnect is mitigated by the library design: when Async::LimitedQueue is closed, any tasks blocked waiting to enqueue are unblocked. The ensure block callsqueue.close, which will unblock any producer stuck onenqueue, so producers won't remain blocked indefinitely.
Code Review - PR #2026: Client Disconnect Handling and DocumentationOverall AssessmentThis is a well-crafted PR that adds important production-ready error handling to the concurrent streaming feature. The changes are focused, well-documented, and follow best practices. Strengths
Observations and Suggestions1. Test Coverage Gap (Important)The PR adds closed? mock to the test setup but doesn't include tests for the new error handling paths. Consider adding tests that verify:
2. Logging EnhancementThe log_client_disconnect method logs at debug level appropriately. Consider including which fiber/component was affected to make debugging easier when multiple components are streaming. 3. Edge Case - Queue CleanupWhen a producer exits early due to client disconnect, the ensure block handles queue cleanup correctly. Worth verifying that Async::LimitedQueue handles partial producer failures gracefully. 4. Documentation EnhancementConsider adding exception handling details to the main docstring to document that client disconnects are handled gracefully. Security and Performance
RecommendationAPPROVE with suggested test additions. The code changes are solid and production-ready. The main gap is test coverage for the new error handling paths. Once tests are added for disconnect scenarios, this is ready to merge. Great work on improving the production-readiness of the concurrent streaming feature! |
|
@claude fix the failing rspec tests, seems that you just need to mock the closed? method |
|
Claude finished @AbanoubGhadban's task —— View job ✅ Fixed failing RSpec testsTasks:
Summary:Fixed 9 failing tests in Root Cause: The production code at Solution: Added
This matches the approach already used in Commit: |
Fixes 9 failing tests in react_on_rails_pro_helper_spec.rb by adding the closed? method stub to mocked streams. The production code now checks response.stream.closed? for client disconnect detection, so test mocks need to support this method. Changes: - Add closed? stub returning false to stream_view_containing_react_components tests - Add closed? stub returning false to cached_stream_react_component tests 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-authored-by: Abanoub Ghadban <AbanoubGhadban@users.noreply.github.com>
|
Closing this PR because it was based on code that has since been significantly refactored. Why This PR Should Be ClosedTest FailuresThis PR introduces 9 test failures, all caused by the same issue: The code calls Timeline Issue
Path ForwardIf client disconnect handling is still needed:
The concurrent streaming work from #2015 is working well on master. Any additional improvements should build on that foundation. |
Summary
Improves error handling and documentation for concurrent component streaming (building on #2015).
Changes
1. 🚨 Client Disconnect Handling
Previously, if a client disconnected mid-stream, producer and writer tasks would continue processing unnecessarily, wasting server resources.
Added:
IOErrorandErrno::EPIPEexception handling in producer tasksresponse.stream.closed?check before expensive fiber operationsCode location: react_on_rails_pro/lib/react_on_rails_pro/concerns/stream.rb:96-109, 113-125
2. 📚 Documentation Enhancements
Added comprehensive documentation for:
Code location: react_on_rails_pro/lib/react_on_rails_pro/concerns/stream.rb:50-63, 75-77
Related
Testing
🤖 Generated with Claude Code
This change is
Summary by CodeRabbit
New Features
Bug Fixes
Tests