• Skip to primary navigation
  • Skip to main content
  • Skip to footer
  • Store
  • Support
  • My Account
  • Cart

9seeds

Building Custom WordPress Solutions | Plugin Development

 
  • Custom Development
  • Themes
  • Plugins
  • About
  • Contact
  • Blog

Code

WordPress Performance

WordPress Post Editor Performance

Posted on August 17, 2016

WordPress Performance sometimes seems like an endless quest, but there are sometimes little things that come along and yield great rewards.

Front end and back end WordPress Performance auditing and optimizing is a topic I’ve been meaning to write more depth about since speaking on the topic at a few WordPress meetups, but I wanted to quickly share a performance tip on something I came across today while preparing a bug report for Yoast’s WordPress SEO plugin.

Spotting Performance Issues in WordPress

One of the ways we spot performance issues is to run Query Monitor on most of the sites we work on. Query Monitor is a great tool for a quick view into what’s going on on the backend on every page view and I’ll go into more in a future post.

TL:DR; Skip to #3 at the end.

I am no Chris Lema, which is a abstract way of saying, “I personally don’t spend much time in WordPress post editor”. However, today I was in there investigating a little visual glitch with the WordPress SEO snippet preview metabox when I also noticed that the post editor was loading very slowly. Thanks to Query Monitor I could instantly see the cause was a slow MySQL Query and it was coming from a function called meta_form().

Now, this was on a client’s site with over 100,000 posts and with a site that size performance issues show up rather quickly. This however, does affects smaller sites as well though not to the extent it does large publisher websites like this one. Also just to be clear it is really impacted by the number of rows in the postmeta table, not the number of posts directly, but I figured saying 100,000 post made more sense that 1.8 million rows. On the big site like this one though it was causing a 15 second query. While loading the post editor is infrequent, even on a site like this one with over 100 authors, that 15s is taking up a valuable MySQL thread and certainly frustrating authors.

On my much smaller personal blog it was still delaying page load several seconds and here on 9seeds.com (two sites with no where near as big of a database as our typical client’s site) it was causing a 7s slowdown on the post editor loading.

WordPress Performance
Slow meta_form() database query

Geeky WordPress Developer Details of the slowdown

WordPress runs a function called meta_form() which displays the custom fields meta box. It runs a somewhat intensive query on the wp_postmeta to generate that box (a box most people don’t even need anymore, but that’s a whole separate issue).

That query looks like this:

SELECT DISTINCT meta_key
FROM wp_postmeta 
WHERE meta_key NOT BETWEEN '_'
AND '_z'  HAVING meta_key
NOT LIKE '\\_%' 
ORDER BY meta_key 
LIMIT 30

That’s actually the improved version of the query! When these changes were made to improve the query back in WordPress version 4.3 it seems that only half the needed changes to the database were made for existing sites. The table index was updated to 191 characters, but the field itself was not updated. That mismatch makes that query super slow on large wp_postmeta tables.

There is a new bug report for this performance issue already. While it seems like a trivial thing to fix any time WordPress core needs to modify the existing database on millions of WordPress it’s a pretty big deal and can take a while to get incorporated.

The good news is there are a couple fixes you can implement yourself.

1. (not recommended) My first fix was to truncate the meta_key column to 191 characters with:

I shouldn’t need to say this but backup your database and test this in a safe local or staging environment before doing this on your live database! Database updates are dangerous things without an “undo” button so be extra careful. If you don’t even know how/where to run this code you probably shouldn’t be doing this sort of thing. Keep reading
ALTER TABLE wp_postmeta MODIFY meta_key varchar(191);

That worked, but it’s a bit dangerous since you could have a very long meta_key and it could get truncated, so don’t do that, there are better ways.

2. (recommended) Much easier, in WordPress 4.4 there was a new filter introduced that short-circuits that query ‘postmeta_form_keys‘ which you can provide a pre-defined array of keys to if you need.

add_filter('postmeta_form_keys', array('my_key1, my_key2');

3. (most recommended) Just ditch loading the ancient custom fields box entirely with:

/**
 * Remove Ancient Custom Fields metabox from post editor
 * because it uses a very slow query meta_key sort query 
 * so on sites with large postmeta tables it is super slow
 * and is rarely useful anymore on any site
 */ 
function s9_remove_post_custom_fields_metabox() {
     foreach ( get_post_types( '', 'names' ) as $post_type ) {
         remove_meta_box( 'postcustom' , $post_type , 'normal' );    
     } 
} 
add_action( 'admin_menu' , 's9_remove_post_custom_fields_metabox' ); 

Added to your theme or a core functionality plugin.

Please do let me know if this helps you in the comments, and stay tuned for a deeper dive into WP site performance auditing and fixes.

Continue Reading

Jon Brown

    More by Jon Brown

    Pretty PHP Date Ranges

    Posted on December 28, 2013

    Pretty PHP Date Ranges

    Last week I needed to come up with a way to display a pretty date range based on a starting and ending date in PHP. It’s simple enough use PHP’s date function to convert a unix timestamp into a pretty date format, but PHP doesn’t have a built in function to format date ranges.

    The goal was to take two unix time stamps and output a range that looks like this:

    January 25 – 26th, 2014

    This sounds and is simple until you take into account that sometimes it crosses months and needs to look like this:

    January 25 – February 2nd, 2014

    or could cross years like this:

    December 25th, 2013 – January 3rd, 2014

    So here is the code to build those prettified date ranges:

    
     * @since 1.0
     */
    
    function jb_verbose_date_range($start_date = '',$end_date = '') {
    
        $date_range = '';
    
        // If only one date, or dates are the same set to FULL verbose date
        if ( empty($start_date) || empty($end_date) || ( date('FjY',$start_date) == date('FjY',$end_date) ) ) { // FjY == accounts for same day, different time
            $start_date_pretty = date( 'F jS, Y', $start_date );
            $end_date_pretty = date( 'F jS, Y', $end_date );
        } else {
             // Setup basic dates
            $start_date_pretty = date( 'F j', $start_date );
            $end_date_pretty = date( 'jS, Y', $end_date );
            // If years differ add suffix and year to start_date
            if ( date('Y',$start_date) != date('Y',$end_date) ) {
                $start_date_pretty .= date( 'S, Y', $start_date );
            }
    
            // If months differ add suffix and year to end_date
            if ( date('F',$start_date) != date('F',$end_date) ) {
                $end_date_pretty = date( 'F ', $end_date) . $end_date_pretty;
            }
        }
    
        // build date_range return string
        if( ! empty( $start_date ) ) {
              $date_range .= $start_date_pretty;
        }
    
        // check if there is an end date and append if not identical
        if( ! empty( $end_date ) ) {
            if( $end_date_pretty != $start_date_pretty ) {
                  $date_range .= ' - ' . $end_date_pretty;
              }
         }
        return $date_range;
    }
    

    Also available on GitHub/Gist here.

    To actually use that in WordPress you’d just add something like this in your template file where you wanted to output the date, here based on two custom field values.

    ';
         echo jb_verbose_date_range( $start_date , $end_date );
         echo '';
    }
    
    Continue Reading

    Jon Brown

      More by Jon Brown

      Footer

      Get in Touch

      • New Project Inquiry
      • Product Support and General Inquiry
      • Store Purchase Terms and Conditions
      • Store FAQ
      • Cookie Policy
      • Privacy Policy

      Our Services

      • Custom WP Development
      • Theme Store
      • Plugin Store

      WordPress Plugins for Sale

      • Time Tracker
      • Authorize.net SIM Gateway

      WordPress Plugins for Free

      • Simple Calendar
      • WP Chargify
      • Facebook
      • Twitter
      • LinkedIn
      • WordPress
      • GitHub

      Copyright 2023 | 9seeds, LLC

      Like nearly all websites this one uses cookies too. Like most users we think consent banners like these are a dumb solution, but it's what we've got until new laws are passed. We use cookies on our website for remembering your preferences, for example if you're logged in or what is in your cart. We also use 3rd party cookies for analytics so we know what pages on the site are most popular. By clicking “Accept”, you consent to the use of ALL the cookies.
      Do not sell my personal information.
      Cookie SettingsAccept
      Manage consent

      Privacy Overview

      This website uses cookies to improve your experience while you navigate through the website. Out of these, the cookies that are categorized as necessary are stored on your browser as they are essential for the working of basic functionalities of the website. We also use third-party cookies that help us analyze and understand how you use this website. These cookies will be stored in your browser only with your consent. You also have the option to opt-out of these cookies. But opting out of some of these cookies may affect your browsing experience and may even preclude you being able to login to the website.
      Necessary
      Always Enabled
      Necessary cookies are absolutely essential for the website to function properly. These cookies ensure basic functionalities and security features of the website, anonymously.
      CookieDurationDescription
      __stripe_mid1 yearThis cookie is set by Stripe payment gateway. This cookie is used to enable payment on the website without storing any patment information on a server.
      __stripe_sid30 minutesThis cookie is set by Stripe payment gateway. This cookie is used to enable payment on the website without storing any patment information on a server.
      cookielawinfo-checkbox-advertisement1 yearSet by the GDPR Cookie Consent plugin, this cookie is used to record the user consent for the cookies in the "Advertisement" category .
      cookielawinfo-checkbox-analytics1 yearSet by the GDPR Cookie Consent plugin, this cookie is used to record the user consent for the cookies in the "Analytics" category .
      cookielawinfo-checkbox-necessary1 yearSet by the GDPR Cookie Consent plugin, this cookie is used to record the user consent for the cookies in the "Necessary" category .
      cookielawinfo-checkbox-others1 yearSet by the GDPR Cookie Consent plugin, this cookie is used to store the user consent for cookies in the category "Others".
      cookielawinfo-checkbox-performance1 yearSet by the GDPR Cookie Consent plugin, this cookie is used to store the user consent for cookies in the category "Performance".
      Functional
      Functional cookies help to perform certain functionalities like sharing the content of the website on social media platforms, collect feedbacks, and other third-party features.
      Performance
      Performance cookies are used to understand and analyze the key performance indexes of the website which helps in delivering a better user experience for the visitors.
      Analytics
      Analytical cookies are used to understand how visitors interact with the website. These cookies help provide information on metrics the number of visitors, bounce rate, traffic source, etc.
      CookieDurationDescription
      _ga2 yearsThe _ga cookie, installed by Google Analytics, calculates visitor, session and campaign data and also keeps track of site usage for the site's analytics report. The cookie stores information anonymously and assigns a randomly generated number to recognize unique visitors.
      _gid1 dayInstalled by Google Analytics, _gid cookie stores information on how visitors use a website, while also creating an analytics report of the website's performance. Some of the data that are collected include the number of visitors, their source, and the pages they visit anonymously.
      CONSENT2 yearsYouTube sets this cookie via embedded youtube-videos and registers anonymous statistical data.
      Advertisement
      Advertisement cookies are used to provide visitors with relevant ads and marketing campaigns. These cookies track visitors across websites and collect information to provide customized ads.
      CookieDurationDescription
      VISITOR_INFO1_LIVE5 months 27 daysA cookie set by YouTube to measure bandwidth that determines whether the user gets the new or old player interface.
      YSCsessionYSC cookie is set by Youtube and is used to track the views of embedded videos on Youtube pages.
      yt-remote-connected-devicesneverYouTube sets this cookie to store the video preferences of the user using embedded YouTube video.
      yt-remote-device-idneverYouTube sets this cookie to store the video preferences of the user using embedded YouTube video.
      Others
      Other uncategorized cookies are those that are being analyzed and have not been classified into a category as yet.
      CookieDurationDescription
      cookielawinfo-checkbox-functional1 yearThe cookie is set by GDPR cookie consent to record the user consent for the cookies in the category "Functional".
      SAVE & ACCEPT
      Powered by CookieYes Logo