Collaboration: Replace post meta storage with dedicated database table#11256
Collaboration: Replace post meta storage with dedicated database table#11256josephfusco wants to merge 81 commits intoWordPress:trunkfrom
Conversation
wp_collaboration
Test using WordPress PlaygroundThe changes in this pull request can previewed and tested using a WordPress Playground instance. WordPress Playground is an experimental project that creates a full WordPress instance entirely within the browser. Some things to be aware of
For more details about these limitations and more, check out the Limitations page in the WordPress Playground documentation. |
c08b703 to
693c813
Compare
Introduces the wp_collaboration table for storing real-time editing data (document states, awareness info, undo history) and the WP_Collaboration_Table_Storage class that implements all CRUD operations against it. Bumps the database schema version to 61840.
Replaces WP_HTTP_Polling_Sync_Server with WP_HTTP_Polling_Collaboration_Server using the wp-collaboration/v1 REST namespace. Switches to string-based client IDs, fixes the compaction race condition, adds a backward-compatible wp-sync/v1 route alias, and uses UPDATE-then-INSERT for awareness data.
Deletes WP_Sync_Post_Meta_Storage and WP_Sync_Storage interface, and removes the wp_sync_storage post type registration from post.php. These are superseded by the dedicated collaboration table.
Adds wp_is_collaboration_enabled() gate, injects the collaboration setting into the block editor, registers cron event for cleaning up stale collaboration data, and updates require/include paths for the new storage and server classes.
Adds 67 PHPUnit tests for WP_HTTP_Polling_Collaboration_Server covering document sync, awareness, undo/redo, compaction, permissions, cursor mechanics, race conditions, cron cleanup, and the backward-compatible wp-sync/v1 route. Adds E2E tests for 3-user presence, sync, and undo/redo. Removes the old sync server tests. Updates REST schema setup and fixtures for the new collaboration endpoints.
87fc57a to
886f0b1
Compare
Adds a cache-first read path to get_awareness_state() following the transient pattern: check the persistent object cache, fall back to the database on miss, and prime the cache with the result. set_awareness_state() updates the cached entries in-place after the DB write rather than invalidating, so the cache stays warm for the next reader in the room. This is application-level deduplication: the shared collaboration table cannot carry a UNIQUE KEY on (room, client_id) because sync rows need multiple entries per room+client pair. Sites without a persistent cache see no behavior change — the in-memory WP_Object_Cache provides no cross-request benefit but keeps the code path identical.
Restore the `wp_client_side_media_processing_enabled` filter and the `finalize` route that were accidentally removed from the REST schema test. Add the `collaboration` table to the list of tables expected to be empty after multisite site creation.
The connectors API key entries in wp-api-generated.js were incorrectly carried over during the trunk merge. Trunk does not include them in the generated fixtures since the settings are dynamically registered and not present in the CI test context.
5140e44 to
09d0b86
Compare
|
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 Core Committers: Use this line as a base for the props when committing in SVN: To understand the WordPress project's expectations around crediting contributors, please review the Contributor Attribution page in the Core Handbook. |
Rename the `update_value` column to `data` in the collaboration table storage class and tests, and fix array arrow alignment to satisfy PHPCS. The shorter name is consistent with WordPress meta tables and avoids confusion with the `update_value()` method in `WP_REST_Meta_Fields`.
Add a composite index on (type, client_id) to the collaboration table to speed up awareness upserts, which filter on both columns. Bump $wp_db_version from 61840 to 61841 so existing installations pick up the schema change via dbDelta on upgrade.
1a44948 to
d4e27d4
Compare
|
Carrying over props from original PR: |
Introduce MAX_BODY_SIZE (16 MB), MAX_ROOMS_PER_REQUEST (50), and MAX_UPDATE_DATA_SIZE (1 MB) constants to cap request payloads. Wire a validate_callback on the route to reject oversized request bodies with a 413, add maxItems to the rooms schema, and replace the hardcoded maxLength with the new constant.
Reject non-numeric object IDs early in can_user_collaborate_on_entity_type(). Verify that a post's actual type matches the room's claimed entity name before granting access. For taxonomy rooms, confirm the term exists in the specified taxonomy and simplify the capability check to use assign_term with the term's object ID.
Cover oversized request body (413), exceeding max rooms (400), non-numeric object ID, post type mismatch, nonexistent taxonomy term, and term in the wrong taxonomy.
peterwilsoncc
left a comment
There was a problem hiding this comment.
A few notes from my first pass are inline. There will probably be more passes as I continuing reviewing the code and testing the functionality.
/*
* For multi-line comments the WordPress Coding Standard
* is to write them like this rather than with consecutive lines
* beginning with a `//`.
*
* This applies in a few places so I haven't dropped an inline comment.
*/
…rage Convert consecutive single-line comments to block comment style per WordPress coding standards, replace forward slashes with colons in cache keys to avoid ambiguity, hoist `global $wpdb` above the cache check in `get_awareness_state()`, and clarify the `$cursor` param docblock in `remove_updates_before_cursor()`.
When collaboration is disabled, run both DELETE queries (sync and awareness rows) before unscheduling the cron hook so leftover data is removed. Hoist `global $wpdb` to the top of the function so the disabled branch can use it. Add a comment noting future persistent types may also need exclusion from the sync cleanup query.
peterwilsoncc
left a comment
There was a problem hiding this comment.
I've added a few notes inline.
For the test suite (I've only noted it once inline but it applies elsewhere), I recommend using the pre_option_{$option} filter whenever possible to ensure that it gets reset by the test suite. The filter requires the use of __return_zero as returning false doesn't work for WP reasons.
I took the liberty of pushing the required changes to tests/qunit/fixtures/wp-api-generated.js to get the tests passing.
|
Oh, forgot to mention: for the cron job specific tests, I'd recommend moving them to a separate file rather than keep them in the REST endpoints file. |
e196ebd to
d95a339
Compare
Co-Authored-By: westonruter <westonruter@git.wordpress.org>
|
@josephfusco I've pushed an update to your PR to rename the ID column to |
Co-authored-by: Peter Wilson <519727+peterwilsoncc@users.noreply.github.com>
Adopt the approach from PR#11599: default awareness timestamp bucketing to 10 seconds (up from 5) with a filter for hosts to adjust. Matches the wp_sync_awareness_timestamp_granularity filter and guards against zero values.
…pdate. Replace the inline cache update logic with a simple cache delete, letting the next get_awareness_state() call re-read from the database. This keeps cache-building logic in a single location.
The database schema requirement is a system-level constraint, not a user preference. Move it from wp_is_collaboration_enabled() to wp_is_collaboration_allowed() where it belongs.
Remove redundant DELETE in set_up() as transaction rollback handles cleanup. Use REST_TESTS_IMPOSSIBLY_HIGH_NUMBER for nonexistent post ID test.
Add message parameters to bare assertions for easier failure
identification. Replace update_option('db_version') with
pre_option_db_version filter so the value auto-restores on
test teardown.
Separate cron cleanup tests from the REST endpoint test file into tests/phpunit/tests/collaboration/collaborationCronCleanup.php.
|
Apologies for the late replies on a couple of threads: Client ID reactivation after 60s:
Yes — the awareness row expires via the TTL filter, and the next poll re-inserts it. Covered by Separate awareness DELETE (60s cleanup):
Technically true, but without the 60-second cleanup, awareness rows from abandoned sessions linger for 7 days. Keeping the separate DELETE bounds the table. |
Remove blank line before closing brace in set_up() and expand inline closures to multi-line format per WordPress coding standards.
Port fix from PR#11716 by @alecgeatches. When two offline users reconnect with the same cursor, the second compaction was silently dropped, causing permanent YDoc divergence. Store the bytes as a regular update so Yjs can merge them idempotently.
# Conflicts: # src/wp-includes/collaboration/class-wp-http-polling-collaboration-server.php # tests/phpunit/tests/rest-api/rest-sync-server.php
Port test improvements and db_version check refactor from WordPress#11256
The real-time collaboration sync layer currently stores messages as post meta, which creates side effects at scale. This moves it to a single dedicated
wp_collaborationtable purpose-built for the workload.Table Definition
Testing
References
Trac ticket: https://core.trac.wordpress.org/ticket/64696
PR with prior work and feedback (2 table approach): #11068
Use of AI Tools
Co-authored with Claude Code (Opus 4.6), used to synthesize discussion across related tickets and PRs into a single implementation. All code was reviewed and tested before submission.
This Pull Request is for code review only. Please keep all other discussion in the Trac ticket. Do not merge this Pull Request. See GitHub Pull Requests for Code Review in the Core Handbook for more details.