New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Adding OrderTableDatastore and Meta caching integration #46023
base: trunk
Are you sure you want to change the base?
Conversation
Hi @jorgeatorres, @vedanshujain, Apart from reviewing the code changes, please make sure to review the testing instructions as well. You can follow this guide to find out what good testing instructions should look like: |
Pausing review on this until I can come up with a better testing process. |
Renaming methods to invalidate_ rather than delete, marking as @internal
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Great work! Submitting some intial feedback
@@ -98,13 +100,16 @@ public function migrate_orders( array $order_post_ids ): void { | |||
} | |||
|
|||
$this->handle_migration_error( $order_post_ids, $errors, $exception, $using_transactions, $name ); | |||
if ( ! $using_transactions ) { | |||
wc_get_container()->get( OrdersTableDataStore::class )->invalidate_cache_for_objects( $order_post_ids ); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just curious, why is this required? Wouldn't the object be correctly cached at this point? (and same for cache invalidation at line 112)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The PostsToOrdersMigrationController
runs direct queries against the tables/data the OrdersTableDatastore
isn't managed by the OrdersTableDatastore
manages without running those queries through it. So there is nothing in that chain that would clear the object in the datastore's cache. We need to explicitly invalidate the cache for those objects since we're modifying data outside of the datastore context.
* | ||
* @return string | ||
*/ | ||
abstract protected function get_cache_group(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Note that this change won't be backward compatible, maybe we can instead make this function a no-op with a developer warning.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good point. We'll need a good default to make sure it still works correctly. I'm thinking that falling back to the value of ::get_table_name()
may be a good option.
* @return bool[] Array of return values, grouped by the object_id. Each value is either true on success, or false | ||
* if the contents were not deleted. | ||
*/ | ||
public function invalidate_cache_for_objects( array $object_ids ): array { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This function should probably be part of the WPCacheEngine class, it seems very generic, and have a very low dependency on the internals of the datastore class.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The dependency it has on the internals of the datastore class are how objects are cached. Caching is meant to be opaque, so what happens in this method is specific to this implementation, e.g, what cache group/keying to use or if we add a similar implementation on top of a CPT datastore, we would likely call clean_post_cache()
within this method.
Ideally, this method wouldn't even need to be public since the fact that there is caching isn't really something that should be known outside of the datastore. Ideally, we also wouldn't have to call the meta datastore to invalidate its cache for an object. But, because there are existing pathways that directly modify data that the OrdersTableDataStore
manages, we need a way for those paths to be able to notify the datastore that the data was modified externally.
Would it make more sense to leave this method here if the method were called something like ::objects_modified_externally( array $object-ids )
?
Test using WordPress PlaygroundThe changes in this pull request can be previewed and tested using a WordPress Playground instance. Test this pull request with WordPress Playground. Note that this URL is valid for 30 days from when this comment was last updated. You can update it by closing/reopening the PR or pushing a new commit. |
@@ -3076,6 +3153,7 @@ protected function after_meta_change( &$order, $meta ) { | |||
} else { | |||
$order_cache = wc_get_container()->get( OrderCache::class ); | |||
$order_cache->remove( $order->get_id() ); | |||
$this->invalidate_cache_for_objects( array( $order->get_id() ) ); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would like to leave this out as all the callers to ::after_meta_change()
already flush meta cache and there isn't really a direct reason to flush the cache for the object too. However, having this here is needed for Woo Subscriptions compatibility when $subscription->update_dates()
is used to update the created date. This may be a trivial incompatibility, though.
Removing this line takes off another 10 queries from the checkout flow.
Submission Review Guidelines:
Changes proposed in this Pull Request:
Closes #45550.
Background:
The current OrderCaching works by attempting to cache the Order objects. However, the current implementation of
WC_Data::__sleep()/__wakeup()
only allow the ID to be serialized. The reason for this was to avoid serializing the datastore with a data object. The end result means that any unserialized WC_Data object has to be reinitialized completely, requiring DB queries to retrieve the data.This PR introduces caching of order data into the HPOS DataStores layer so that the DB data is cached reducing the queries to initialize order objects.
Benefits:
Alternative Considered:
I looked at improving the serialization handling of WC_Data and it's child classes so that we could serialize all the relevant data with the entities without connected services, like the data stores. There were a couple issues with this:
First, PHP limitations meant that the serialization/deserialization would need to be pretty complex: PHP doesn't have a simple way to use the default serialization and just exclude certain properties. To properly restore an object, each class in the hierarchy would need to potentially deal with any private properties it defines. With extended classes from other plugins, those child classes would need to be properly modified for this to work or compatibility would break.
Second, there is already a bit of juggling going on around caching orders because the data stores have accessible public API's for making updates. Shifting the caching to them allows them to directly control the cache and update it when needed.
How to test the changes in this Pull Request:
Using the WooCommerce Testing Instructions Guide, include your detailed testing instructions:
Unit tests have been updated to validate proper cache invalidation.
The following can be used to test the functionality of the OrdersTableDataStore caching. :
PR46023Tests.php
somewhere within theplugins/woocommerce/tests/php/src
diretory.--filter=PR46023Tests
Further Testing