Slow and laggy post creation time on WordPress
Today I received a ticket to look into an issue on a WP website where the post creation has become progressively slow. The wp_insert_post()
is triggered as a response to an API request and what started as a 1 sec response ended up becoming 20sec.
Eliminating the obvious
I started to debug by eliminating each line on that particular API endpoint till I found that it waswp_insert_post()
that's taking a longer execution time.
Therefore, I tried simplifying the argument list passed into the wp_insert_post function and kept on validating the response time. I kept on trimming down the arg
list until it was no more than post_title, post_status, post_type. But still there was no change in response time.
Using Query Monitor plugin is highly recommended as it helped to trace the issue with slow response and any other PHP warning.
Debugger to the rescue
I finally decided to step in to the wp_insert_post()
on wordpress/wp-includes/post.php
and traverse line by line to determine the code which consume a lot of time. And there it was;
Culprit
It turned out the code segment which tries to determine a unique slug takes the most of the time.
The reason being, I was setting a generic post_title
value to the posts that were getting created via the API and at the moment when the slow response got worse there were 1 million posts in the database.
If you have a look on wordpress/wp-includes/post.php
line 4737–4757, WP loops through all the posts in the db to determine the suffix that could be added to the new post. And it just keeps on looping through all the posts only to find that there are so many posts with the same titles as “Created through API” while the user sits and waits to hear back from the API.
// line 4737–4757
$is_bad_hierarchical_slug = apply_filters( 'wp_unique_post_slug_is_bad_hierarchical_slug', false, $slug, $post_type, $post_parent );
if ( $post_name_check
|| in_array( $slug, $feeds, true ) || 'embed' === $slug
|| preg_match( "@^($wp_rewrite->pagination_base)?\d+$@", $slug )
|| $is_bad_hierarchical_slug
) {
$suffix = 2;
do {
$alt_post_name = _truncate_post_slug( $slug, 200 - ( strlen( $suffix ) + 1 ) ) . "-$suffix";
$post_name_check = $wpdb->get_var( $wpdb->prepare( $check_sql, $alt_post_name, $post_type, $post_ID, $post_parent ) );
$suffix++;
} while ( $post_name_check );
$slug = $alt_post_name;
}
} else {
// Post slugs must be unique across all posts.
$check_sql = "SELECT post_name FROM $wpdb->posts WHERE post_name = %s AND post_type = %s AND ID != %d LIMIT 1";
$post_name_check = $wpdb->get_var( $wpdb->prepare( $check_sql, $slug, $post_type, $post_ID ) );
The Fix
In order to fix the issue I used uniqid()
PHP function where it generates a prefixed unique identifier based on the current time in microseconds.
The solution is simple as
'post_title' => 'Created through API' . uniqid(' ', TRUE),
This code helped to bring down the response time by 19400 milliseconds
Conclusion
If you are dealing with large content based WP sites, be aware when using matching titles. Maybe there are plugins that helps to generate unique slugs. But on my scenario where everything is API based I went with the above solution.
Let me know if you have run in to any weird WP issues.