From 5f6b531f70f5bb1f1580e7e7bc1580c3ae6202fd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Helen=20Hou-Sand=C3=AD?= Date: Thu, 3 Jul 2014 16:45:14 +0000 Subject: [PATCH] Improve oEmbed caching. Introduces the concept of a TTL for oEmbed caches and a filter for `oembed_ttl`. We will no longer replace previously valid oEmbed responses with an `{{unknown}}` cache value. When this happens due to reaching a rate limit or a service going down, it is data loss, and is not acceptable. This means that oEmbed caches for a post are no longer deleted indiscriminately every time that post is saved. oEmbed continues to be cached in post meta, with the addition of a separate meta key containing the timestamp of the last retrieval, which is used to avoid re-requesting a recently cached oEmbed response. By default, we consider a valued cached in the past day to be fresh. This can greatly reduce the number of outbound requests, especially in cases where a post containing multiple embeds is saved frequently. The TTL used to determine whether or not to request a response can be filtered using `oembed_ttl`, thus allowing for the possibility of respecting the optional oEmbed response parameter `cache_age` or altering the period of time a cached value is considered to be fresh. Now that oEmbeds are previewed in the visual editor as well as the media modal, oEmbed caches are often populated before a post is saved or published. By pre-populating and avoiding having to re-request that response, we also greatly reduce the chances of a stampede happening when a published post is visible before oEmbed caching is complete. As it previously stood, a stampede was extremely likely to happen, as the AJAX caching was only triggered when `$_GET['message']` was 1. The published message is 6. We now trigger the caching every time `$_GET['message']` is present on the edit screen, as we are able to avoid triggering so many HTTP requests overall. props markjaquith. fixes #14759. see #17210. Built from https://develop.svn.wordpress.org/trunk@28972 git-svn-id: http://core.svn.wordpress.org/trunk@28761 1a063a9b-81f0-0310-95a4-ce76da25c4cd --- wp-admin/includes/ajax-actions.php | 6 ++-- wp-includes/class-wp-embed.php | 49 ++++++++++++++++++++++-------- 2 files changed, 38 insertions(+), 17 deletions(-) diff --git a/wp-admin/includes/ajax-actions.php b/wp-admin/includes/ajax-actions.php index 44958a070f..77dabad840 100644 --- a/wp-admin/includes/ajax-actions.php +++ b/wp-admin/includes/ajax-actions.php @@ -225,10 +225,8 @@ function wp_ajax_imgedit_preview() { * @since 3.1.0 */ function wp_ajax_oembed_cache() { - global $wp_embed; - - $return = ( $wp_embed->cache_oembed( $_GET['post'] ) ) ? '1' : '0'; - wp_die( $return ); + $GLOBALS['wp_embed']->cache_oembed( $_GET['post'] ); + wp_die( 0 ); } /** diff --git a/wp-includes/class-wp-embed.php b/wp-includes/class-wp-embed.php index 1b849f6f28..af71e28efd 100644 --- a/wp-includes/class-wp-embed.php +++ b/wp-includes/class-wp-embed.php @@ -31,9 +31,6 @@ class WP_Embed { // Attempts to embed all URLs in a post add_filter( 'the_content', array( $this, 'autoembed' ), 8 ); - // When a post is saved, invalidate the oEmbed cache - add_action( 'pre_post_update', array( $this, 'delete_oembed_caches' ) ); - // After a post is saved, cache oEmbed items via AJAX add_action( 'edit_form_advanced', array( $this, 'maybe_run_ajax_cache' ) ); } @@ -78,7 +75,7 @@ class WP_Embed { public function maybe_run_ajax_cache() { $post = get_post(); - if ( ! $post || empty($_GET['message']) || 1 != $_GET['message'] ) + if ( ! $post || empty( $_GET['message'] ) ) return; ?> @@ -192,15 +189,36 @@ class WP_Embed { if ( $post_ID ) { // Check for a cached result (stored in the post meta) - $cachekey = '_oembed_' . md5( $url . serialize( $attr ) ); - if ( $this->usecache ) { - $cache = get_post_meta( $post_ID, $cachekey, true ); + $key_suffix = md5( $url . serialize( $attr ) ); + $cachekey = '_oembed_' . $key_suffix; + $cachekey_time = '_oembed_time_' . $key_suffix; - // Failures are cached + /** + * Filter the oEmbed TTL (time to live). + * + * @since 4.0.0 + * + * @param string $url The attempted embed URL. + * @param array $attr An array of shortcode attributes. + * @param int $post_ID Post ID. + */ + $ttl = apply_filters( 'oembed_ttl', DAY_IN_SECONDS, $url, $attr, $post_ID ); + + $cache = get_post_meta( $post_ID, $cachekey, true ); + $cache_time = get_post_meta( $post_ID, $cachekey_time, true ); + + if ( ! $cache_time ) { + $cache_time = 0; + } + + $cached_recently = ( time() - $cache_time ) < $ttl; + + if ( $this->usecache || $cached_recently ) { + // Failures are cached. Serve one if we're using the cache. if ( '{{unknown}}' === $cache ) return $this->maybe_make_link( $url ); - if ( ! empty( $cache ) ) + if ( ! empty( $cache ) ) { /** * Filter the cached oEmbed HTML. * @@ -214,6 +232,7 @@ class WP_Embed { * @param int $post_ID Post ID. */ return apply_filters( 'embed_oembed_html', $cache, $url, $attr, $post_ID ); + } } /** @@ -230,9 +249,13 @@ class WP_Embed { // Use oEmbed to get the HTML $html = wp_oembed_get( $url, $attr ); - // Cache the result - $cache = ( $html ) ? $html : '{{unknown}}'; - update_post_meta( $post_ID, $cachekey, $cache ); + // Maybe cache the result + if ( $html ) { + update_post_meta( $post_ID, $cachekey, $html ); + update_post_meta( $post_ID, $cachekey_time, time() ); + } elseif ( ! $cache ) { + update_post_meta( $post_ID, $cachekey, '{{unknown}}' ); + } // If there was a result, return it if ( $html ) { @@ -246,7 +269,7 @@ class WP_Embed { } /** - * Delete all oEmbed caches. + * Delete all oEmbed caches. Unused by core as of 4.0.0. * * @param int $post_ID Post ID to delete the caches for. */