WordPress Transients – this ⊆ WordPress Performance – What Transients?



WordPress Transients – this ⊆ WordPress Performance – What Transients?

0 0


wordpress-transients

A presentation on WordPress transients

On Github ethanclevenger91 / wordpress-transients

WordPress Transients

this ⊆ WordPress Performance

Created by Ethan Clevenger  @ethanclevenger  ethanclevenger91

This is a tech talk

It assumes you've developing themes or plugins. You know PHP, you know WordPress. If you're just a blogger, it won't mean a ton to you.

What Transients?

The Codex:

[The WordPress Transients API] offers a simple and standardized way of storing cached data in the database temporarily by giving it a custom name and a timeframe after which it will expire and be deleted.

Why Transients?

Performance

Slow site = high bounce rate

Fast site = fun site!

Big O - nx

foreach($foo as $b => $bar) {
  foreach($bar as $f => $fizz) {
    foreach($fizz as $z => $buzz){
      ...
    }
  }
}

APIs

$this->fetchUrl("https://graph.facebook.com/{$id}/posts?access_token={$tkn}");

Nasty SQL

SELECT AUI.idea_id, AUI.title, AUI.events,
  GROUP_CONCAT(IC.category_name SEPARATOR ';') as categories
  FROM (
  	SELECT UI.idea_id, UI.title, GROUP_CONCAT(IE.events SEPARATOR ';') as events ,
  	       CONCAT(';',UI.idea_categories,';') as temp_categories
   	FROM user_idea UI
  	LEFT JOIN idea_events IE ON UI.idea_id = IE.idea_id
  	GROUP BY UI.idea_id
  ) AS AUI
  INNER JOIN idea_categories IC
  ON AUI.temp_categories LIKE CONCAT('%;',IC.category_id,';%')
  GROUP BY AUI.idea_id;

When Transients?

Generally

  • You know when data will expire
  • You know the event that will cause data to expire

But Also

Any time a little outdated information isn't a big deal

How Transients?

A Simple, Local Example

Before

$mPosts = new WP_Query(['post_type'=>'post', 'posts_per_page'=>-1]);
if($mPposts->have_posts()): while($mPosts->have_posts()): $mPosts->the_post();
  echo '<div class="post">';
  echo '<h2>'.get_the_title().'</h2>';
  the_content();
  echo '</div>';
endwhile; endif;

After

$mPosts = get_transient('my_posts');
if($mPosts === false) {
  $mPosts = new WP_Query(['post_type'=>'post', 'posts_per_page'=>-1]);
  set_transient('my_posts', $mPosts, DAY_IN_SECONDS);
}
if($mPosts->have_posts()): while($mPosts->have_posts()): $mPosts->the_post();
  echo '<div class="post">';
  echo '<h2>'.get_the_title().'</h2>';
  the_content();
  echo '</div>';
endwhile; endif;

Still needs some work

A few things still suck here. This code could be cleaner, for one. Also, what happens if I make a time-sensitive post before the transient expires?
  • Abstract that shit
  • What if I post?
Let's address the first issue first and abstract this a little, since it'll make the second part a bit easier

Good

$mPosts = get_transient('my_posts');
if($mPosts === false) {
  $mPosts = new WP_Query(['post_type'=>'post', 'posts_per_page'=>-1]);
  set_transient('my_posts', $mPosts, DAY_IN_SECONDS);
}if($mPosts->have_posts()): while($mPosts->have_posts()): $mPosts->the_post();
  //make it look pretty
endwhile; endif;

Better

$mPosts = getMyPosts();
function getMyPosts() {
  $mPosts = get_transient('my_posts');
  if($mPosts === false) {
    $mPosts = refreshPostsTransient();
  }
  return $mPosts;
}
function refreshPostsTransient() {
  $mPosts = new WP_Query(['post_type' => 'post', 'posts_per_page' =>-1]);
  set_transient('my_posts', $mPosts, DAY_IN_SECONDS);
  return $mPosts;
}

I just posted about an event in two hours and the transient doesn't expire for four hours!

But we're responsible programmers and we put all of our functionality into a class, right? Right? Right?

Hook

add_action('save_post', 'refreshPostsTransient');

Best

add_action('save_post', [$this, 'refreshPostsTransient']);

A thought

What if my transient takes a really long time to generate?

We are, of course, talking about optimization here and we want to be scalable, and we don't want that one user each hour or day or whatever waiting forever for this thing to be generated. Why are we doing this during a real user session? That's a good question, and it's why these don't scale super well out of the box. We want things to refresh when they expire, but we also want it to refresh before someone is waiting on it, which is how the transient currently functions. It doesn't refresh the transient until after the information is no good. Let's beat it to the punch.

How About a Cron?

Here's the pitch

http://mysite.com?mytransient=refresh

Glove ready

add_action('init', [$this, 'maybe_refresh_transient']);

Catch

public function maybe_refresh_transient() {
  if('refresh' === $_GET['mytransient']) {
    $this->refreshPostsTransient();
  }
}
Awesome. If we set this cron to run every 50 minutes, it can do all the data processing and reset the transient in the background, without any users waiting on it.

Third-Party APIs

Our information might be dated, but probably not a big deal
public function getResults($platform) {
  $results = get_transient("social_feeds_results_{$platform}");
  if(false === $results) {
    $results = array();

    switch($platform) {
      case 'twitter' :
      $results = $this->_getResultsTwitter();
      break;
      case 'facebook' :
      $results = $this->_getResultsFacebook();
      break;
    }
    set_transient("social_feeds_results_{$platform}", base64_encode(maybe_serialize($results)), HOUR_IN_SECONDS);
  }
  return maybe_unserialize(base64_decode($results));
}
$tweets = $socialFeeds->getResults('twitter');

Get Silly

Data URI as image src

<img src="data: image/jpeg;base64,/9j...9k=">
function fetchLastThousandInstagramPicturesTransient() {
  $images = get_transient('thousand_instagram_pictures');
  if($images === false) {
    $images = fetchLastThousandInstagramPictures(); //an array of URLs
    $images = dataUriEncodeImages($images);
    set_transient('thousand_instagram_pictures', $images, HOUR_IN_SECONDS);
  }
  return $images;
}
function dataUriEncodeImages($images) {
  $imagesAsData = [];
  foreach($images as $image) {
    $imagesAsData[] = getDataURI($image);
  }
  return $imagesAsData;
}
function getDataURI($image) {
  return 'data: '.mime_content_type($image).';base64,'.base64_encode(file_get_contents($image));
}
$images = fetchLastThousandInstagramPicturesTransient();
foreach($images as $image) {
  echo '<img src=" '.$image.'">';
}

Resources

David Walsh WordPress Codex Reveal.js

Find this talk

http://ethanclevenger.com/wordpress-transients
WordPress Transients this ⊆ WordPress Performance Created by Ethan Clevenger   @ethanclevenger   ethanclevenger91