Proof that sourceValue has a race condition #679
Draft
+1,099
−0
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.
Onyx sourceValue issues
These tests demonstrate and prove multiple issues with Onyx sourceValue handling:
sourceValue
visibleuseSidebarOrderedReports
conditional logic ignores availablesourceValues
sourceValue
preserves the keys of the latest onyx update during unrelated rerendersSee the thread in #quality for more info
Test Files
simpleSourceValueRaceConditionDemo.ts
- Pure race condition test proving batching loses intermediatesourceValues
useSidebarOrderedReportsVulnerability.ts
- Logic bug and compound issue tests replicating production patternsstaleSourceValueTest
- Test demonstrating that sourceValue persists during unrelated renders, leading to unnecessary cache bustingHow to Run the Tests
The race condition test and what it proves
The Race Condition Mechanism
Multiple Discrete Updates: The test performs 3 separate Onyx operations:
Onyx.merge(collection_item1)
- Add first collection itemOnyx.merge(collection_item2)
- Add second collection itemOnyx.merge(collection_item3)
- Add third collection itemReact Batching: Due to
unstable_batchedUpdates
and setTimeout-based batching in Onyx, all updates get batched into a single renderHow this works internally:
batchUpdatesQueue
(inOnyxUtils.ts
)maybeFlushBatchUpdates()
usessetTimeout(0)
to defer processing to the next event loop tickunstable_batchedUpdates(() => { updatesCopy.forEach(applyUpdates) })
wraps all queued updatessourceValue
gets set by the first update, then overwritten by subsequent updates, but only the final state is visible to the componentLost sourceValue Information: Only the first
sourceValue
is visible to the component, losing information about subsequent updatesExpected vs Actual Behavior
Expected (without race condition):
Actual (with race condition):
Test Output Example
When you run the simple demo test, you'll see output like:
Technical Deep Dive: The Batching Mechanism
Where
unstable_batchedUpdates
is CalledThe race condition is caused by Onyx's internal batching mechanism in
lib/OnyxUtils.ts
:Why This Causes the Race Condition
Onyx.merge()
call adds an update tobatchUpdatesQueue
unstable_batchedUpdates
executes all updates synchronously within React's batching contextsourceValue
assignment survives the batching processThis is why the test demonstrates that 3 discrete updates result in only 1
sourceValue
being visible to components.