-
Notifications
You must be signed in to change notification settings - Fork 381
Description
Hello folks! I am a Software Engineer with WordPress VIP at Automattic. Some of my work involves load testing applications for performance analysis. For customers we see using AMP, we often see AMP_Theme_Support::finish_output_buffering
and AMP_Content_Sanitizer::sanitize_document
show up in slow traces during load tests. For platforms with persistent object cache, there is huge potential for performance gains by caching the output and short circuiting. This would eliminate many repeat and expensive calls on subsequent requests. For customers with scale and high traffic in mind, this is important to application stability.
This suggestion adds pre_amp_finish_output_buffering
and amp_finish_output_buffering
filters near finish_output_buffering
in order to enable performant page caching for AMP HTML outputs via object cache or other means.
Filter usage:
pre_amp_finish_output_buffering
: return cached value if present to short-circuit buffer.
amp_finish_output_buffering
: save freshly prepared output after processing.
/**
* Filter to allow lookup of a cached AMP output before expensive processing.
*
* Returning a non-false value will short-circuit AMP output processing.
*
* @since X.X.X
*
* @param false|string $cached_response Cached value, or false to proceed.
* @param string $buffered_output Initial output buffer.
*/
$cached = apply_filters( 'pre_amp_finish_output_buffering', false, $response );
if ( false !== $cached && is_string( $cached ) ) {
return $cached;
}
/**
* Filter to allow saving or processing the AMP output buffer after it's rendered.
*
* Use this to cache the response, for example.
*
* @since X.X.X
*
* @param string $response Final processed AMP response (HTML).
* @param string $buffered_output Initial output buffer before processing.
*/
$response = apply_filters( 'amp_finish_output_buffering', $response, $response /* NOTE: pass original if needed */ );
These filters would then allow a user to leverage caching like this:
/**
* Example: Cache the *full AMP output* by document URL in object cache (for 2 minutes).
*/
add_filter( 'pre_amp_finish_output_buffering', function( $false, $response ) {
$key = 'wpvip_amp_output_' . md5( amp_get_current_url() );
$cached = wp_cache_get( $key, 'amp_output' );
return ( false === $cached ) ? false : $cached;
}, 10, 2 );
add_filter( 'amp_finish_output_buffering', function( $response, $original ) {
$key = 'wpvip_amp_output_' . md5( amp_get_current_url() );
wp_cache_set( $key, $response, 'amp_output', 120 );
return $response;
}, 10, 2 );
We've seen this reduce response times on AMP endpoints by ~25% in testing.
plugins/amp/includes/class-amp-theme-support.php
:
/**
* Finish output buffering.
*
* @since 0.7
* @see AMP_Theme_Support::start_output_buffering()
*
* @param string $response Buffered Response.
* @return string Processed Response.
*/
public static function finish_output_buffering( $response ) {
self::$is_output_buffering = false;
/**
* Filter to allow lookup of a cached AMP output before expensive processing.
*
* Returning a non-false value will short-circuit AMP output processing.
*
* @since X.X.X
*
* @param false|string $cached_response Cached value, or false to proceed.
* @param string $buffered_output Initial output buffer.
*/
$cached = apply_filters( 'pre_amp_finish_output_buffering', false, $response );
if ( false !== $cached && is_string( $cached ) ) {
return $cached;
}
try {
$response = self::prepare_response( $response );
} catch ( Error $error ) { // Only PHP 7+.
$response = self::render_error_page( $error );
} catch ( Exception $exception ) {
$response = self::render_error_page( $exception );
}
/**
* Filter to allow saving or processing the AMP output buffer after it's rendered.
*
* Use this to cache the response, for example.
*
* @since X.X.X
*
* @param string $response Final processed AMP response (HTML).
* @param string $buffered_output Initial output buffer before processing.
*/
$response = apply_filters( 'amp_finish_output_buffering', $response, $response /* NOTE: pass original if needed */ );
/**
* Fires when server timings should be sent.
*
* This is immediately before the processed output buffer is sent to the client.
*
* @since 2.0
* @internal
*/
do_action( 'amp_server_timing_send' );
return $response;
}
Thanks so much for your time! Looking forward to connecting on this 😁