Repository navigation
Core Data: Discard entity list responses superseded by a newer request - #81886
Draft
adamsilverstein wants to merge 3 commits into
Draft
adamsilverstein wants to merge 3 commits into
adamsilverstein wants to merge 3 commits into
Conversation
getEntityRecords kept whichever list response was delivered last, even when its body was read from the database before a newer one. A batch upload invalidates the attachment list once per file, so several list refetches are in flight together and a stale one delivered late could replace the fresher list, overwrite the records it contains, and mark their individual resolutions finished. Reserve a sequence number before each request goes out - as getEntityRecord already does - and drop a response once a newer one for the same query has been received, including the remaining pages of a superseded progressive run.
Hold the inserter Media tab's list request at the network layer, upload an image while its stale body is in flight, let the post-upload refetch deliver a fresher list, and only then deliver the stale one. The fresher list must survive, so the Media tab keeps showing the new upload.
|
The following accounts have interacted with this PR and/or linked issues. I will continue to update these lists as activity occurs. You can also manually ask me to refresh this list by adding the If you're merging code through a pull request on GitHub, copy and paste the following into the bottom of the merge commit message. To understand the WordPress project's expectations around crediting contributors, please review the Contributor Attribution page in the Core Handbook. |
|
Size Change: +4 B (0%) Total Size: 7.75 MB 📦 View Changed
|
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
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.
Claude took on the follow-up it flagged in #81846, fix and tests below:
Fixes #81885
Note
Stacked on #81846, which must land first - only the last two commits here are new.
What?
getEntityRecordsnow discards a list response once a newer response for the same query has already been received - the list counterpart of the record guard added in #81846.Why?
invalidateResolutionstarts a second request for the same list while the first may still be in flight, and the store keeps whichever response is delivered last. A list read from the database before an update can therefore replace the refetched one that already reflects it - and nothing invalidates it afterwards.Batch uploads are where this bites. Uploading several files at once invalidates the attachment list once per file, so several list refetches are in flight together, and the odds of one arriving out of order grow with every extra file. A stale one delivered late makes an already-uploaded image vanish from the inserter's Media tab.
The damage also reaches past the list itself. The resolver writes every record it received through
receiveEntityRecordsand ends by callingfinishResolutions( 'getEntityRecord', ... )for each of them, so a stale list response can overwrite fresher individual records and mark their resolutions finished. That write never goes through thegetEntityRecordresolver, so the per-record guard from #81846 cannot catch it.As with #81846: Playground serializes PHP requests, so responses cannot arrive out of order there - this only reproduces on real installations.
How?
The same mechanism as #81846 - reserve a sequence number before the request goes out, and drop the response once a newer one for the same query has been received.
getEntityRecordsneeded three decisions on top of that:finishResolutionsandreceiveUserPermissionscalls are covered along with the list write.receiveEntityRecordsper page, so each page checks the guard before delivering. Pages a superseded run already delivered were overwritten by the newer response's deliveries, so stopping is enough.The guard keeps the conservative properties from #81846: only responses to an identical query are compared, so a different
_fieldsset or query never discards another one's data, and a newer request that fails discards nothing.Testing Instructions
All tests were verified to fail without the change and pass with it.
Unit - the ordering guard across the fetch branches:
E2e - the race reproduced deterministically against a real editor. It holds the inserter Media tab's attachment list request at the network layer (reading its stale body immediately), uploads a new image while it is held, lets the post-upload refetch deliver a fresher list, and only then delivers the stale one. The fresher list must survive in the store, and the Media tab must keep showing the new upload:
Manual reproduction needs an unlucky response ordering on a busy installation - see the reproduction notes in the issue.
AI Use
Claude Code did the typing here, I did the asking. I will review and test.