Invalidate query transients to avoid cache flush

Part of #5777. These transients are required and cannot be predicted.

If not using an external cache system, the DB is cleared still to keep
size down after invalidation.

For these 2 transients, invalidation occurs when visibility altering
product attributes are edited.
This commit is contained in:
Mike Jolley 2014-07-03 12:59:54 +01:00
parent 3974c1028a
commit ee48dcd5fd
4 changed files with 55 additions and 14 deletions

View File

@ -23,6 +23,37 @@ class WC_Cache_Helper {
add_action( 'admin_notices', array( __CLASS__, 'notices' ) );
}
/**
* Get transient version
*
* When using transients with unpredictable names, e.g. those containing an md5
* hash in the name, we need a way to invalidate them all at once.
*
* When using default WP transients we're able to do this with a DB query to
* delete transients manually.
*
* With external cache however, this isn't possible. Instead, this function is used
* to append a unique string (based on time()) to each transient. When transients
* are invalidated, the transient version will increment and data will be regenerated.
*
* Raised in issue https://github.com/woothemes/woocommerce/issues/5777
* Adapted from ideas in http://tollmanz.com/invalidation-schemes/
*
* @param string $group Name for the group of transients we need to invalidate
* @param boolean $refresh true to force a new version
* @return string transient version based on time(), 10 digits
*/
public static function get_transient_version( $group, $refresh = false ) {
$transient_name = $group . '-transient-version';
$transient_value = get_transient( $key );
if ( false === $transient_value || true === $refresh ) {
$transient_value = time();
set_transient( $transient_name, $transient_value );
}
return $transient_value;
}
/**
* Prevent caching on dynamic pages.
*

View File

@ -59,15 +59,21 @@ class WC_Post_Data {
* Delete product view transients when needed e.g. when post status changes, or visibility/stock status is modified.
*/
public static function delete_product_query_transients() {
// Increments the transient version to invalidate cache
WC_Cache_Helper::get_transient_version( 'product_query', true );
// If not using an external caching system, we can clear the transients out manually and avoid filling our DB
if ( ! wp_using_ext_object_cache() ) {
global $wpdb;
if ( wp_using_ext_object_cache() ) {
wp_cache_flush(); // There isn't a reliable method of looking up the names, so flush the cache.
return;
$wpdb->query( "
DELETE FROM `$wpdb->options`
WHERE `option_name` LIKE ('\_transient\_wc\_uf\_pid\_%')
OR `option_name` LIKE ('\_transient\_timeout\_wc\_uf\_pid\_%')
OR `option_name` LIKE ('\_transient\_wc\_products\_will\_display\_%')
OR `option_name` LIKE ('\_transient\_timeout\_wc\_products\_will\_display\_%')
" );
}
$wpdb->query( "DELETE FROM `$wpdb->options` WHERE `option_name` LIKE ('\_transient\_wc\_uf\_pid\_%') OR `option_name` LIKE ('\_transient\_timeout\_wc\_uf\_pid\_%')" );
$wpdb->query( "DELETE FROM `$wpdb->options` WHERE `option_name` LIKE ('\_transient\_wc\_products\_will\_display\_%') OR `option_name` LIKE ('\_transient\_timeout\_wc\_products\_will\_display\_%')" );
}
/**

View File

@ -435,7 +435,7 @@ class WC_Query {
unset( $current_wp_query['paged'] );
// Generate a transient name based on current query
$transient_name = 'wc_uf_pid_' . md5( http_build_query( $current_wp_query ) );
$transient_name = 'wc_uf_pid_' . md5( http_build_query( $current_wp_query ) . WC_Cache_Helper::get_transient_version( 'product_query' ) );
$transient_name = ( is_search() ) ? $transient_name . '_s' : $transient_name;
if ( false === ( $unfiltered_product_ids = get_transient( $transient_name ) ) ) {
@ -464,17 +464,19 @@ class WC_Query {
$this->unfiltered_product_ids = $unfiltered_product_ids;
// Also store filtered posts ids...
if ( sizeof( $this->post__in ) > 0 )
if ( sizeof( $this->post__in ) > 0 ) {
$this->filtered_product_ids = array_intersect( $this->unfiltered_product_ids, $this->post__in );
else
} else {
$this->filtered_product_ids = $this->unfiltered_product_ids;
}
// And filtered post ids which just take layered nav into consideration (to find max price in the price widget)
if ( sizeof( $this->layered_nav_post__in ) > 0 )
if ( sizeof( $this->layered_nav_post__in ) > 0 ) {
$this->layered_nav_product_ids = array_intersect( $this->unfiltered_product_ids, $this->layered_nav_post__in );
else
} else {
$this->layered_nav_product_ids = $this->unfiltered_product_ids;
}
}
/**

View File

@ -1340,7 +1340,9 @@ if ( ! function_exists( 'woocommerce_products_will_display' ) ) {
return true;
}
if ( false === ( $products_will_display = get_transient( 'wc_products_will_display_' . $parent_id ) ) ) {
$transient_name = 'wc_products_will_display_' . $parent_id . WC_Cache_Helper::get_transient_version( 'product_query' );
if ( false === ( $products_will_display = get_transient( $transient_name ) ) ) {
$has_children = $wpdb->get_col( $wpdb->prepare( "SELECT term_id FROM {$wpdb->term_taxonomy} WHERE parent = %d AND taxonomy = %s", $parent_id, $taxonomy ) );
if ( $has_children ) {
@ -1363,7 +1365,7 @@ if ( ! function_exists( 'woocommerce_products_will_display' ) ) {
}
}
set_transient( 'wc_products_will_display_' . $parent_id, $products_will_display, YEAR_IN_SECONDS );
set_transient( $transient_name, $products_will_display, YEAR_IN_SECONDS );
return $products_will_display;
}