Opened 19 months ago
Last modified 19 months ago
#61025 new enhancement
Do not write in DB when updating a user if no user data has changed
| Reported by: |
|
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
@
19 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