Opened 9 months ago
Last modified 9 months ago
#61025 new enhancement
Do not write in DB when updating a user if no user data has changed
Reported by: | asumaran | Owned by: | |
---|---|---|---|
Milestone: | Awaiting Review | Priority: | normal |
Severity: | normal | Version: | 6.5 |
Component: | Users | Keywords: | |
Focuses: | performance | Cc: |
Description
I've been working on improving the response times when checking out using WooCommerce.
We use Hyper DB with a separate host for writes. Subsequent reads are affected after every write. Avoiding any write query helps.
I noticed there's a duplicate query when checking out.
UPDATE `wp_users` SET `user_pass` = '$P$BlixIWEMbCbx76WhYlGoBnta7X8Bgf.', `user_nicename` = 'foo1713310736', `user_email` = 'foo1713310736@mail.test', `user_url` = '', `user_registered` = '2024-04-16 23:38:56', `user_activation_key` = '', `display_name` = 'foo1713310736' WHERE `ID` = 180
Turns out, WC creates a new cutomer using wc_create_new_customer
which under the good uses wp_insert_user
and then lines below updates the customer using WC_Customer->save()
method which under the hood uses wp_update_user
which also uses wp_insert_user
.
At first I've tried to fix it in WooCommerce's scope but the issue is in the wp_insert_user
function.
Since there's no hook to control the write, I'm proposing the same as update_metadata
is already doing, if the passed value is the same as the previous one then skip the write query.
Here's how to reproduce the improvement:
Run the following using WP Shell
$username = 'foo' . time();
$user_email = $username . '@mail.test';
$user_id = wp_insert_user([ 'user_login' => $username, 'user_pass' => 'password', 'user_email' => $user_email ]);
wp_update_user( [ 'ID' => $user_id, 'user_email' => $user_email ] );
wp_update_user( [ 'ID' => $user_id, 'user_email' => $user_email ] );
wp_update_user( [ 'ID' => $user_id, 'user_email' => $user_email ] );
exit;
Small plugin to inspect queries
<?php /* * Plugin Name: Show Queries (@asumaran) */ if ( ! defined( 'SAVEQUERIES' ) ) { define( 'SAVEQUERIES', true ); } add_action( 'shutdown', 'output_queries' ); function output_queries() { global $wpdb; $queries = []; foreach ( $wpdb->queries as $query ) { if ( str_contains( $query[0], 'UPDATE `wp_users`' ) ) { $queries[] = $query[0]; } } error_log( "\r\n" . print_r( join( "\r\n", $queries ), true ) ); }
Without the patch:
It should show 3 UPDATE
to the wp_users
table in the log
UPDATE `wp_users` SET `user_pass` = '$P$BlDtStXHL/XErbE2J8VeILcjNOcVub.', `user_nicename` = 'foo1713365151', `user_email` = 'foo1713365151@mail.test', `user_url` = '', `user_registered` = '2024-04-17 14:45:51', `user_activation_key` = '', `display_name` = 'foo1713365151' WHERE `ID` = 183 UPDATE `wp_users` SET `user_pass` = '$P$BlDtStXHL/XErbE2J8VeILcjNOcVub.', `user_nicename` = 'foo1713365151', `user_email` = 'foo1713365151@mail.test', `user_url` = '', `user_registered` = '2024-04-17 14:45:51', `user_activation_key` = '', `display_name` = 'foo1713365151' WHERE `ID` = 183 UPDATE `wp_users` SET `user_pass` = '$P$BlDtStXHL/XErbE2J8VeILcjNOcVub.', `user_nicename` = 'foo1713365151', `user_email` = 'foo1713365151@mail.test', `user_url` = '', `user_registered` = '2024-04-17 14:45:51', `user_activation_key` = '', `display_name` = 'foo1713365151' WHERE `ID` = 183
With the patch:
It shouldn't log any update queries to the wp_users
table.
Attachments (3)
Change History (4)
#1
@
9 months ago
Updated the patch to skip calling the profile_update action if the UPDATE query did not run.
By not calling this action, WooCommerce will avoid running an unnecessary UPDATE query to set the last_update
user meta if the user data hasn't changed.
Although, in the profile_update
action the $old_user_data
and $userdata
is passed, we could use it to avoid running the query in WooCommerce's scope. But didn't make sense to me to avoid running the UPDATE query and then calling the profile_update
action.
I think it's consistent to avoid calling the action if the UPDATE query didn't run.
So, either my changes make sense or a new filter is introduced to avoid the UPDATE query we are trying to avoid with my changes.
Happy to hear anything about my proposal.
Do not update the users table if the user data didn't change