Skip to content
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

Merged
merged 26 commits into from
Aug 23, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
0a5e3f2
Add post-processor cache effectiveness.
Aug 7, 2018
fe8bf17
Turn off response caching when exceeding cache miss threshold.
Aug 7, 2018
1d1f399
Expose keys and add tests to validate caching conditions.
Aug 7, 2018
7e6f874
Convert response cache key to internal variable instead of public sta…
Aug 9, 2018
eab2d95
Convert post-processor cache key to internal variable instead of publ…
Aug 9, 2018
d7afb0d
Init $caches_for_url to an empty array.
Aug 9, 2018
73e72b9
Set post-processor cache time to 10 minutes.
Aug 9, 2018
7316ee0
Convert post-processor cache to occurrence instead of page-by-page.
Aug 9, 2018
773bfee
Store the cache miss URL in an option. Then disable.
Aug 10, 2018
75611f5
Add notice to AMP general screen.
Aug 10, 2018
bb39b89
Add caching section to the AMP General sub page.
Aug 13, 2018
2c760bd
Merge branch 'develop' into improve/post-processor-response-cache
Aug 13, 2018
efd9d93
Automatically disable response caching when threshold is exceeded.
Aug 13, 2018
f37e7ff
Handle re-enabling the response cache.
Aug 13, 2018
b0dc43d
Top level notice is dismissible.
Aug 13, 2018
8e553e9
Improve top level notice to be user friendly and link to Wiki.
Aug 13, 2018
1e6a8af
Improve field description and inline warning.
Aug 13, 2018
b05a38f
Merge branch 'develop' into improve/post-processor-response-cache
Aug 22, 2018
0274715
Call AMP_Theme_Support::reset_cache_miss_url_option() directly.
Aug 22, 2018
ecb1587
Check wp_using_ext_object_cache() as part of setting and enable.
Aug 22, 2018
752de78
Use wp_using_ext_object_cache() as the default value of 'enable_respo…
Aug 22, 2018
e575cde
Show notice only when the cache has been disabled due to exceeding th…
Aug 23, 2018
f6716ec
Add description for the enable checkbox.
Aug 23, 2018
bf3dd20
Fix self reference for local constant.
Aug 23, 2018
cdd0506
Rename response cache to post-processor cache
westonruter Aug 23, 2018
65ad9fa
Bump CACHE_MISS_THRESHOLD from 3 to 20
westonruter Aug 23, 2018
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
106 changes: 105 additions & 1 deletion includes/class-amp-theme-support.php
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,34 @@ class AMP_Theme_Support {
*/
const RESPONSE_CACHE_GROUP = 'amp-response';

/**
* Post-processor cache effectiveness group name.
*
* @var string
*/
const POST_PROCESSOR_CACHE_EFFECTIVENESS_GROUP = 'post_processor_cache_effectiveness_group';

/**
* Post-processor cache effectiveness key name.
*
* @var string
*/
const POST_PROCESSOR_CACHE_EFFECTIVENESS_KEY = 'post_processor_cache_effectiveness';

/**
* Cache miss threshold for determining when to disable post-processor cache.
*
* @var int
*/
const CACHE_MISS_THRESHOLD = 20;

/**
* Cache miss URL option name.
*
* @var string
*/
const CACHE_MISS_URL_OPTION = 'amp_cache_miss_url';

/**
* Sanitizer classes.
*
Expand Down Expand Up @@ -1760,6 +1788,13 @@ public static function prepare_response( $response, $args = array() ) {
$current_url = amp_get_current_url();
$ampless_url = amp_remove_endpoint( $current_url );

// When response caching is enabled, determine if it should be turned off for cache misses.
$caches_for_url = null;
if ( true === $args['enable_response_caching'] ) {
list( $disable_response_caching, $caches_for_url ) = self::check_for_cache_misses();
$args['enable_response_caching'] = ! $disable_response_caching;
}

// Return cache if enabled and found.
$cache_response = null;
if ( true === $args['enable_response_caching'] ) {
Expand Down Expand Up @@ -1802,7 +1837,15 @@ public static function prepare_response( $response, $args = array() ) {
return $response_cache['body'];
}

$cache_response = function( $body, $validation_results ) use ( $response_cache_key ) {
$cache_response = function( $body, $validation_results ) use ( $response_cache_key, $caches_for_url ) {
$caches_for_url[] = $response_cache_key;
wp_cache_set(
AMP_Theme_Support::POST_PROCESSOR_CACHE_EFFECTIVENESS_KEY,
$caches_for_url,
AMP_Theme_Support::POST_PROCESSOR_CACHE_EFFECTIVENESS_GROUP,
600 // 10 minute cache.
);

return wp_cache_set(
$response_cache_key,
compact( 'body', 'validation_results' ),
Expand Down Expand Up @@ -1956,6 +1999,67 @@ public static function prepare_response( $response, $args = array() ) {
return $response;
}

/**
* Check for cache misses. When found, store in an option to retain the URL.
*
* @since 1.0
*
* @return array {
* State.
*
* @type bool Flag indicating if the threshold has been exceeded.
* @type string[] Collection of URLs.
* }
*/
private static function check_for_cache_misses() {
// If the cache miss threshold is exceeded, return true.
if ( self::exceeded_cache_miss_threshold() ) {
return array( true, null );
}

// Get the cache miss URLs.
$cache_miss_urls = wp_cache_get( self::POST_PROCESSOR_CACHE_EFFECTIVENESS_KEY, self::POST_PROCESSOR_CACHE_EFFECTIVENESS_GROUP );
$cache_miss_urls = is_array( $cache_miss_urls ) ? $cache_miss_urls : array();

$exceeded_threshold = (
! empty( $cache_miss_urls )
&&
count( $cache_miss_urls ) >= self::CACHE_MISS_THRESHOLD
);

if ( ! $exceeded_threshold ) {
return array( $exceeded_threshold, $cache_miss_urls );
}

// When the threshold is exceeded, store the URL for cache miss and turn off response caching.
update_option( self::CACHE_MISS_URL_OPTION, amp_get_current_url() );
AMP_Options_Manager::update_option( 'enable_response_caching', false );
return array( true, null );
}

/**
* Reset the cache miss URL option.
*
* @since 1.0
*/
public static function reset_cache_miss_url_option() {
if ( get_option( self::CACHE_MISS_URL_OPTION ) ) {
delete_option( self::CACHE_MISS_URL_OPTION );
}
}

/**
* Checks if cache miss threshold has been exceeded.
*
* @since 1.0
*
* @return bool
*/
public static function exceeded_cache_miss_threshold() {
$url = get_option( self::CACHE_MISS_URL_OPTION, false );
return ! empty( $url );
}

/**
* Adds 'data-amp-layout' to the allowed <img> attributes for wp_kses().
*
Expand Down
52 changes: 52 additions & 0 deletions includes/options/class-amp-options-manager.php
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Copy link
Member

@westonruter westonruter Aug 15, 2018

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)

Copy link
Contributor Author

@hellofromtonya hellofromtonya Aug 22, 2018

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.

);

/**
Expand All @@ -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' ) );
Copy link
Contributor Author

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?

Copy link
Member

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?

Copy link
Contributor Author

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.

}

/**
Expand Down Expand Up @@ -78,6 +80,7 @@ public static function get_options() {
if ( empty( $options ) ) {
$options = array();
}
self::$defaults['enable_response_caching'] = wp_using_ext_object_cache();
return array_merge( self::$defaults, $options );
}

Expand Down Expand Up @@ -198,6 +201,16 @@ 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'] = (
wp_using_ext_object_cache()
&&
! empty( $new_options['enable_response_caching'] )
);
if ( $options['enable_response_caching'] ) {
AMP_Theme_Support::reset_cache_miss_url_option();
}

return $options;
}

Expand Down Expand Up @@ -325,4 +338,43 @@ public static function persistent_object_caching_notice() {
);
}
}

/**
* Render the cache miss admin notice.
*
* @return void
*/
public static function render_cache_miss_notice() {
if ( 'toplevel_page_' . self::OPTION_NAME !== get_current_screen()->id ) {
return;
}

if ( ! self::show_response_cache_disabled_notice() ) {
return;
}

printf(
'<div class="notice notice-warning is-dismissible"><p>%s <a href="%s">%s</a></p></div>',
esc_html__( "The AMP plugin's post-processor cache disabled due to the detection of highly-variable content.", 'amp' ),
esc_url( 'https://github.com/Automattic/amp-wp/wiki/Post-Processor-Cache' ),
esc_html__( 'More details', 'amp' )
);
}

/**
* Show the response cache disabled notice.
*
* @since 1.0
*
* @return bool
*/
public static function show_response_cache_disabled_notice() {
return (
wp_using_ext_object_cache()
&&
! self::get_option( 'enable_response_caching' )
&&
AMP_Theme_Support::exceeded_cache_miss_threshold()
);
}
}
41 changes: 41 additions & 0 deletions includes/options/class-amp-options-menu.php
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,19 @@ public function add_menu_items() {
)
);

if ( wp_using_ext_object_cache() ) {
add_settings_field(
'caching',
__( 'Caching', 'amp' ),
array( $this, 'render_caching' ),
AMP_Options_Manager::OPTION_NAME,
'general',
array(
'class' => 'amp-caching-field',
)
);
}

$submenus = array(
new AMP_Analytics_Options_Submenu( AMP_Options_Manager::OPTION_NAME ),
);
Expand Down Expand Up @@ -390,6 +403,34 @@ function updateFieldsetVisibility() {
<?php
}

/**
* Render the caching settings section.
*
* @since 1.0
*
* @todo Change the messaging and description to be user-friendly and helpful.
*/
public function render_caching() {
?>
<fieldset>
<?php if ( AMP_Options_Manager::show_response_cache_disabled_notice() ) : ?>
<div class="notice notice-info notice-alt inline">
<p><?php esc_html_e( 'The post-processor cache was disabled due to detecting randomly generated content found on', 'amp' ); ?> <a href="<?php echo esc_url( get_option( AMP_Theme_Support::CACHE_MISS_URL_OPTION, '' ) ); ?>"><?php esc_html_e( 'on this web page.', 'amp' ); ?></a></p>
<p><?php esc_html_e( 'Randomly generated content was detected on this web page. To avoid filling up the cache with unusable content, the AMP plugin\'s post-processor cache was automatically disabled.', 'amp' ); ?>
<a href="<?php echo esc_url( 'https://github.com/Automattic/amp-wp/wiki/Post-Processor-Cache' ); ?>"><?php esc_html_e( 'Read more', 'amp' ); ?></a>.</p>
</div>
<?php endif; ?>
<p>
<label for="enable_response_caching">
<input id="enable_response_caching" type="checkbox" name="<?php echo esc_attr( AMP_Options_Manager::OPTION_NAME . '[enable_response_caching]' ); ?>" <?php checked( AMP_Options_Manager::get_option( 'enable_response_caching' ) ); ?>>
<?php esc_html_e( 'Enable post-processor caching.', 'amp' ); ?>
</label>
</p>
<p class="description"><?php esc_html_e( 'This will enable post-processor caching to speed up processing an AMP response after WordPress renders a template.', 'amp' ); ?></p>
</fieldset>
<?php
}

/**
* List template conditional options.
*
Expand Down
Loading