Skip to content

An open editor makes every cached post query stale on each Heartbeat tick #548

Description

@josephfusco

On a site with a persistent object cache, every Heartbeat tick from an open editor makes every cached post query on the site stale.

flowchart LR
  A[Post open in editor] -->|every Heartbeat tick| B[Lock rewritten in post meta]
  B --> C[Posts last_changed bumped]
  C --> D[Every cached post query stale]
Loading

A post lock already is presence: who holds this post, refreshed every tick, stale 150 seconds after the last one. The same editor's presence row already records which block they are in, through gutenberg-sync-engines' block awareness. Keeping the lock in that row puts both facts in one place, and it stops the cache flush as a side effect. Writing the lock through $wpdb, as r62099 did for sync storage, would fix the cache and leave the two apart.

Today With the lock in presence
Where the lock lives Post meta The editor's presence row
Cached post queries on each tick Go stale Stay valid
Locking for the person editing Works Works the same

Core touches _edit_lock only through get_post_meta(), update_post_meta() and delete_post_meta(), so their three short-circuit filters move it without changing a caller. The Customizer changeset lock is _edit_lock on a post too, so the same filters cover it.

  • Write _edit_lock to the lock holder's presence row from update_post_metadata, and clear it from delete_post_metadata
  • Read it back from get_post_metadata, so wp_check_post_lock() and every caller of it work unchanged
  • Test: refreshing a lock leaves wp_cache_get_last_changed( 'posts' ) unchanged, and a second user still sees the post as locked

The test asserts the cache key itself because that is the defect; a test on the filters alone would still pass if something else wrote the meta.

Where this happens in core
  • wp_set_post_lock() writes _edit_lock with update_post_meta(), and the value is a fresh timestamp each time
  • default-filters.php#L126 hooks updated_post_meta to wp_cache_set_posts_last_changed()
  • WP_Customize_Manager::refresh_changeset_lock() writes the same key on the changeset post
  • Nothing else in core reads the key, apart from the exporter skipping it; a plugin that queries _edit_lock in SQL would miss the lock
  • Proving this in the plugin is step two of the core proposal, before any patch

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

PerformanceWork relates to query load, cache behavior, or scaling[Area] Post Lock BridgeIssues for the post-lock bridge[Type] EnhancementA suggestion for improvement of an existing feature

Type

No type

Projects

No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions