Support » Plugin: WP REST Cache » Caching API on a CDN i.e CloudFlare

  • Resolved andrewceharris

    (@andrewceharris)


    This plugin is great, we use it to power a headless NextJS WordPress site and it helped drastically reduce build times by caching repeated requests.

    However, i wish there was a way to go a step further with the caching.

    We have Cloudflare in-front of our WordPress site, when we cache the requests using their edge network we can get requests down from ~300ms cached to 30ms. Which in turn both helps reduce build times further and load on our origin.

    The problem is cache busting here, ideally we’d have a way to hook into the cleanup_deleted_caches function to get all the URLs that were busted to send in a batch to Cloudflare to also bust.

    I’ve been playing around with a fork of the plugin to with a wp_rest_cache/deleted_caches action that’s fired when the cron job is run, to provide this.

    /**
     * Function called by a cron job to delete flushed or deleted caches from the transients API.
     *
     * @return void
     */
    public function cleanup_deleted_caches() {
    	global $wpdb;
    
    	/**
    	 * How many caches should be cleanup in each run?
    	 *
    	 * Allows to change the number of cleaned up caches per cron run.
    	 *
    	 * @since 2020.2.0
    	 *
    	 * @param int $limit The maximum number of cleaned up caches per cron run.
    	 */
    	$limit = (int) apply_filters( 'wp_rest_cache/max_cleanup_caches', 1000 );
    
            // EDIT: select URI & headers
    	$sql = "SELECT <code>request_uri</code>, <code>request_headers</code>, <code>cache_key</code>, <code>deleted</code>
    							FROM    {$this->db_table_caches}
    							WHERE   <code>expiration</code> = %s
    							AND     <code>cleaned</code> = %d
    							LIMIT   %d";
    	// phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
    	$caches = $wpdb->get_results( $wpdb->prepare( $sql, date_i18n( 'Y-m-d H:i:s', 1 ), 0, $limit ) );
    
            // EDIT: add a hook that can be triggered when caches are cleared.
    	do_action( 'wp_rest_cache/deleted_caches', $caches );
    
    	if ( $caches ) {
    		foreach ( $caches as $cache ) {
    			$this->delete_cache( $cache->cache_key, $cache->deleted );
    		}
    	}
    
    	$sql = "SELECT  COUNT( <code>cache_id</code> ) AS <code>number_of_caches</code>
    							FROM    {$this->db_table_caches}
    							WHERE   <code>expiration</code> = %s
    							AND     <code>cleaned</code> = %d";
    	// phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
    	$count = $wpdb->get_var( $wpdb->prepare( $sql, date_i18n( 'Y-m-d H:i:s', 1 ), 0 ) );
    
    	if ( $count > 0 ) {
    		$this->schedule_cleanup();
    	}
    }

    Using this hook, i then hit the Cloudflare API telling it to purge the URLs that were busted.

    I also modified the schedule_cleanup function to not run on a cron job. This is primarily as i wanted the cache busting to be instant rather than after 5 minutes.

    I know this is there for performance, but needed a way to trigger this on all the cases that a cache entry could get deleted as outlined here https://wordpress.org/support/topic/hook-for-when-cache-is-cleared/

    
    private function schedule_cleanup() {
    	$this->cleanup_deleted_caches();
    }

    Would be potentially nice to have the option to trigger this instantly vs using a cron job.

    Finally on top of this i needed to add some headers to tell Cloudflare to cache the resource, i could do this using the hook below.

    add_filter(
    	'wp_rest_cache/cache_headers',
    	function ( $headers ) {
    
    		// Add cache-headers so that Cloudflare can cache the response.
    		$headers['Cache-Control'] = 'public, max-age=300, s-maxage=300';
    		return $headers;
    	},
    	10000
    );

    However, ideally this would be added to the initial request too, the header is only returned in the second request if the request wasn’t already cached. So it requires 2 requests before Cloudflare caches it.

    Not sure if you know of a better way to hook this up to a CDN infront? Thanks!

Viewing 2 replies - 1 through 2 (of 2 total)
  • Plugin Author Richard Korthuis

    (@rockfire)

    Hi @andrewceharris

    Thank you for using our plugin!

    I think you have a valid case for your suggested changes to the cleanup_deleted_caches function, I will discuss it with my colleagues and see if we can add it to our next release.

    As for the schedule_cleanup function: I think we can add a filter you can use to disable using the cron.

    Finally the headers: Since we let WordPress handle the first request (and simply catch it to cache it), this isn’t something that can be done with hooks provided by our plugin. However, as WordPress is handling this request, you could hook into the core hooks. I think you could do it the same way as WordPress itself is adding the cors headers using the rest_pre_serve_request hook, see: https://github.com/WordPress/WordPress/blob/master/wp-includes/rest-api.php#L223 which they use to add the headers: https://github.com/WordPress/WordPress/blob/master/wp-includes/rest-api.php#L723

    Plugin Author Richard Korthuis

    (@rockfire)

    Hi @andrewceharris

    We just released a new version of our plugin, which includes the action wp_rest_cache/deleted_caches you suggested. And it also includes a filter wp_rest_cache/delete_caches_immediately which (if you use it to return true ) will skip the cron deletion and will delete the caches immediately.

Viewing 2 replies - 1 through 2 (of 2 total)
  • The topic ‘Caching API on a CDN i.e CloudFlare’ is closed to new replies.