[wp-trac] [WordPress Trac] #24958: Large number of revisions cause memory exhaustion

WordPress Trac noreply at wordpress.org
Fri Sep 26 10:59:51 UTC 2025


#24958: Large number of revisions cause memory exhaustion
------------------------------------+------------------------------
 Reporter:  jshreve                 |       Owner:  adamsilverstein
     Type:  defect (bug)            |      Status:  assigned
 Priority:  low                     |   Milestone:  Future Release
Component:  Revisions               |     Version:  3.6
 Severity:  normal                  |  Resolution:
 Keywords:  has-patch dev-feedback  |     Focuses:
------------------------------------+------------------------------

Comment (by frkly):

 I came across this ticket while investigating an issue where updating a
 post with many revisions triggered the following PHP out-of-memory error:


 {{{
 PHP Fatal error:  Allowed memory size of *** bytes exhausted (tried to
 allocate *** bytes) in /var/www/html/wp-includes/class-wpdb.php on line
 2322
 }}}


 The root cause seems to be that, solely for the purpose of checking
 whether the current post has changed compared to the latest revision and
 therefore needs to be saved as a new revision, WordPress core retrieves
 all revisions, including those that are not the latest.

 In @oglekler’s example, in order to identify “which revision is the
 latest,” WordPress uses get_posts to load around 900 revisions into
 memory, and that is what leads to the memory exhaustion.

 The call sequence is:


 {{{
 wp_update_post
   → wp_insert_post
     → wp_after_insert_post
       → wp_save_post_revision_on_insert
         → wp_save_post_revision
           → wp_get_post_revisions
             → get_children
               → get_posts
 }}}


 `wp_get_post_revisions` literally fetches all post data of all revisions,
 but in practice only the `post_name` is used except for the latest
 revision.

 Notably, because this path ultimately goes through `get_posts()`, it
 primes the object cache for every retrieved revision (and often post
 meta/terms, depending on query flags). On sites with a persistent object
 cache (e.g. Redis/Memcached), those hundreds of revision entries can also
 be stored in the external cache, increasing memory/IO footprint. In other
 words, the current implementation can end up caching data for revisions
 that are never needed (e.g. “from 900 versions ago”), which unnecessarily
 consumes resources.

 In the core code `wp-includes/revision.php`, it is currently implemented
 like this:

 https://core.trac.wordpress.org/browser/trunk/src/wp-
 includes/revision.php?rev=59715#L163

 {{{#!php
 $revisions = wp_get_post_revisions( $post_id );
 if ( $revisions ) {
     // Grab the latest revision, but not an autosave.
     foreach ( $revisions as $revision ) {
         if ( str_contains( $revision->post_name,
 "{$revision->post_parent}-revision" ) ) {
             $latest_revision = $revision;
             break;
         }
     }

     if ( isset( $latest_revision ) &&  ....
 }}}



 What we actually need here is the ID of the single latest revision among
 those whose `post_name` matches a specific pattern (i.e.,
 `{$post_id}-revision%`, excluding autosaves). Loading the entire set of
 revisions is unnecessary for that.


 A more efficient approach would be:

 {{{#!php
 // Fetch only the single latest non-autosave revision ID within the
 matching post_name pattern.
 $latest_id = $wpdb->get_var( $wpdb->prepare(
     "
     SELECT ID
     FROM {$wpdb->posts}
     WHERE post_parent = %d
       AND post_type   = 'revision'
       AND post_status='inherit'
       AND post_name   LIKE %s
     ORDER BY post_date DESC, ID DESC
     LIMIT 1
     ",
     $post_id,
     $wpdb->esc_like( "{$post_id}-revision" ) . '%'
 ) );

 $latest_revision = $latest_id ? get_post( $latest_id ) : null;

 if ($latest_revision && ....

 }}}

 I only discovered this issue today and have not done a deep analysis, and
 I’m not an expert in this area.
 I hope others can review this approach and consider whether it’s a
 practical improvement for core.

-- 
Ticket URL: <https://core.trac.wordpress.org/ticket/24958#comment:25>
WordPress Trac <https://core.trac.wordpress.org/>
WordPress publishing platform


More information about the wp-trac mailing list