-
-
Notifications
You must be signed in to change notification settings - Fork 638
Concurrently drain streaming fibers #2015
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
base: master
Are you sure you want to change the base?
Conversation
…ogs; behind config flag
- handle when a component fiber is already drained after the first resume (the shell). - typically, streamed components will have more chunks besides the first chunk. - this is an edge case exposed while testing. - we're basically avoiding getting a FiberError: attempt to resume a terminated fiber
…, edge cases, and producer error
…ming backpressure
…start writer before producers; remove per-producer done markers
- propagate runtime errors instead of silencing them - keep gracefully handling already terminated fibers from resuming - properly handle cleaning/shutting down when errors occur
replace custom test doubles with RSpec's instance_double for cleaner and more maintainable test isolation. This approach follows established mocking patterns and removes dependency on custom streaming buffer implementations.
…onents concurrently
This reverts commit 528ee15ed651378a24d2d35a75063f096df7624e.
…ing" This reverts commit 41f5eb9db2251f2c5f338b037575b46071981508.
…t_streaming_buffer_size for improved memory management and validation
…roved clarity and performance
…doesn't expect a :size keyword argument
The exact version 15.0.0 was yanked from RubyGems, causing CI failures. Updated to 16.0.1.rc.4 which is available and stable.
…on_rails 16.0 The using_packer? method was removed in react_on_rails 16.0 as part of the transition to Shakapacker-only support. Since Shakapacker is now assumed to always be in use, the check is no longer needed. Changes: - Remove using_packer? check from Utils.bundle_file_name method - Remove mock of non-existent method from spec - Update comments to reference Shakapacker instead of webpacker Fixes CI failures in utils_spec.rb
WalkthroughThis PR adds concurrent streaming for React components using the async gem, replaces sequential fiber draining with a bounded producer/consumer model, introduces a configurable buffer size (default 64), and updates tests and changelog to exercise the new async streaming behavior. Changes
Sequence Diagram(s)sequenceDiagram
participant Client
participant Stream as drain_streams_concurrently()
participant Producers as Producer Tasks
participant Queue as LimitedQueue
participant Writer as Writer Task
participant Response
Client->>Stream: stream_view_containing_react_components()
activate Stream
Stream->>Producers: start producer tasks (fibers → enqueue [idx,chunk])
Stream->>Writer: start writer task (dequeue → write)
activate Producers
activate Writer
par Producers produce
Producers->>Queue: enqueue([index, chunk])
and Writer consume
Writer->>Queue: dequeue()
Queue-->>Writer: [index, chunk]
Writer->>Response: write(chunk)
end
Producers->>Queue: enqueue(nil) -- signal end
Writer->>Queue: dequeue()
Queue-->>Writer: nil
Writer->>Response: close()
deactivate Producers
deactivate Writer
Stream-->>Client: streaming complete
deactivate Stream
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes
Possibly related issues
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)
🚧 Files skipped from review as they are similar to previous changes (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 |
PR Review: Concurrently drain streaming fibersThis PR introduces concurrent streaming of React components, which is a significant performance improvement. Here's my detailed review: ✅ Strengths1. Excellent Implementation of Concurrent Streaming
2. Strong Test Coverage
3. Good Configuration Design
4. Documentation
|
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
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
react_on_rails_pro/lib/react_on_rails_pro/configuration.rb (1)
115-123: Call the new buffer-size validation.
validate_concurrent_component_streaming_buffer_sizeis never invoked, soconfig.concurrent_component_streaming_buffer_sizecan silently acceptnil,0, or negative values until we hitAsync::LimitedQueue.newat runtime and get a low-level exception. Wire the validation intosetup_config_valuesso misconfiguration surfaces as the intendedReactOnRailsPro::Error.Apply this diff:
def setup_config_values configure_default_url_if_not_provided validate_url validate_remote_bundle_cache_adapter setup_renderer_password setup_assets_to_copy + validate_concurrent_component_streaming_buffer_size setup_execjs_profiler_if_needed check_react_on_rails_support_for_rsc end
🧹 Nitpick comments (2)
react_on_rails_pro/spec/react_on_rails_pro/stream_spec.rb (2)
468-487: Tighten the backpressure test assertions.The assertion
expect(write_timestamps.length).to be >= 2is too permissive. Since 5 chunks are enqueued plus the initial TEMPLATE write, consider asserting the exact count:- expect(write_timestamps.length).to be >= 2 + expect(write_timestamps.length).to eq(6) # TEMPLATE + 5 chunksAlternatively, if the exact count varies, at least verify all chunks were written:
- expect(write_timestamps.length).to be >= 2 + expect(stream).to have_received(:write).with("TEMPLATE") + 5.times { |i| expect(stream).to have_received(:write).with("Chunk#{i}") }
374-488: Consider adding error handling tests.The test suite covers the happy path well, but consider adding tests for error scenarios:
- What happens when a component fiber raises an error during streaming?
- How does the system handle a queue that closes prematurely?
- What's the behavior when a client disconnects mid-stream?
The PR description mentions "handling for client disconnects," so tests validating this behavior would be valuable.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (1)
react_on_rails_pro/Gemfile.lockis excluded by!**/*.lock
📒 Files selected for processing (8)
react_on_rails_pro/.gitignore(1 hunks)react_on_rails_pro/CHANGELOG.md(2 hunks)react_on_rails_pro/lib/react_on_rails_pro/concerns/stream.rb(1 hunks)react_on_rails_pro/lib/react_on_rails_pro/configuration.rb(6 hunks)react_on_rails_pro/lib/react_on_rails_pro/utils.rb(2 hunks)react_on_rails_pro/react_on_rails_pro.gemspec(1 hunks)react_on_rails_pro/spec/dummy/spec/helpers/react_on_rails_pro_helper_spec.rb(7 hunks)react_on_rails_pro/spec/react_on_rails_pro/stream_spec.rb(2 hunks)
🧰 Additional context used
🧠 Learnings (10)
📚 Learning: 2024-10-08T20:53:47.076Z
Learnt from: theforestvn88
Repo: shakacode/react_on_rails PR: 1620
File: spec/dummy/client/app/startup/HelloTurboStream.jsx:3-3
Timestamp: 2024-10-08T20:53:47.076Z
Learning: The `RailsContext` import in `spec/dummy/client/app/startup/HelloTurboStream.jsx` is used later in the project, as clarified by the user theforestvn88.
Applied to files:
react_on_rails_pro/spec/dummy/spec/helpers/react_on_rails_pro_helper_spec.rbreact_on_rails_pro/spec/react_on_rails_pro/stream_spec.rbreact_on_rails_pro/lib/react_on_rails_pro/utils.rb
📚 Learning: 2025-02-18T13:08:01.477Z
Learnt from: AbanoubGhadban
Repo: shakacode/react_on_rails PR: 1644
File: lib/react_on_rails/helper.rb:190-197
Timestamp: 2025-02-18T13:08:01.477Z
Learning: RSC support validation in React on Rails Pro is handled through a chain of validations:
1. Pro version check in `run_stream_inside_fiber`
2. RSC support check during pack generation via `ReactOnRailsPro.configuration.enable_rsc_support`
3. RSC support validation during component registration
This makes additional validation in the helper methods unnecessary.
Applied to files:
react_on_rails_pro/spec/dummy/spec/helpers/react_on_rails_pro_helper_spec.rbreact_on_rails_pro/CHANGELOG.mdreact_on_rails_pro/spec/react_on_rails_pro/stream_spec.rbreact_on_rails_pro/lib/react_on_rails_pro/configuration.rb
📚 Learning: 2025-09-16T08:01:11.146Z
Learnt from: justin808
Repo: shakacode/react_on_rails PR: 1770
File: lib/generators/react_on_rails/templates/base/base/app/javascript/src/HelloWorld/ror_components/HelloWorld.client.jsx:2-2
Timestamp: 2025-09-16T08:01:11.146Z
Learning: React on Rails uses webpack CSS Modules configuration with namedExports: true, which requires the import syntax `import * as style from './file.module.css'` rather than the default export pattern. This configuration enables better tree shaking and bundle size optimization for CSS modules.
Applied to files:
react_on_rails_pro/CHANGELOG.mdreact_on_rails_pro/spec/react_on_rails_pro/stream_spec.rbreact_on_rails_pro/react_on_rails_pro.gemspecreact_on_rails_pro/.gitignorereact_on_rails_pro/lib/react_on_rails_pro/utils.rbreact_on_rails_pro/lib/react_on_rails_pro/configuration.rb
📚 Learning: 2025-04-26T21:55:55.874Z
Learnt from: alexeyr-ci2
Repo: shakacode/react_on_rails PR: 1732
File: spec/dummy/client/app-react16/startup/ReduxSharedStoreApp.client.jsx:40-44
Timestamp: 2025-04-26T21:55:55.874Z
Learning: In the react_on_rails project, files under `app-react16` directories are copied/moved to corresponding `/app` directories during the conversion process (removing the `-react16` suffix), which affects their relative import paths at runtime.
Applied to files:
react_on_rails_pro/CHANGELOG.mdreact_on_rails_pro/react_on_rails_pro.gemspecreact_on_rails_pro/lib/react_on_rails_pro/utils.rb
📚 Learning: 2025-02-12T16:38:06.537Z
Learnt from: Romex91
Repo: shakacode/react_on_rails PR: 1697
File: package-scripts.yml:28-28
Timestamp: 2025-02-12T16:38:06.537Z
Learning: The file `node_package/lib/ReactOnRails.full.js` is autogenerated during the build process and should not be present in the repository.
Applied to files:
react_on_rails_pro/CHANGELOG.mdreact_on_rails_pro/react_on_rails_pro.gemspecreact_on_rails_pro/.gitignorereact_on_rails_pro/lib/react_on_rails_pro/utils.rb
📚 Learning: 2025-02-13T16:50:47.848Z
Learnt from: AbanoubGhadban
Repo: shakacode/react_on_rails PR: 1644
File: node_package/src/clientStartup.ts:18-21
Timestamp: 2025-02-13T16:50:47.848Z
Learning: In the react_on_rails module, the `reactOnRailsPageUnloaded` function in clientStartup.ts is intentionally kept private as it's only used internally as a callback for `onPageUnloaded`.
Applied to files:
react_on_rails_pro/CHANGELOG.md
📚 Learning: 2025-09-15T21:24:48.207Z
Learnt from: AbanoubGhadban
Repo: shakacode/react_on_rails PR: 1781
File: node_package/src/ClientSideRenderer.ts:82-95
Timestamp: 2025-09-15T21:24:48.207Z
Learning: In React on Rails, the force_load feature includes both explicit `data-force-load="true"` usage and the ability to hydrate components during the page loading state (`document.readyState === 'loading'`). Both capabilities require a Pro license, so the condition `!railsContext.rorPro && (isComponentForceLoaded || document.readyState === 'loading')` correctly gates both scenarios.
Applied to files:
react_on_rails_pro/CHANGELOG.mdreact_on_rails_pro/react_on_rails_pro.gemspecreact_on_rails_pro/.gitignore
📚 Learning: 2025-10-23T17:22:01.074Z
Learnt from: AbanoubGhadban
Repo: shakacode/react_on_rails PR: 1875
File: lib/react_on_rails/utils.rb:112-124
Timestamp: 2025-10-23T17:22:01.074Z
Learning: In React on Rails, when Pro is installed but not licensed, the intended behavior is to raise an error on boot. The `react_on_rails_pro?` method validates licenses and should raise errors early (including during path resolution in methods like `server_bundle?`) to enforce licensing requirements rather than failing later with obscure errors.
Applied to files:
react_on_rails_pro/CHANGELOG.mdreact_on_rails_pro/react_on_rails_pro.gemspecreact_on_rails_pro/.gitignorereact_on_rails_pro/lib/react_on_rails_pro/configuration.rb
📚 Learning: 2025-02-18T13:08:01.477Z
Learnt from: AbanoubGhadban
Repo: shakacode/react_on_rails PR: 1644
File: lib/react_on_rails/helper.rb:190-197
Timestamp: 2025-02-18T13:08:01.477Z
Learning: RSC support validation is handled in deeper level calls of the React on Rails Pro codebase, so it doesn't need to be validated again in the `rsc_payload_react_component` helper method.
Applied to files:
react_on_rails_pro/CHANGELOG.mdreact_on_rails_pro/lib/react_on_rails_pro/configuration.rb
📚 Learning: 2025-07-08T05:57:29.630Z
Learnt from: AbanoubGhadban
Repo: shakacode/react_on_rails PR: 1745
File: node_package/src/RSCRequestTracker.ts:8-14
Timestamp: 2025-07-08T05:57:29.630Z
Learning: The global `generateRSCPayload` function in React on Rails Pro (RORP) is provided by the framework during rendering requests, not implemented in application code. The `declare global` statements are used to document the expected interface that RORP will inject at runtime.
Applied to files:
react_on_rails_pro/CHANGELOG.mdreact_on_rails_pro/lib/react_on_rails_pro/utils.rb
🧬 Code graph analysis (3)
react_on_rails_pro/spec/dummy/spec/helpers/react_on_rails_pro_helper_spec.rb (1)
react_on_rails_pro/lib/react_on_rails_pro/concerns/stream.rb (1)
stream_view_containing_react_components(33-46)
react_on_rails_pro/lib/react_on_rails_pro/concerns/stream.rb (1)
react_on_rails_pro/lib/react_on_rails_pro/configuration.rb (1)
configuration(9-38)
react_on_rails_pro/spec/react_on_rails_pro/stream_spec.rb (2)
react_on_rails_pro/lib/react_on_rails_pro/stream_request.rb (4)
initialize(4-64)initialize(5-13)initialize(66-149)initialize(67-69)react_on_rails_pro/lib/react_on_rails_pro/concerns/stream.rb (1)
stream_view_containing_react_components(33-46)
⏰ 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). (6)
- GitHub Check: build-dummy-app-webpack-test-bundles
- GitHub Check: rspec-package-specs (3.3.7)
- GitHub Check: build-dummy-app-webpack-test-bundles
- GitHub Check: lint-js-and-ruby
- GitHub Check: claude-review
- GitHub Check: markdown-link-check
🔇 Additional comments (7)
react_on_rails_pro/lib/react_on_rails_pro/utils.rb (1)
111-121: Documentation correctly updated to reflect Shakapacker.The comment changes accurately replace outdated "webpacker" references with "Shakapacker" to align with the current bundler. No functional changes present. Other "webpacker" references in the codebase are appropriately scoped to test files or serve a specific purpose (e.g., the doctor module's deprecation check for Webpacker v16 removal), so they do not conflict with this documentation update.
react_on_rails_pro/spec/react_on_rails_pro/stream_spec.rb (3)
7-31: Well-designed test double for streaming.The
StreamControllerprovides a clean, minimal implementation for testing concurrent streaming behavior. The fiber-based approach correctly mirrors the production streaming model.
374-394: Clean test helpers for async streaming.The
run_streamandsetup_stream_testhelpers provide good abstractions for testing concurrent streaming scenarios. The use ofSyncproperly wraps async operations.
396-487: Consider CI environment robustness for timing-based tests.These tests rely on
sleepcalls (0.05s-1s) to control timing assertions. While acceptable for streaming tests, consider whether these values are sufficient for slower CI environments. If tests become flaky, you may need to increase sleep durations or use more robust wait mechanisms.react_on_rails_pro/spec/dummy/spec/helpers/react_on_rails_pro_helper_spec.rb (3)
332-361: Good backward-compatible enhancement.The dual handling of
Async::QueueandArrayinputs maintains backward compatibility while enabling async streaming tests. The queue dequeue loop with nil termination follows the expected pattern.
452-470: Effective helper for testing async streaming.The
execute_stream_view_containing_react_componentshelper provides a good abstraction for testing the async streaming flow. The progressive enqueueing with timing checks validates that chunks are written immediately upon receipt.Note that the
sleep 0.05calls and the assertion at line 465 create a timing dependency that could be flaky in slower environments.
472-515: Tests successfully adapted to async streaming.The tests have been cleanly migrated to use the new async streaming helper while maintaining comprehensive coverage of Rails context, component specification tags, and view content rendering.
The .claude/ directory is created at the project root, not in subdirectories. The root .gitignore already handles .claude/ with selective ignoring (settings.local.json and .fuse_hidden*), making this subdirectory entry unnecessary and inconsistent with the root ignore policy.
…_size Wire up validate_concurrent_component_streaming_buffer_size in setup_config_values to ensure config.concurrent_component_streaming_buffer_size is validated at initialization time. Without this call, invalid values (nil, 0, negative numbers) would be silently accepted and cause runtime errors later when Async::LimitedQueue is instantiated. Addresses CodeRabbit feedback on the original PR.
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: 1
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
react_on_rails_pro/lib/react_on_rails_pro/configuration.rb(7 hunks)
🧰 Additional context used
🧠 Learnings (4)
📚 Learning: 2025-02-18T13:08:01.477Z
Learnt from: AbanoubGhadban
Repo: shakacode/react_on_rails PR: 1644
File: lib/react_on_rails/helper.rb:190-197
Timestamp: 2025-02-18T13:08:01.477Z
Learning: RSC support validation in React on Rails Pro is handled through a chain of validations:
1. Pro version check in `run_stream_inside_fiber`
2. RSC support check during pack generation via `ReactOnRailsPro.configuration.enable_rsc_support`
3. RSC support validation during component registration
This makes additional validation in the helper methods unnecessary.
Applied to files:
react_on_rails_pro/lib/react_on_rails_pro/configuration.rb
📚 Learning: 2025-09-16T08:01:11.146Z
Learnt from: justin808
Repo: shakacode/react_on_rails PR: 1770
File: lib/generators/react_on_rails/templates/base/base/app/javascript/src/HelloWorld/ror_components/HelloWorld.client.jsx:2-2
Timestamp: 2025-09-16T08:01:11.146Z
Learning: React on Rails uses webpack CSS Modules configuration with namedExports: true, which requires the import syntax `import * as style from './file.module.css'` rather than the default export pattern. This configuration enables better tree shaking and bundle size optimization for CSS modules.
Applied to files:
react_on_rails_pro/lib/react_on_rails_pro/configuration.rb
📚 Learning: 2025-02-18T13:08:01.477Z
Learnt from: AbanoubGhadban
Repo: shakacode/react_on_rails PR: 1644
File: lib/react_on_rails/helper.rb:190-197
Timestamp: 2025-02-18T13:08:01.477Z
Learning: RSC support validation is handled in deeper level calls of the React on Rails Pro codebase, so it doesn't need to be validated again in the `rsc_payload_react_component` helper method.
Applied to files:
react_on_rails_pro/lib/react_on_rails_pro/configuration.rb
📚 Learning: 2025-10-23T17:22:01.074Z
Learnt from: AbanoubGhadban
Repo: shakacode/react_on_rails PR: 1875
File: lib/react_on_rails/utils.rb:112-124
Timestamp: 2025-10-23T17:22:01.074Z
Learning: In React on Rails, when Pro is installed but not licensed, the intended behavior is to raise an error on boot. The `react_on_rails_pro?` method validates licenses and should raise errors early (including during path resolution in methods like `server_bundle?`) to enforce licensing requirements rather than failing later with obscure errors.
Applied to files:
react_on_rails_pro/lib/react_on_rails_pro/configuration.rb
⏰ 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). (6)
- GitHub Check: build
- GitHub Check: build-dummy-app-webpack-test-bundles
- GitHub Check: build-dummy-app-webpack-test-bundles
- GitHub Check: lint-js-and-ruby
- GitHub Check: claude-review
- GitHub Check: markdown-link-check
🔇 Additional comments (5)
react_on_rails_pro/lib/react_on_rails_pro/configuration.rb (5)
63-63: LGTM!The default buffer size of 64 is reasonable for concurrent component streaming. Since this is configurable, users can tune it based on their specific workload requirements.
73-73: LGTM!The attribute accessor is properly added following the established pattern for configuration options.
84-86: LGTM!The parameter is properly added to the constructor with appropriate default value, and the assignment follows the established pattern. Adding it at the end maintains backward compatibility.
Also applies to: 112-112
121-121: LGTM!The validation is properly invoked during configuration setup, following the established pattern for other configuration validations.
35-36: LGTM!The new configuration option is properly integrated into the default configuration instantiation, maintaining consistency with other configuration parameters.
Change concurrent_component_streaming_buffer_size validation to require Integer instead of Numeric. Buffer sizes represent discrete queue capacity counts and should not accept float values like 64.5. This provides better type safety and clearer error messages for users who might accidentally pass non-integer values. Addresses CodeRabbit feedback.
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.
Thank you for addressing the validation issue! The change from Numeric to Integer ensures buffer sizes are whole numbers as intended. The implementation looks good! ✨
Code ReviewThank you for this PR! This is a significant improvement to the streaming infrastructure. I've reviewed the changes and have the following feedback: ✅ Strengths
🔍 Issues & Suggestions1. CRITICAL: Client Disconnect Handling
|
|
@ihabadham respond with why you don't want to address the review comments. CC: @AbanoubGhadban |
Implements #550
Example:
Summary by CodeRabbit
Improvements
Added
concurrent_component_streaming_buffer_size(default: 64) to tune memory vs. performance.Tests
Migrated from: shakacode/react_on_rails_pro#552
This change is