-
Notifications
You must be signed in to change notification settings - Fork 381
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
Improve post processor response cache #1325
Conversation
We are capturing each response's URL and storing it into the cache. @todo turn off response caching when the number of caches for the response's URL exceeds the cache miss threshold.
When the number of cached URLs for the given response exceeds the cache miss threshold, disable the response caching. It also disables continuing to cache the response's URLs. @todo add getter for response cache to test the response.
Added the caching keys as public properties. Why? 1. Eliminates passing it into the caching closure. 2. Exposes both of them for testing. Then we use these keys to valid the caching conditions before and after we hit the threshold.
@westonruter Commits through to 1d1f399 are a prototype per our discussions here. It's implementing:
Take a look and see what you think. BTW Notices are not included yet. |
includes/class-amp-theme-support.php
Outdated
self::$post_processor_cache_key = null; | ||
if ( true === $args['enable_response_caching'] ) { | ||
self::$post_processor_cache_key = md5( $current_url ); | ||
$caches_for_url = wp_cache_get( self::$post_processor_cache_key, self::POST_PROCESSOR_CACHE_EFFECTIVENESS ); |
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.
Sone of the subsequent checks could perhaps be simplified by checking if this is an array here, and if not, just set it to be an empty array.
includes/class-amp-theme-support.php
Outdated
* | ||
* @var string | ||
*/ | ||
public static $response_cache_key; |
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.
Is this necessary? Aren't references limited to a single method? Since this is a static class it would be better of we limited the state that is persisted in it's class variables IMO.
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.
@westonruter I added this public property for the following purposes:
- to give us the ability to deeply test by knowing what the cache key is and then checking for it in the cache.
- to simplify access to the logic within the caching closure, i.e. meaning that we don't have to pass it via
use
.
It serves no other external purpose as part of an API.
Let's think about it.
From a testing point of view, we can infer that we disabled the cache by the state of the post-processor cache. While that's not a direct test on the response cache, both are within the closure. Therefore, it's a minimal risk to test by inference.
Hmm, I like that approach better actually. We get the benefit only exposing what is needed externally while avoided the extra work of resetting persistent state.
I'll make that change.
includes/class-amp-theme-support.php
Outdated
if ( empty( $caches_for_url ) ) { | ||
$caches_for_url = array( AMP_Theme_Support::$response_cache_key ); | ||
} else { | ||
$caches_for_url[] = AMP_Theme_Support::$response_cache_key; |
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.
Per above, this could be the sole line instead of the if/else.
…ic static property.
When there are no URLs cached, we get `false` back. To simplify the code, if we do not get an array back, the variable is initialized to an empty array.
For the notice, are you thinking there would be a new key added to the |
Well that's a good question @westonruter. I think to find the solution, we need to explore what we want to report back to the developer/admin. We are disabling the cache on a page-by-page basis. Do we want to give a list of URLs (i.e. that have cache disabled)? Or do we want a general notice? Or both? It's a more broad discussion for the DX/UX. |
@hellofromtonya humm, how about showing the notice on the general settings page, similar to how we are showing a notice for not having the object cache active. I didn't think about letting the cache be disabled on a page-by-page basis. I realize that is how it is implemented now. I think that once we get to the point where the uncached count is greater than |
Also, currently the Something else: if there are logged-in users and the REST API is being used on the frontend, it is likely that the API nonce is going to be output. This nonce will be unique for each user. This being the case, it is very likely that the |
Maybe such caching should just be disabled entirely if the user is logged in. Also, I just realized something else, when we do: $caches_for_url[] = $response_cache_key; We could easily cause for the homepage, for example, to trigger the cache to get disabled once you've published 3 posts, each causing the homepage to change. So what I think we're missing here is a timestamp for when the cache MISS happened. And then what we'd want to do is only consider such entries that have happened within a recent time period (e.g. 10 minutes) for whether the |
Correct.
I agree. That's the next step as we think about how to notify.
How about instead if we set the cache expiration to say 10 minutes? |
Yeah, that sounds good. |
@westonruter okay so instead of page-by-page, once we hit the threshold, we:
That's a simple solution. And as you said, page-by-page is likely overkill right now. |
Instead of storing cache miss URLs page-by-page, this commit stores it on occurrence, using a key and group constant.
1. Refactored into a separate method as there's a lot going on. 2. Check if cache is turned off. 3. If no, check if we've exceeded the threshold. 4. If yes, store the URL.
|
||
printf( | ||
'<div class="notice notice-warning"><p>%s <a href="%s">%s</a></p></div>', | ||
esc_html__( 'Response caching was disabled due to exceeding the cache miss threshold.', 'amp' ), |
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.
We'll probably need to figure out a way to explain what this means to non-developers. Maybe there should be a link to the Wiki with more information about what causes this?
Also, should the notice be dismissible?
Should there be a checkbox to re-enable? If so, then the notice could be displayed inline with the checkbox.
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.
We'll probably need to figure out a way to explain what this means to non-developers. Maybe there should be a link to the Wiki with more information about what causes this?
Agreed. We need more work on the messaging. I've noted it as a @todo
.
should the notice be dismissible?
I think so. I think it's okay that the developer or admin dismisses this warning.
Thinking through it, I'd like to use this top-level warning as an alert: "Hey, we automatically turned off caching. Go to the AMP > General page for more information." Use it to alert them that we did something automatically and it needs their attention.
Should there be a checkbox to re-enable? If so, then the notice could be displayed inline with the checkbox.
Agreed. This is the next step, i.e. adding a re-enable checkbox that appears when caching is disabled.
@todo Change the messaging/description to be more user-friendly and helpful.
printf( | ||
'<div class="notice notice-warning is-dismissible"><p>%s <a href="%s">%s</a></p></div>', | ||
esc_html__( "The AMP plugin's response cache disabled due to detecting randomly generated content.", 'amp' ), | ||
esc_url( 'https://github.com/Automattic/amp-wp/wiki/Response-cache#automatically-disabling-of-the-response-cache' ), |
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.
@westonruter I opened a new Wiki page and started the process of explaining the cache. Once we get the UI done, then screenshots can be added to the page to provide users/developers more clarity.
@@ -48,6 +49,7 @@ public static function register_settings() { | |||
|
|||
add_action( 'update_option_' . self::OPTION_NAME, array( __CLASS__, 'maybe_flush_rewrite_rules' ), 10, 2 ); | |||
add_action( 'admin_notices', array( __CLASS__, 'persistent_object_caching_notice' ) ); | |||
add_action( 'admin_notices', array( __CLASS__, 'render_cache_miss_notice' ) ); |
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.
@westonruter As we are automatically turning off the response cache, I'm thinking we may want to notify not only on this settings page, but on other pages too. Why? We can't be certain the admin/developer will come to this page. At the very least, I'm thinking this notice should be on all AMP settings' pages. What do you think?
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.
What if we limit it to the general settings page but add an indicator to the admin menu item so that the user knows there has something changed on this page?
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.
@westonruter I like that approach in general. I think it spans more than just this PR and edge case. Thinking about, it seems we could have a general indicator to alert of something that needs attention. That indicator could link to the plugin's settings page where more information lives.
If we go for that approach, I think that's a separate feature, issue, and PR.
// Handle the caching option. | ||
$options['enable_response_caching'] = ! empty( $new_options['enable_response_caching'] ); | ||
if ( $options['enable_response_caching'] ) { | ||
do_action( 'amp_reenable_response_cache' ); |
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.
Wouldn't it not be better to call the method here rather than indirectly trigger the action that the method is is connected to? Is there any utility for the amp_reenable_response_cache
action generally?
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.
Upon review, you're right. There is not need for a separation action here. Switching to call direct.
@@ -109,6 +109,17 @@ public function add_menu_items() { | |||
) | |||
); | |||
|
|||
add_settings_field( |
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 setting should only be added if wp_using_ext_object_cache()
returns true, as we should only do response caching if we know that the cached value will persist across requests.
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.
True. Good point.
@@ -31,6 +31,7 @@ class AMP_Options_Manager { | |||
'disable_admin_bar' => false, | |||
'all_templates_supported' => true, | |||
'supported_templates' => array( 'is_singular' ), | |||
'enable_response_caching' => true, |
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 default value here can be the return value of wp_using_ext_object_cache()
(which wouldn't work as-is right here in this static context)
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.
One way to accomplish this is to set the default state within get_options()
:
self::$defaults['enable_response_caching'] = wp_using_ext_object_cache();
return array_merge( self::$defaults, $options );
This change is implemented in commit 752de78.
@@ -198,6 +200,12 @@ public static function validate_options( $new_options ) { | |||
// Store the current version with the options so we know the format. | |||
$options['version'] = AMP__VERSION; | |||
|
|||
// Handle the caching option. | |||
$options['enable_response_caching'] = ! empty( $new_options['enable_response_caching'] ); |
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 condition here should probably be wp_using_ext_object_cache() && ! empty( $new_options['enable_response_caching'] )
…nse_caching'. The $defaults array is a static property. Therefore, we set the default value within the `get_options()` method just before we merge defaults and the stored options.
@westonruter When we changed the conditions by using
|
@@ -355,8 +355,8 @@ public static function render_cache_miss_notice() { | |||
|
|||
printf( | |||
'<div class="notice notice-warning is-dismissible"><p>%s <a href="%s">%s</a></p></div>', | |||
esc_html__( "The AMP plugin's response cache disabled due to detecting randomly generated content.", 'amp' ), | |||
esc_url( 'https://github.com/Automattic/amp-wp/wiki/Response-cache#automatically-disabling-of-the-response-cache' ), | |||
esc_html__( "The AMP plugin's post-processor cache disabled due to the detection of highly-variable content.", 'amp' ), |
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.
Much better @westonruter. Thank you 👍
@hellofromtonya Great work. This was tough! |
Collaborative effort between both of us. Thanks for your help and for first identifying this as an issue. |
Randomly generated content can occur on a web page. When that happens, the response will differ for each request causing cache misses for the plugin's AMP response cache. As @westonruter notes in Issue #1239:
This PR detects continual cache misses by caching the page's URL and then comparing the number of cached URLs to a threshold. When that threshold is exceeded, it disables the response cache and then notifies the admin.
TODO
Closes #1239.