RTC: Attach sync observers after hydrating persisted CRDT doc#77966
RTC: Attach sync observers after hydrating persisted CRDT doc#77966maxschmeling merged 2 commits intotrunkfrom
Conversation
Loading an entity ran the record-map observer's _updateEntityRecord on the same applyUpdateV2 that hydrated the live ydoc from the persisted CRDT doc. Because the live ydoc has no CRDT_DOC_META_PERSISTENCE_KEY, the blocks filter in getPostChangesFromCRDTDoc unconditionally reports blocks as changed, which then triggered a redundant deserializeBlockAttributes pass and an editRecord dispatch whose blocks already matched the editor's parsed content. Attaching observers after applyPersistedCrdtDoc avoids the spurious fire. Providers connect asynchronously, so no peer update can land in the synchronous window before the observers are attached. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
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: +1 B (0%) Total Size: 7.95 MB 📦 View Changed
ℹ️ View Unchanged
|
|
Flaky tests detected in 9fa0f4a. 🔍 Workflow run URL: https://github.com/WordPress/gutenberg/actions/runs/25416735627
|
Loading an entity ran the record-map observer's _updateEntityRecord on the same applyUpdateV2 that hydrated the live ydoc from the persisted CRDT doc. Because the live ydoc has no CRDT_DOC_META_PERSISTENCE_KEY, the blocks filter in getPostChangesFromCRDTDoc unconditionally reports blocks as changed, which then triggered a redundant deserializeBlockAttributes pass and an editRecord dispatch whose blocks already matched the editor's parsed content. Attaching observers after applyPersistedCrdtDoc avoids the spurious fire. Providers connect asynchronously, so no peer update can land in the synchronous window before the observers are attached. Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
I just cherry-picked this PR to the release/23.1 branch to get it included in the next release: 61c1dde |
What?
In
loadEntity, attach the Yjs observers after we hydrate the document, not before.Why?
Loading a post does two things to the live Yjs doc:
The problem: hydration itself looks like a change to the observer, so it fires on load. That kicks off
_updateEntityRecord, which re-parses every block and dispatches aneditRecord— even though the just-hydrated content already matches what the editor parsed fromrecord.content.How?
Attach the observer after hydration. The hydration update happens with no listener, so nothing reacts to it. Real peer updates that arrive later still fire the observer normally.
This is safe because between provider setup and observer attach, all code is synchronous — peer updates can only arrive in async callbacks, which run later.
Testing Instructions
_updateEntityRecordanddeserializeBlockAttributesno longer appear in the trace on initial load. (One earliergetChangesFromCRDTDoccall inside_applyPersistedCrdtDocis expected and correct.)Use of AI Tools
Authored with assistance from Claude Code.
Before:

After:
