11

I have a WordPress database with over 2 million posts. Whenever I insert a new post I have to call wp_set_object_terms which takes over two seconds to execute. I came across this post that recommends calling wp_defer_term_counting to skip the term counting.

Are there serious consequences to the functioning of WordPress if I use this approach?

Here's the code from the post just in case the link goes dead:

function insert_many_posts(){
  wp_defer_term_counting(true);
  $tasks = get_default_tasks(); 
  for ($tasks as $task){
     $post = array(
       'post_title' => $task[content],
       'post_author' => $current_user->ID,
       'post_content' => '',
       'post_type' => 'bpc_default_task',
       'post_status' => 'publish'
     );
     $task_id = wp_insert_post( $post );

     if ( $task[category] )
        //Make sure we're passing an int as the term so it isn't mistaken for a slug
        wp_set_object_terms( $task_id, array( intval( $category ) ), 'bpc_category' );
  }
}
8

Here are some thoughts on the issue, but please note that this is by no means a conclusive answer as there may be some things I have overlooked however this should give you an insight to the potential gotchas.


Yes, technically there could be consequences.

Where calling wp_defer_term_counting(true) becomes truly beneficial is when for instance you are performing a mass insertion to the database of posts and assigned terms to each object as part of the process.

In such a case you would do the following:

wp_defer_term_counting(true); //defer counting terms

//mass insertion or posts and assignment of terms here

wp_defer_term_counting(false); //count terms after completing business logic

Now in your case, if you are only inserting one post at a time, then deferring term counting will still benefit you however by not call wp_defer_term_counting(false) after your operation could leave you and or other parties involved with the request in a bind if you rely upon the term count for any other logic/processing, conditional or otherwise.

To explain further, let's say you do the following:

Assume we have 3 terms within a taxonomy called product_cat, the IDs for those terms are 1 (term name A), 2 (term name B) and 3 (term name C) respectively.

Each of the above terms already has a term count of 5 (just for the example).

Then this happens...

wp_defer_term_counting(true); //defer counting terms

$post_id = wp_insert_post($data);

wp_set_object_terms($post_id, array(1, 2, 3), 'product_cat');

Then later in your logic you decide to fetch the term because you want to assess the amount of objects associated with that term and perform some other action based on the result.

So you do this...

$terms = get_the_terms($post_id, 'product_cat');

//let's just grab the first term object off the array of returned results
//for the sake of this example $terms[0] relates to term_id 1 (A)
echo $terms[0]->count; //result 5

//dump output of $terms above
array (
  0 => 
  WP_Term::__set_state(array(
     'term_id' => 1,
     'name' => 'A',
     'slug' => 'a',
     'term_group' => 0,
     'term_taxonomy_id' => 1,
     'taxonomy' => 'product_cat',
     'description' => '',
     'parent' => 0,
     'count' => 5, //notice term count still equal to 5 instead of 6
     'filter' => 'raw',
  )),
)

In the case of our example, we said that term name A (term_id 1) already has 5 objects associated with it, in otherwords already has a term count of 5.

So we would expect the count parameter on the returned object above to be 6 but because you did not call wp_defer_term_counting(false) after your operation, the term counts were not updated for the terms applicable (term A, B or C).

Therefore that is the consequence of calling wp_defer_term_counting(true) without calling wp_defer_term_counting(false) after your operation.

Now the question is of course, does this effect you? What if you have no need to call get_the_terms or perform some action that retrieves the term where by you use the count value to perform some other operation? Well in that case great, no problem for you.

But... what if someone else is hooked onto set_object_terms action in the wp_set_object_terms() function and they are relying upon the term count being correct? Now you see where the consequences could arise.

Or what if after the request has terminated, another request is performed that retrieves a taxonomy term and makes use of the count property in their business logic? That could be a problem.

While it may sound far fetched that count values could be of much harm, we cannot assume the way such data will be used based on our own philosophy.

Also as mentioned in the alternate answer, the count as seen on the taxonomy list table will not be updated either.

In fact the only way to update term counts after you defer term counting and your request has ended is to manually call wp_update_term_count($terms, $taxonomy) or wait until someone adds a term for the given taxonomy either via the taxonomy UI or programmatically.

Food for thought.

  • 1
    I think you sum this up brilliantly. It all depends on if you are going to make use of the actual term count as far as I can pick up digging through related source code – Pieter Goosen Mar 8 '16 at 11:38
  • 3
    Yeah I thought this was an interesting question so I had to dig deeper... seems the only issue is if you require an accurate term count during the same request and even after the request has ended. Come to think about it, if anyone is ever relying on the term count for any serious business logic then you couldn't be guaranteed that what you are looking at is in fact the correct count. You'd have to manually try and update the count (e.g. wp_update_term_count()) before using its value. I had no idea that would be the case. – userabuser Mar 8 '16 at 11:46
  • Very comprehensive answer. Since I'm actually doing a mass insert, from what you've explained I need to loop through the terms afterwards and call wp_update_term_count($terms, $taxonomy) on each, correct? – KalenGi Mar 8 '16 at 11:50
  • 2
    WordPress never fails giving one a rude awakening. I must admit, one sometimes learn more answering questions like these than just stuffing around in core in general ;-) – Pieter Goosen Mar 8 '16 at 11:51
  • 1
    If you are doing mass insert, I would just wp_defer_term_counting(true), DO MASS INSERT then wp_defer_term_counting(false). Only reason you would call wp_update_term_count() directly is if you stored the term_ids in a transient then defer counting entirely but for example fire off an AJAX request behind the scenes, grab the transient then manually call wp_update_term_count() or use a cron-job or similar. If you are in the same request (before execution fully terminates) then calling wp_defer_term_counting(false) calls wp_update_term_count() under the hood anyway. – userabuser Mar 8 '16 at 11:59
0

This should be relatively safe as an operation. This is deferring the counting of terms that appears on the Edit Taxonomy page. So, it doesn't appear that there would be any serious consequences.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.