-
-
Notifications
You must be signed in to change notification settings - Fork 633
Fix async props race condition with lazy AsyncPropsManager initialization #2297
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: upcoming-v16.3.0
Are you sure you want to change the base?
Fix async props race condition with lazy AsyncPropsManager initialization #2297
Conversation
Implements lazy initialization pattern for AsyncPropsManager to handle race conditions between initial render requests and update chunks. The new getOrCreateAsyncPropsManager function: - Accepts sharedExecutionContext as parameter - Creates or retrieves existing AsyncPropsManager instance - Ensures same manager is used regardless of which code executes first This fixes the bug where commenting out sleep statements in test_incremental_rendering.html.erb caused async props not to be sent, because update chunks were arriving before the manager was set up. Co-Authored-By: Claude Opus 4.5 <[email protected]>
- Remove unused log import (replaced by console.log for debugging) - Change ++ operators to += 1 to satisfy no-plusplus rule Co-Authored-By: Claude Opus 4.5 <[email protected]>
|
Important Review skippedAuto reviews are disabled on base/target branches other than the default branch. Please check the settings in the CodeRabbit UI or the You can disable this status message by setting the ✨ Finishing touches🧪 Generate unit tests (beta)
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: Fix async props race condition with lazy AsyncPropsManager initializationSummaryThis PR effectively addresses the race condition described in #2270 by implementing lazy initialization of ✅ Strengths1. Excellent Architecture & Documentation
2. Clean Lazy Initialization Pattern
3. Minimal API Surface Changes
4. Good Error Handling
|
size-limit report 📦
|
5efcf80 to
455e7d4
Compare
Greptile OverviewGreptile SummaryThis PR successfully addresses a race condition in async props handling where update chunks could arrive before Key ChangesCore Fix:
Architecture: Issues FoundCritical Issue: Code Quality:
Race Condition ResolutionThe lazy initialization pattern correctly handles all timing scenarios:
The implementation is thread-safe because Confidence Score: 2/5
Important Files ChangedFile Analysis
Sequence DiagramsequenceDiagram
participant Rails as Rails Server
participant Worker as Node Worker
participant VM as VM Context
participant Manager as AsyncPropsManager
participant React as React Component
Note over Rails,React: Race Condition Fix: Lazy Initialization Pattern
Rails->>Worker: POST /incremental-render<br/>(First NDJSON line: renderingRequest)
Worker->>VM: handleIncrementalRenderRequest()
VM->>VM: buildExecutionContext()<br/>Creates sharedExecutionContext Map
Note over VM,Manager: INITIAL RENDER PATH
VM->>VM: Execute renderingRequest JS
VM->>Manager: getOrCreateAsyncPropsManager(sharedExecutionContext)
alt Manager doesn't exist
Manager->>Manager: new AsyncPropsManager()
Manager->>VM: Store in sharedExecutionContext["asyncPropsManager"]
end
VM->>React: addAsyncPropsCapabilityToComponentProps(props, sharedExecutionContext)
React->>Manager: getReactOnRailsAsyncProp("users")
Manager->>React: Returns Promise (suspends)
React-->>Worker: Initial HTML shell
par Update Chunks Can Arrive Anytime
Note over Rails,Manager: UPDATE CHUNK PATH (can race with initial render)
Rails->>Worker: NDJSON line 2: updateChunk for "users"
Worker->>VM: sink.add(updateChunk)
VM->>VM: Execute updateChunk JS
VM->>Manager: getOrCreateAsyncPropsManager(sharedExecutionContext)
alt Manager already exists
Manager->>Manager: Return existing instance
else Manager doesn't exist yet (race condition)
Manager->>Manager: new AsyncPropsManager()
Manager->>VM: Store in sharedExecutionContext["asyncPropsManager"]
end
VM->>Manager: setProp("users", userData)
Manager->>React: Resolve Promise
React-->>Worker: More HTML chunks
end
Rails->>Worker: Close HTTP request
Worker->>VM: sink.handleRequestClosed()
VM->>Manager: getOrCreateAsyncPropsManager(sharedExecutionContext)
VM->>Manager: endStream()
Manager->>Manager: Reject unresolved promises
React-->>Worker: Final HTML chunks
|
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.
1 file reviewed, 1 comment
Additional Comments (1)
Old (what this fixture uses): var { props: propsWithAsyncProps, asyncPropManager } =
ReactOnRails.addAsyncPropsCapabilityToComponentProps(usedProps);New (what it should be): var { props: propsWithAsyncProps } =
ReactOnRails.addAsyncPropsCapabilityToComponentProps(usedProps, sharedExecutionContext);The new signature:
This fixture needs to be updated to match the new API, otherwise tests using this fixture will fail. Prompt To Fix With AIThis is a comment left during a code review.
Path: packages/react-on-rails-pro-node-renderer/tests/fixtures/projects/spec-dummy/asyncComponentsTreeForTestingRenderingRequest.js
Line: 23:25
Comment:
This test fixture uses the OLD API signature for `addAsyncPropsCapabilityToComponentProps`, which is incompatible with the changes in this PR. The function signature has changed:
**Old (what this fixture uses):**
```javascript
var { props: propsWithAsyncProps, asyncPropManager } =
ReactOnRails.addAsyncPropsCapabilityToComponentProps(usedProps);
```
**New (what it should be):**
```javascript
var { props: propsWithAsyncProps } =
ReactOnRails.addAsyncPropsCapabilityToComponentProps(usedProps, sharedExecutionContext);
```
The new signature:
1. Requires `sharedExecutionContext` as a second parameter
2. No longer returns `asyncPropManager` (it's managed internally via `getOrCreateAsyncPropsManager`)
3. The manual `sharedExecutionContext.set("asyncPropsManager", asyncPropManager)` line is no longer needed
```suggestion
if (ReactOnRails.isRSCBundle) {
var { props: propsWithAsyncProps } = ReactOnRails.addAsyncPropsCapabilityToComponentProps(usedProps, sharedExecutionContext);
usedProps = propsWithAsyncProps;
}
```
This fixture needs to be updated to match the new API, otherwise tests using this fixture will fail.
How can I resolve this? If you propose a fix, please make it concise. |
Code Review - PR #2297SummaryThis PR fixes a race condition in async props handling by implementing lazy initialization of the AsyncPropsManager. The core issue is that update chunks could arrive before the initial render creates the manager, causing async props to be lost. ✅ Strengths1. Solid Architecture - Lazy Initialization PatternThe
2. Excellent DocumentationThe JSDoc comments clearly explain:
This is particularly well done in 3. Type SafetyProper TypeScript types added to 4. Backward CompatibilityThe change is internal - no breaking changes to the public API.
|
Remove debugging logs from: - AsyncPropsManager.ts - handleIncrementalRenderRequest.ts - handleIncrementalRenderStream.ts Co-Authored-By: Claude Opus 4.5 <[email protected]>
455e7d4 to
30c296d
Compare
Code Review - PR #2297This PR successfully addresses a race condition in async props delivery by implementing lazy initialization of AsyncPropsManager. The fix is well-architected and follows good engineering principles. Strengths
Critical Issue: Missing Test CoverageThe existing test (test_incremental_rendering.html.erb) uses sleep 1 which MASKS the race condition this PR fixes. You need a test that exercises the race condition without sleep delays. Please add a test that emits props immediately to verify the fix prevents regression. Other Suggestions
VerdictApproved with suggestions. Core fix is solid, but please add the automated test before merging. Code Quality: 8.5/10 Great work identifying and fixing this subtle timing issue! |
Code Review - PR 2297Overall AssessmentStrong fix for a critical race condition. The implementation is well-thought-out and follows good engineering practices. Strengths
Issues and RecommendationsCRITICAL: Missing Test CoverageThe PR has NO tests for the race condition fix. This is a serious gap for a concurrency issue. Required tests:
The test plan checkbox in the PR description is unfilled. This needs local testing before merge per CLAUDE.md guidelines. MODERATE: RSpec Test Needs UpdateThe existing test at react_on_rails_pro/spec/react_on_rails_pro/server_rendering_js_code_spec.rb lines 38-39 and 84 checks for the OLD implementation and will likely fail because asyncPropManager variable no longer exists in generated JS. Security and Performance
Before Merging Checklist
ConclusionThis is a solid fix for a real production issue, but it needs test coverage before merging. The race condition is subtle and could easily regress without tests. Recommendation: Request changes for test coverage, then approve once tests are added. Great work identifying and solving this timing issue! |
Async props requires RSC bundle because getOrCreateAsyncPropsManager is only implemented there (server bundle has a stub that throws). Co-Authored-By: Claude Opus 4.5 <[email protected]>
Code Review FeedbackOverall AssessmentThis PR effectively solves a critical race condition in async props handling. The lazy initialization pattern is a solid architectural choice that properly addresses the core issue where update chunks could arrive before the AsyncPropsManager was initialized. Strengths
CRITICAL Issues Found1. Outdated Test Assertion File: react_on_rails_pro/spec/react_on_rails_pro/async_props_emitter_spec.rb:23 The test on line 23 still expects the OLD code pattern:
But the implementation NOW uses:
This test will FAIL! It needs to be updated to expect the new function call pattern. 2. Missing Test Coverage The PR test plan mentions testing stream_react_component_with_async_props without sleep delays, but:
Recommendation: Add an integration test that calls stream_react_component_with_async_props with NO sleep delays and verifies async props are delivered correctly. 3. Type Safety Issue File: packages/react-on-rails-pro/src/AsyncPropsManager.ts:158 The type assertion assumes the value is either AsyncPropsManager or undefined, but Map.get() could return ANY value if something else pollutes the key. Consider adding an instanceof check for defensive programming against accidental key collisions. Security & PerformanceSecurity:
Performance:
Required Actions Before Merge
Status: Needs work - Fix the test assertion before merging. Otherwise, solid implementation of the race condition fix. Generated with Claude Code |
Summary
AsyncPropsManageris initializedgetOrCreateAsyncPropsManager(sharedExecutionContext)for lazy initializationFixes #2270
Test plan
stream_react_component_with_async_propswithout sleep delays before emit calls🤖 Generated with Claude Code