Skip to content

Commit

Permalink
Events: Cache event queries for performance.
Browse files Browse the repository at this point in the history
Co-Authored-By: Paul Kevan <[email protected]>
  • Loading branch information
iandunn and pkevan committed Sep 28, 2023
1 parent 025eb44 commit 0d2ffed
Show file tree
Hide file tree
Showing 2 changed files with 117 additions and 7 deletions.
120 changes: 115 additions & 5 deletions public_html/wp-content/themes/wporg-events-2023/inc/event-getters.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,72 @@

defined( 'WPINC' ) || die();


add_action( 'init', __NAMESPACE__ . '\schedule_cron_jobs' );
add_action( 'events_landing_prime_query_cache', __NAMESPACE__ . '\prime_query_cache' );

/**
* Schedule cron jobs.
*/
function schedule_cron_jobs(): void {
if ( ! wp_next_scheduled( 'events_landing_prime_query_cache' ) ) {
wp_schedule_event( time(), 'hourly', 'events_landing_prime_query_cache' );
}
}

/**
* Prime the caches of events.
*
* Without this, users would have to wait for new results to be generated every time the cache expires. That could
* make the front end very slow. This will refresh the cache before it expires, so that the front end can always
* load cached results.
*/
function prime_query_cache(): void {
get_all_upcoming_events( true );

$city_landing_uris = get_known_city_landing_request_uris();

foreach ( $city_landing_uris as $request_uri ) {
get_city_landing_page_events( $request_uri, true );
}
}

/**
* Get a list of all known city landing page request URIs.
*
* For example, `/rome/`, `/rome/training/`, `/rome/2023/`.
*/
function get_known_city_landing_request_uris(): array {
$city_landing_pages = array();

$city_sites = get_sites( array(
'network_id' => EVENTS_NETWORK_ID,
'path__not_in' => array( '/' ),
'number' => false,
'public' => 1,
'archived' => 0,
'deleted' => 0,
) );

foreach ( $city_sites as $site ) {
$parts = explode( '/', trim( $site->path, '/' ) );

if ( 3 !== count( $parts ) ) {
continue;
}

$city = $parts[0];
$year = absint( $parts[1] );
$title = preg_replace( '#^(.*)(-\d+)$#', '$1', $parts[2] ); // Strip any `-2`, `-3`, `-n` suffixes.

$city_landing_pages[ sprintf( '/%s/', $city ) ] = true;
$city_landing_pages[ sprintf( '/%s/%s/', $city, $year ) ] = true;
$city_landing_pages[ sprintf( '/%s/%s/', $city, $title ) ] = true;
}

return array_keys( $city_landing_pages );
}

/**
* Query a table that's encoded with the `latin1` charset.
*
Expand All @@ -13,6 +79,8 @@
* `utf8mb4`, you'll get Mojibake.
*
* @param string $prepared_query ⚠️ This must have already be ran through `$wpdb->prepare()` if needed.
*
* @return object|null
*/
function get_latin1_results_with_prepared_query( string $prepared_query ) {
global $wpdb;
Expand All @@ -33,8 +101,16 @@ function get_latin1_results_with_prepared_query( string $prepared_query ) {
/**
* Get a list of all upcoming events across all sites.
*/
function get_all_upcoming_events(): array {
global $wpdb;
function get_all_upcoming_events( bool $force_refresh = false ): array {
$cache_key = 'event_landing_all_upcoming_events';

if ( ! $force_refresh ) {
$cached_events = get_transient( $cache_key );

if ( $cached_events ) {
return $cached_events;
}
}

$events = get_latin1_results_with_prepared_query( '
SELECT
Expand Down Expand Up @@ -62,6 +138,9 @@ function get_all_upcoming_events(): array {
unset( $event->date_utc );
}

// `prime_query_cache()` should update this hourly, but expire after a day just in case it doesn't.
set_transient( $cache_key, $events, DAY_IN_SECONDS );

return $events;
}

Expand All @@ -70,8 +149,22 @@ function get_all_upcoming_events(): array {
*
* See `get_city_landing_sites()` for how request URIs map to events.
*/
function get_city_landing_page_events( string $request_uri ): array {
$limit = 300;
function get_city_landing_page_events( string $request_uri, bool $force_refresh = false ): array {
$limit = 300;
$cache_key = 'event_landing_city_events_' . md5( $request_uri );

if ( empty( $request_uri ) || '/' === $request_uri ) {
return array();
}

if ( ! $force_refresh ) {
$cached_events = get_transient( $cache_key );

if ( $cached_events ) {
return $cached_events;
}
}

$sites = get_city_landing_sites( $request_uri, $limit );

switch_to_blog( WORDCAMP_ROOT_BLOG_ID );
Expand Down Expand Up @@ -109,14 +202,31 @@ function get_city_landing_page_events( string $request_uri ): array {

restore_current_blog();

// `prime_query_cache()` should update this hourly, but expire after a day just in case it doesn't find all the
// valid request URIs.
set_transient( $cache_key, $events, DAY_IN_SECONDS );

return $events;
}

/**
* Standardize request URIs so they can be reliably used as cache keys.
*
* For example, `/rome`, `/rome/` and `/rome/?foo=bar` should all be `/rome/`.
*/
function normalize_request_uri( $raw_uri, $query_string ) {
$clean_uri = str_replace( '?' . $query_string, '', $raw_uri );
$clean_uri = trailingslashit( $clean_uri );
$clean_uri = '/' . ltrim( $clean_uri, '/' );

return $clean_uri;
}

/**
* Get sites that match the given request URI.
*
* /rome/ -> All sites in Rome
* /rome/training/ -> All training sites in Rome
* /rome/training/ -> All training sites in Rome (including those with slugs like `training-2`)
* /rome/2023/ -> All sites in Rome in 2023
*/
function get_city_landing_sites( string $request_uri, int $limit ): array {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@
return;
}

// Can't use $wp->request because Gutenberg calls this on `init`, before `parse_request`.
$request_uri = str_replace( '?' . $_SERVER['QUERY_STRING'], '', $_SERVER['REQUEST_URI'] );
// Can't use `$wp->request` because Gutenberg calls this on `init`, before `parse_request`.
$request_uri = normalize_request_uri( $_SERVER['REQUEST_URI'], $_SERVER['QUERY_STRING'] );

$map_options = array(
'id' => 'city-landing-page',
Expand Down

0 comments on commit 0d2ffed

Please sign in to comment.