SlideShare ist ein Scribd-Unternehmen logo
1 von 43
Downloaden Sie, um offline zu lesen
Cloud, Cache, and Congs
                       WordCamp NYC 2012




Saturday, June 9, 12
Scott Taylor
                       Lead PHP Developer, eMusic
                           @wonderboymusic
                            www.scotty-t.com
                       #projectmanagementsoftware




Saturday, June 9, 12
eMusic

                Multisite
          Regionalized Content
          Regionalized Caching
       Shared Content across Sites
       Tons of Custom Post Types
              Post Formats
          Tons of Web Services



Saturday, June 9, 12
eMusic Architecture
                       • 12-24 Amazon EC2 instances (CentOS)
                       • 4 MySQL (1 Write, 3 Read)
                       • 4 Memcached (~28GB RAM)
                       • Amazon S3 / Cloudfront CDN
                       • PHP 5.3 / MySQL 5.5
                       • PECL: APC, Memcached, HTTP
                       • Batcache
Saturday, June 9, 12
APC = duh

                       • Essential for PHP
                       • Opcode cache
                       • apc.shm_size = 64M or higher


Saturday, June 9, 12
Logs

                       • Never allow any PHP-related notices,
                         errors, warnings, exceptions, etc
                       • Check access logs regularly for 404s, 500s
                         etc
                       • make a constant for toggling debug logging

Saturday, June 9, 12
Production Data

                       • Always pull down, never push up
                       • We only push imported legacy content up
                       • output buffering
                       • code filtering
                       • more to come on this....

Saturday, June 9, 12
Output Buffering


                        ob_start( $callback )

                             ob_start();

                            echo ‘Daryl’;

                       $daryl = ob_get_clean();




Saturday, June 9, 12
ob_start( function ( $data ) {

                          return str_replace(
                             $urls_array,
                             EMUSIC_CURRENT_HOST,
                             $data
                          );

                       } );



Saturday, June 9, 12
Congs

                       • Mandatory machine configs
                       • HyperDB config
                       • Overridable sunrise.php


Saturday, June 9, 12
Machine Cong
                       • DB credentials local to that machine
                       • Amazon S3 bucket
                       • Web Service Endpoints
                       • Memcached Servers
                       if ( file_exists( ‘/wp-config/config.php’ ) ) {
                            require_once( ‘/wp-config/config.php’ );
                       } else {
                            die( ‘You must have a local config!’ );
                       }
Saturday, June 9, 12
Memcached

                       • Memcached PHP Extension (not Memcache)
                       • Can be used locally (127.0.0.1)
                       • Memcached Redux supports
                         wp_cache_get_/set_multi( )
                       • Johnny Cache

Saturday, June 9, 12
Batcache
                       • Full-page caching
                       • Can be configured
                       • You can partition cache by unique values
                       • Loads before plugins - any code you need
                         has to be duped or added early
                         (sunrise.php)


Saturday, June 9, 12
class batcache {

                     // This is the base configuration. You can edit these variables or move
                 them into your wp-config.php file.
                     var $max_age = 300;

                 // Expire batcache items aged this many seconds (zero to disable batcache)

                       var $remote   =     0;

                 // Zero disables sending buffers to remote datacenters (req/sec is never sent)

                       var $times    =     5;

                 // Only batcache a page after it is accessed this many times... (two or more)

                       var $seconds =    120;

                 // ...in this many seconds (zero to ignore this and use batcache immediately)

                       var $group    = 'batcache';

                 // Name of memcached group. You can simulate a cache flush by changing this.

                       var $unique   = array( BATCACHE_REGION, BATCACHE_COUNTRY );

                 // If you conditionally serve different content, put the variable values here.

                       var $headers = array(
                               'X-nananana' => 'Batcache'
                       );

                       . . . .
                 }


Saturday, June 9, 12
Sunrise
                       • Used to alter multisite context
                       • sets $current_blog and $current_site
                       • filters all URL functions to resolve all URLs
                         to your current domain
                       • registers custom locations for media
                       • filters Admin URLs

Saturday, June 9, 12
switch_to_blog( $blog_id )

                       • All dynamic functions need to account for
                         this
                       • Shared content needs to resolve proper
                         URLs
                       • Different sites have different media
                         locations



Saturday, June 9, 12
Fix switch_to_blog()
                       $current_blog = new stdClass();
                       $current_blog->site_id = 1;
                       $current_blog->archived = 0;
                       $current_blog->mature = 0;
                       $current_blog->spam = 0;
                       $current_blog->deleted = 0;
                       $current_blog->lang_id = 0;
                       $current_blog->public = 1;
                       $current_blog->registered = '2011-02-20 03:38:22';
                       $current_blog->last_updated = $_SERVER['REQUEST_TIME'];
                       $current_blog->domain = EMUSIC_CURRENT_HOST;

                       function emusic_switch_to_blog( $blog_id, $prev_blog_id = 0 ) {
                           if ( $blog_id === $prev_blog_id )
                                return;

                           global $current_blog, $emusic_paths;
                           $current_blog->blog_id = $blog_id;
                           $current_blog->path = $emusic_paths[$blog_id];
                       }

                       emusic_switch_to_blog( $the_id );

                       add_action( 'switch_blog', 'emusic_switch_to_blog', 10, 2 );

                       $blog_id = $the_id;
                       $site_id = 1;

                       $current_site = new stdClass();
                       $current_site->blog_id = $the_id;
                       $current_site->id = 1;
                       $current_site->domain = EMUSIC_CURRENT_HOST;
                       $current_site->site_name = 'eMusic';
                       $current_site->path = $the_path;
Saturday, June 9, 12
Filter URLs

                       add_filter( 'pre_option_upload_path', function () {
                           $id = get_current_blog_id();
                           if ( 1 < $id )
                                return $_SERVER['DOCUMENT_ROOT'] . "/blogs/{$id}/files";

                              return $_SERVER['DOCUMENT_ROOT'] . '/' . EMUSIC_UPLOADS;
                       } );

                       add_filter( 'pre_option_upload_url_path', function () {
                           $id = get_current_blog_id();
                           if ( 1 < $id )
                                return 'http://' . EMUSIC_CURRENT_HOST . "/blogs/{$id}/files";

                              return 'http://' . EMUSIC_CURRENT_HOST   . '/' . EMUSIC_UPLOADS;
                       } );

                       add_filter( 'pre_option_siteurl', function () {
                            global $current_blog;
                            $extra = rtrim( $current_blog->path, '/' );
                            return 'http://' . EMUSIC_CURRENT_HOST . $extra;
                       } );

                       add_filter( 'pre_option_home', function () {
                            global $current_blog;
                            $extra = rtrim( $current_blog->path, '/' );
                            return 'http://' . EMUSIC_CURRENT_HOST . $extra;
                       } );



Saturday, June 9, 12
Plugins
                       • Filter active network plugins (don’t rely on
                         database being correct)
                       • Filter each site’s plugins (if you have a
                         manageable number)
                       • Use classes, not a bunch of functions
                       • Extend before you copy / paste

Saturday, June 9, 12
Site Congs
  require_once( 'site-configs/global.php' );

  if ( $the_id > 1 ) {
      define( 'UPLOADBLOGSDIR', 0 );
      define( 'UPLOADS', 0 );
      define( 'BLOGUPLOADDIR', $_SERVER['DOCUMENT_ROOT'] . "/blogs/{$the_id}/files" );

          switch ( $the_id ) {
          case 2:
              require_once( 'site-configs/bbpress.php' );
               break;

          case 3:
              require_once( 'site-configs/dots.php' );
               break;

          case 5:
              require_once( 'site-configs/support.php' );
               break;
          }

      add_filter( 'pre_option_template', function () {
           return 'dark';
      } );
  } else {
      require_once( 'site-configs/emusic.php' );
  }




Saturday, June 9, 12
Global Plugins
  add_filter( 'pre_site_option_active_sitewide_plugins', function () {
       return array(
           'batcache/batcache.php'                         => 1,
           'akismet/akismet.php'                           => 1,
           'avatar/avatar.php'                             => 1,
           'bundle/bundle.php'                             => 1,
           'cloud/cloud.php'                               => 1,
           'download/download.php'                         => 1,
           'emusic-notifications/emusic-notifications.php' => 1,
           'emusic-ratings/emusic-ratings.php'             => 1,
           'emusic-xml-rpc/emusic-xml-rpc.php'             => 1,
           'johnny-cache/johnny-cache.php'                 => 1,
           'like-buttons/like-buttons.php'                 => 1,
           'members/members.php'                           => 1,
           //'minify/minify.php'                             => 1,
           'apc-admin/apc-admin.php'                       => 1
           //'debug-bar/debug-bar.php'                     => 1
       );
  } );




Saturday, June 9, 12
Site Plugins


                   add_filter( 'pre_option_active_plugins', function () {
                        return array(
                            'artist-images/artist-images.php',
                            'catalog-comments/catalog-comments.php',
                            'emusic-post-types/emusic-post-types.php',
                            'discography.php',
                            'emusic-radio/emusic-radio.php',
                            'gravityforms/gravityforms.php',
                            'super-ghetto/super-ghetto.php'
                            //,'theme-check/theme-check.php'
                        );
                   } );




Saturday, June 9, 12
class MyPlugin {
        function init() {
             add_action( ‘init’, array( $this, ‘register’ ) );
        }

              function register() {}
    }

    $my_plugin = new MyPlugin();
    $my_plugin->init();

    class MyPlugin {
        function init() {
             add_action( ‘init’, array( ‘MyPlugin’, ‘register’ ) );
        }

              function register() {}
    }

    MyPlugin::init();




Saturday, June 9, 12
Share code when possible



        class FeaturePack extends eMusicPostTypes implements PostType {
              ..........
        }




Saturday, June 9, 12
MySQL
                       • Use 5.5, better handlng of weird multibyte
                         strings
                       • Use InnoDB, not MyISAM, pretty much in
                         all cases
                       • HyperDB handles scaling for you
                       • Benchmark queries, don’t be afraid to roll
                         your own SQL, tables, use $wpdb


Saturday, June 9, 12
HyperDB

                       • Can be empty locally
                       • Inherits wp-config DB defaults
                       • contains functions for replication lag
                         detection (when used with mk-heartbeat)




Saturday, June 9, 12
Themes

                       • Theme setup is a class
                       • Extend before you repeat
                       • Classes are better than prefixing function
                         names




Saturday, June 9, 12
Theme Cong in functions.php

    class Theme_17Dots extends Regionalization {
        function __construct() {
            global $dots_regions_tax_map, $dots_regions_map;

                       $this->regions_map = $dots_regions_map;
                       $this->regions_tax_map = $dots_regions_tax_map;

                       parent::__construct();
            }

            function init()        {
                add_action(        'init',                               array( $this, 'register' ) );
                add_action(        'after_setup_theme',                  array( $this, 'setup' ) );
                add_action(        'add_meta_boxes_post',                array( $this, 'boxes' ) );
                add_action(        'save_post',                          array( $this, 'save' ), 10, 2 );
                add_filter(        'embed_oembed_html',                  '_feature_youtube_add_wmode' );
            }
            . . . . . . .
    }




Saturday, June 9, 12
Use base classes


   class Regionalization {
       var $regions_map;

       function __construct() {
           add_filter( 'manage_posts_columns',             array(   $this, 'manage_columns' ) );
           add_action( 'manage_posts_custom_column',       array(   $this,
   'manage_custom_column' ), 10, 2 );
           add_filter( 'posts_clauses',                    array(   $this, 'clauses' ), 10, 2 );
           add_filter( 'manage_edit-post_sortable_columns',array(   $this, 'sortables' ) );
           add_filter( 'pre_get_posts',                    array(   $this, 'pre_posts' ) );
       }
       . . . . . .
   }




Saturday, June 9, 12
pre_get_posts
                       function regionalize( $query ) {
                              global $regions_map;
                              if ( $query->is_main_query() && !is_admin() && ( is_search() || is_archive() || is_home() ) ) {

                                      $types = get_post_types( array( 'publicly_queryable' => true, '_builtin' => false ) );
                                      $tax_region = array(
                                             'taxonomy' => 'region',
                                             'field'     => 'term_id',
                                             'terms'     => array(
                                                    $regions_map[ 'ALL' ],
                                                    $regions_map[ THE_REGION ]
                                             ),
                                             'operator' => 'IN'
                                      );

                                      if ( is_home() ) {
                                             $query->set( 'posts_per_page', 10 );

                                      } else if ( is_search() && get_option( 'editorial_search_enabled' ) ) {
                                             $ctx = get_query_var( 'search_context' );
                                             $query->set( 's', stripslashes( urldecode( get_query_var( 's' ) ) ) );

                                             if ( in_array( $ctx, array( 'features', 'features-books' ) ) ) {
                                                    $query->set( 'posts_per_page', 48 );
                                             } else {
                                                    $query->set( 'posts_per_page', 12 );
                                             }

                                             if ( in_array( $ctx, array( 'books', 'features-books' ) ) ) {
                                                    foreach ( $types as $type )
                                                           if ( false === strpos( $type, 'book' ) )
                                                                  unset( $types[$type] );
                                             }

                                             if ( empty( $query->posts ) && $query->is_paged() )
                                                    $query->is_paged = false;

                                      } else if ( is_tag() ) {
                                             if ( empty( $query->posts ) && $query->is_paged() )
                                                    $query->is_paged = false;
                            }

                                      //error_log( 'REGIONALIZING' );

                                      if ( !is_post_type_archive() )
                                             $query->set( 'post_type', array_keys( $types ) );

                                      $query->set( 'tax_query', array( $tax_region ) );
                                }

                                return $query;
                       }




Saturday, June 9, 12
Assets

                       • Use remote storage
                       • Use a CDN
                       • Replace hosts using output buffer
                       • Cloud - pieces of W3 Total Cache

Saturday, June 9, 12
Minify
                       • JS / CSS concatenation speed up your
                         front-end loading / perceived loading
                       • Minify is automagic
                       • HTML5 CSS properties are automatically
                         inflated
                       • Admin tool to cache-bust URLs
                       • Auto-locking while files are generated
Saturday, June 9, 12
Web Services
                       • cURL PHP extension
                       • curl and curl_multi()
                       • Memcached is essential
                       • hooks into parse_request to load data
                           along with WordPress, allows us to cause
                           WP to 404 and bail early when required
                           data response fails
                       •   Parallelization with curl_multi()

Saturday, June 9, 12
class eMusicRequest {
                          var $request;
                          var $sub_request;

                           var $path;
                           var $page;

                           function load( $page = '' ) {
                              if ( !empty( $page ) )
                                  $this->page = $page;

                               $le = $this->path . $this->page . '.php';
                               if ( le_exists( $le ) )
                                   require_once( $le );
                           }

                           function parse() {
                              $requests = array( $this->request, $this->sub_request );

                               foreach ( $requests as $request ) {
                                  if ( !empty( $request ) ) {
                                      $keys = array_keys( get_class_vars( get_class( $request ) ) );
                                      foreach ( $keys as $var ) {
                                         if ( !empty( $request->$var ) ) {
                                             $GLOBALS[$var] = $request->$var;
                                         }
                                      }
                                  }
                               }
                           }
                       }



Saturday, June 9, 12
class DarkRequest extends eMusicRequest {
                          var $genre;
                          var $post_type;

                           function init( $request ) {
                              global $_GENRES;

                               $vars =& $request->query_vars;

                               .........
                           }

                       }

Saturday, June 9, 12
switch ( get_current_blog_id() ) {
       case 1:
           $_dark_request = new DarkRequest();
           add_action( 'parse_request', array( $_dark_request, 'init' ) );
           break;
       case 4:
           $_my_emusic_request = new MyEMusicRequest();
           add_action( 'parse_request', array( $_my_emusic_request,
       'init' ) );
           break;
       }




Saturday, June 9, 12
<?php
             class HomeRequest extends RequestMap {
                 var $recommendations;

                   function __construct() {
                       parent::__construct();

                       if ( !get_option( 'recs_enabled' ) )
                           return;

                       if ( is_user_logged_in() && 'US' === THE_REGION ) {
                           $user = wp_get_current_user();
                           $user_id = isset( $_GET['user_id'] ) ? $_GET['user_id'] : $user->ID;
                           $params = array( 'userId' => $user_id, 'return' => true );

                              $this->add( get_user_recommendations( $params ), array( $this, 'parse_recommendations' ) );
                              $this->send();
                       }
                   }

                   function parse_recommendations( $data ) {
                       if ( empty( $data['recommendations'] ) )
                           return;

                       $data = $data['recommendations'];

                       if (
                              !empty( $data ) &&
                              isset( $data[key($data)]['items'] ) &&
                              !empty( $data[key($data)]['items'] )
                       ) {
                              $this->recommendations = array_slice( $data[key($data)]['items'], 0, 18 );
                              shuffle( $this->recommendations );

                              foreach ( $this->recommendations as &$rec ) {
                                  $rec = array( 'work_id' => $rec['catalogId'] );
                              }
                       }
                   }
             }




Saturday, June 9, 12
<?php
        class RequestMap extends API {
            private $requests;
            private $responses;
            private $ttl;
            private $useCache = true;

             protected $error = false;

             public function __construct() {
                 $this->flush();
             }

             public function is_error() {
                 return $this->error;
             }

             public function flush() {
                 $this->requests = array();
             }

             public function getTtl() {
                 if ( empty( $this->ttl ) ) {
                     $this->ttl = CACHE::API_CACHE_TTL;
                 }
                 return $this->ttl;
             }

             public function add( $url, $callback, $vars = array() ) {
                 $params = new stdClass();
                 $params->url = $url;
                 $params->callback = $callback;
                 $params->params = (array) $vars;
                 $this->requests[] = $params;
             }

             private function exec( $item, $response ) {
                 $params = array_merge( array( $response ), $item->params );
                 call_user_func_array( $item->callback, $params );
             }

             public function send() {
                 if ( !empty( $this->requests ) ) {
                     $this->responses = self::batch( $this->getRequestUrls(), $this->getTtl(), $this->useCache );

                       if ( is_array( $this->responses ) ) {
                           foreach ( $this->responses as $i => $response ) {
                               if ( !empty( $this->requests[$i] ) ) {
                                   $this->exec( $this->requests[$i], self::parse_response( $response ) );
                               }
                           }
                       }
                  }
             }
        }
Saturday, June 9, 12
class API {

       public static function batch( $urls, $ttl = '', $usecache = true ) {
           $response = array();
           ob_start();

              if ( empty( $ttl ) ) {
                  $ttl = CACHE::API_CACHE_TTL;
              }

              if ( is_array( $urls ) ) {
                  if ( $usecache ) {
                      foreach ( $urls as $index => $url ) {
                          $in = Cache::get( Cache::API, $url );

                               if ( $in ) {
                                   $response[$index] = $in;
                                   unset( $urls[$index] );
                               }
                           }
                       }

                       $keys = array_keys( $urls );
              }
              $calls = self::multi_request( $urls );


              . . . . . . . .




Saturday, June 9, 12
if ( is_array( $calls ) && count( $calls ) > 0 ) {
                  $calls = array_combine( $keys, array_values( $calls ) );

            foreach ( $calls as $index => $c ) {
                if ( $c ) {
                    $response[$index] = self::parse_response( $c );
                    if ( isset( $response[$index]['status']['code'] ) && $response[$index]
['status']['code'] < 400 ) {
                         if ( isset( $response[$index]['results'] ) && !
empty( $response[$index]['results'] ) ) {
                             Cache::put( Cache::API, $urls[$index], $response[$index], $ttl );
                         } else {
                             Cache::put( Cache::API, $urls[$index], $response[$index], $ttl );
                         }
                    } else if ( !isset( $response[$index]['status']['code'] ) ) {
                         Cache::put( Cache::API, $urls[$index], $response[$index], $ttl );
                    }
                } else {
                    $response[$index] = null;
                }
            }
        } else if ( !empty( $calls ) && isset( $urls[0] ) ) {
            $data = self::parse_response( $calls );
            if ( isset( $data['status']['code'] ) && $data['status']['code'] < 400 ) {
                if ( isset( $data['results'] ) && !empty( $data['results'] ) ) {
                    Cache::put( Cache::API, $urls[0], $data, $ttl );
                } else {
                    Cache::put( Cache::API, $urls[0], $data, $ttl );
                }
            } else if ( !isset( $data['status']['code'] ) ) {
                Cache::put( Cache::API, $urls[0], $data, $ttl );
            }
            $response[] = $data;
        }
        ob_end_clean();

              return $response;
Saturday, June 9, 12
Because we used
                       classes, everything is
                            abstracted


Saturday, June 9, 12
Custom Authentication
                       • WP stores slashed passwords
                       • wp_authentication arguments are slashed
                       • Inherit your base system’s rules
                       • DO NOT sanitize email / password
                         (WordPress does by default)
                       • User tables in this case are really a
                         transient cache


Saturday, June 9, 12
Questions / complaints?



Saturday, June 9, 12

Weitere ähnliche Inhalte

Was ist angesagt?

Drupal Javascript for developers
Drupal Javascript for developersDrupal Javascript for developers
Drupal Javascript for developersDream Production AG
 
Memcached Presentation @757rb
Memcached Presentation @757rbMemcached Presentation @757rb
Memcached Presentation @757rbKen Collins
 
Harmonious Development: Standardizing The Deployment Process via Vagrant and ...
Harmonious Development: Standardizing The Deployment Process via Vagrant and ...Harmonious Development: Standardizing The Deployment Process via Vagrant and ...
Harmonious Development: Standardizing The Deployment Process via Vagrant and ...Acquia
 
jQuery and_drupal
jQuery and_drupaljQuery and_drupal
jQuery and_drupalBlackCatWeb
 
Render Caching for Drupal 8
Render Caching for Drupal 8Render Caching for Drupal 8
Render Caching for Drupal 8John Doyle
 
JavaScript in Drupal 7: What developers need to know
JavaScript in Drupal 7: What developers need to knowJavaScript in Drupal 7: What developers need to know
JavaScript in Drupal 7: What developers need to knowkatbailey
 
KISSY 的昨天、今天与明天
KISSY 的昨天、今天与明天KISSY 的昨天、今天与明天
KISSY 的昨天、今天与明天tblanlan
 
Czym jest webpack i dlaczego chcesz go używać?
Czym jest webpack i dlaczego chcesz go używać?Czym jest webpack i dlaczego chcesz go używać?
Czym jest webpack i dlaczego chcesz go używać?Marcin Gajda
 
kissy-past-now-future
kissy-past-now-futurekissy-past-now-future
kissy-past-now-futureyiming he
 
Introducing Assetic: Asset Management for PHP 5.3
Introducing Assetic: Asset Management for PHP 5.3Introducing Assetic: Asset Management for PHP 5.3
Introducing Assetic: Asset Management for PHP 5.3Kris Wallsmith
 
Phase2 OpenPublish Presentation SF SemWeb Meetup, April 28, 2009
Phase2 OpenPublish Presentation SF SemWeb Meetup, April 28, 2009Phase2 OpenPublish Presentation SF SemWeb Meetup, April 28, 2009
Phase2 OpenPublish Presentation SF SemWeb Meetup, April 28, 2009Krista Thomas
 
Geodaten & Drupal 7
Geodaten & Drupal 7Geodaten & Drupal 7
Geodaten & Drupal 7Michael Milz
 
Doctrine 2
Doctrine 2Doctrine 2
Doctrine 2zfconfua
 
Drupal 8 Services
Drupal 8 ServicesDrupal 8 Services
Drupal 8 ServicesPhilip Norton
 
Remy Sharp The DOM scripting toolkit jQuery
Remy Sharp The DOM scripting toolkit jQueryRemy Sharp The DOM scripting toolkit jQuery
Remy Sharp The DOM scripting toolkit jQuerydeimos
 
Intro to advanced caching in WordPress
Intro to advanced caching in WordPressIntro to advanced caching in WordPress
Intro to advanced caching in WordPressMaor Chasen
 
Virtual Madness @ Etsy
Virtual Madness @ EtsyVirtual Madness @ Etsy
Virtual Madness @ EtsyNishan Subedi
 
Drupal 7 Theming - Behind the scenes
Drupal 7 Theming - Behind the scenes Drupal 7 Theming - Behind the scenes
Drupal 7 Theming - Behind the scenes ramakesavan
 
Drupal & javascript
Drupal & javascriptDrupal & javascript
Drupal & javascriptAlmog Baku
 
Joe Walker Interactivewebsites Cometand Dwr
Joe Walker Interactivewebsites Cometand DwrJoe Walker Interactivewebsites Cometand Dwr
Joe Walker Interactivewebsites Cometand Dwrdeimos
 

Was ist angesagt? (20)

Drupal Javascript for developers
Drupal Javascript for developersDrupal Javascript for developers
Drupal Javascript for developers
 
Memcached Presentation @757rb
Memcached Presentation @757rbMemcached Presentation @757rb
Memcached Presentation @757rb
 
Harmonious Development: Standardizing The Deployment Process via Vagrant and ...
Harmonious Development: Standardizing The Deployment Process via Vagrant and ...Harmonious Development: Standardizing The Deployment Process via Vagrant and ...
Harmonious Development: Standardizing The Deployment Process via Vagrant and ...
 
jQuery and_drupal
jQuery and_drupaljQuery and_drupal
jQuery and_drupal
 
Render Caching for Drupal 8
Render Caching for Drupal 8Render Caching for Drupal 8
Render Caching for Drupal 8
 
JavaScript in Drupal 7: What developers need to know
JavaScript in Drupal 7: What developers need to knowJavaScript in Drupal 7: What developers need to know
JavaScript in Drupal 7: What developers need to know
 
KISSY 的昨天、今天与明天
KISSY 的昨天、今天与明天KISSY 的昨天、今天与明天
KISSY 的昨天、今天与明天
 
Czym jest webpack i dlaczego chcesz go używać?
Czym jest webpack i dlaczego chcesz go używać?Czym jest webpack i dlaczego chcesz go używać?
Czym jest webpack i dlaczego chcesz go używać?
 
kissy-past-now-future
kissy-past-now-futurekissy-past-now-future
kissy-past-now-future
 
Introducing Assetic: Asset Management for PHP 5.3
Introducing Assetic: Asset Management for PHP 5.3Introducing Assetic: Asset Management for PHP 5.3
Introducing Assetic: Asset Management for PHP 5.3
 
Phase2 OpenPublish Presentation SF SemWeb Meetup, April 28, 2009
Phase2 OpenPublish Presentation SF SemWeb Meetup, April 28, 2009Phase2 OpenPublish Presentation SF SemWeb Meetup, April 28, 2009
Phase2 OpenPublish Presentation SF SemWeb Meetup, April 28, 2009
 
Geodaten & Drupal 7
Geodaten & Drupal 7Geodaten & Drupal 7
Geodaten & Drupal 7
 
Doctrine 2
Doctrine 2Doctrine 2
Doctrine 2
 
Drupal 8 Services
Drupal 8 ServicesDrupal 8 Services
Drupal 8 Services
 
Remy Sharp The DOM scripting toolkit jQuery
Remy Sharp The DOM scripting toolkit jQueryRemy Sharp The DOM scripting toolkit jQuery
Remy Sharp The DOM scripting toolkit jQuery
 
Intro to advanced caching in WordPress
Intro to advanced caching in WordPressIntro to advanced caching in WordPress
Intro to advanced caching in WordPress
 
Virtual Madness @ Etsy
Virtual Madness @ EtsyVirtual Madness @ Etsy
Virtual Madness @ Etsy
 
Drupal 7 Theming - Behind the scenes
Drupal 7 Theming - Behind the scenes Drupal 7 Theming - Behind the scenes
Drupal 7 Theming - Behind the scenes
 
Drupal & javascript
Drupal & javascriptDrupal & javascript
Drupal & javascript
 
Joe Walker Interactivewebsites Cometand Dwr
Joe Walker Interactivewebsites Cometand DwrJoe Walker Interactivewebsites Cometand Dwr
Joe Walker Interactivewebsites Cometand Dwr
 

Ähnlich wie Cloud, Cache, and Configs at WordCamp NYC 2012

PECL Picks - Extensions to make your life better
PECL Picks - Extensions to make your life betterPECL Picks - Extensions to make your life better
PECL Picks - Extensions to make your life betterZendCon
 
Pure Speed Drupal 4 Gov talk
Pure Speed Drupal 4 Gov talkPure Speed Drupal 4 Gov talk
Pure Speed Drupal 4 Gov talkBryan Ollendyke
 
WordCamp San Francisco 2011: Transients, Caching, and the Complexities of Mul...
WordCamp San Francisco 2011: Transients, Caching, and the Complexities of Mul...WordCamp San Francisco 2011: Transients, Caching, and the Complexities of Mul...
WordCamp San Francisco 2011: Transients, Caching, and the Complexities of Mul...andrewnacin
 
CHI-YAPC-2009
CHI-YAPC-2009CHI-YAPC-2009
CHI-YAPC-2009jonswar
 
4069180 Caching Performance Lessons From Facebook
4069180 Caching Performance Lessons From Facebook4069180 Caching Performance Lessons From Facebook
4069180 Caching Performance Lessons From Facebookguoqing75
 
Zend Server Data Caching
Zend Server Data CachingZend Server Data Caching
Zend Server Data CachingEl Taller Web
 
Scaling in Mind (Case study of Drupal Core)
Scaling in Mind (Case study of Drupal Core)Scaling in Mind (Case study of Drupal Core)
Scaling in Mind (Case study of Drupal Core)jimyhuang
 
10 Things Every Plugin Developer Should Know (WordCamp Atlanta 2013)
10 Things Every Plugin Developer Should Know (WordCamp Atlanta 2013)10 Things Every Plugin Developer Should Know (WordCamp Atlanta 2013)
10 Things Every Plugin Developer Should Know (WordCamp Atlanta 2013)arcware
 
Persistence patterns for containers
Persistence patterns for containersPersistence patterns for containers
Persistence patterns for containersStephen Watt
 
Scaling php applications with redis
Scaling php applications with redisScaling php applications with redis
Scaling php applications with redisjimbojsb
 
HTTP Caching and PHP
HTTP Caching and PHPHTTP Caching and PHP
HTTP Caching and PHPDavid de Boer
 
Php on the desktop and php gtk2
Php on the desktop and php gtk2Php on the desktop and php gtk2
Php on the desktop and php gtk2Elizabeth Smith
 
Mobile & Desktop Cache 2.0: How To Create A Scriptable Cache
Mobile & Desktop Cache 2.0: How To Create A Scriptable CacheMobile & Desktop Cache 2.0: How To Create A Scriptable Cache
Mobile & Desktop Cache 2.0: How To Create A Scriptable CacheBlaze Software Inc.
 
Zend framework 03 - singleton factory data mapper caching logging
Zend framework 03 - singleton factory data mapper caching loggingZend framework 03 - singleton factory data mapper caching logging
Zend framework 03 - singleton factory data mapper caching loggingTricode (part of Dept)
 
Bottom to Top Stack Optimization with LAMP
Bottom to Top Stack Optimization with LAMPBottom to Top Stack Optimization with LAMP
Bottom to Top Stack Optimization with LAMPkatzgrau
 
Bottom to Top Stack Optimization - CICON2011
Bottom to Top Stack Optimization - CICON2011Bottom to Top Stack Optimization - CICON2011
Bottom to Top Stack Optimization - CICON2011CodeIgniter Conference
 
Hybrid Cloud PHPUK2012
Hybrid Cloud PHPUK2012Hybrid Cloud PHPUK2012
Hybrid Cloud PHPUK2012Combell NV
 
Web Application Development using PHP Chapter 7
Web Application Development using PHP Chapter 7Web Application Development using PHP Chapter 7
Web Application Development using PHP Chapter 7Mohd Harris Ahmad Jaal
 

Ähnlich wie Cloud, Cache, and Configs at WordCamp NYC 2012 (20)

Pecl Picks
Pecl PicksPecl Picks
Pecl Picks
 
PECL Picks - Extensions to make your life better
PECL Picks - Extensions to make your life betterPECL Picks - Extensions to make your life better
PECL Picks - Extensions to make your life better
 
Pure Speed Drupal 4 Gov talk
Pure Speed Drupal 4 Gov talkPure Speed Drupal 4 Gov talk
Pure Speed Drupal 4 Gov talk
 
Fatc
FatcFatc
Fatc
 
WordCamp San Francisco 2011: Transients, Caching, and the Complexities of Mul...
WordCamp San Francisco 2011: Transients, Caching, and the Complexities of Mul...WordCamp San Francisco 2011: Transients, Caching, and the Complexities of Mul...
WordCamp San Francisco 2011: Transients, Caching, and the Complexities of Mul...
 
CHI-YAPC-2009
CHI-YAPC-2009CHI-YAPC-2009
CHI-YAPC-2009
 
4069180 Caching Performance Lessons From Facebook
4069180 Caching Performance Lessons From Facebook4069180 Caching Performance Lessons From Facebook
4069180 Caching Performance Lessons From Facebook
 
Zend Server Data Caching
Zend Server Data CachingZend Server Data Caching
Zend Server Data Caching
 
Scaling in Mind (Case study of Drupal Core)
Scaling in Mind (Case study of Drupal Core)Scaling in Mind (Case study of Drupal Core)
Scaling in Mind (Case study of Drupal Core)
 
10 Things Every Plugin Developer Should Know (WordCamp Atlanta 2013)
10 Things Every Plugin Developer Should Know (WordCamp Atlanta 2013)10 Things Every Plugin Developer Should Know (WordCamp Atlanta 2013)
10 Things Every Plugin Developer Should Know (WordCamp Atlanta 2013)
 
Persistence patterns for containers
Persistence patterns for containersPersistence patterns for containers
Persistence patterns for containers
 
Scaling php applications with redis
Scaling php applications with redisScaling php applications with redis
Scaling php applications with redis
 
HTTP Caching and PHP
HTTP Caching and PHPHTTP Caching and PHP
HTTP Caching and PHP
 
Php on the desktop and php gtk2
Php on the desktop and php gtk2Php on the desktop and php gtk2
Php on the desktop and php gtk2
 
Mobile & Desktop Cache 2.0: How To Create A Scriptable Cache
Mobile & Desktop Cache 2.0: How To Create A Scriptable CacheMobile & Desktop Cache 2.0: How To Create A Scriptable Cache
Mobile & Desktop Cache 2.0: How To Create A Scriptable Cache
 
Zend framework 03 - singleton factory data mapper caching logging
Zend framework 03 - singleton factory data mapper caching loggingZend framework 03 - singleton factory data mapper caching logging
Zend framework 03 - singleton factory data mapper caching logging
 
Bottom to Top Stack Optimization with LAMP
Bottom to Top Stack Optimization with LAMPBottom to Top Stack Optimization with LAMP
Bottom to Top Stack Optimization with LAMP
 
Bottom to Top Stack Optimization - CICON2011
Bottom to Top Stack Optimization - CICON2011Bottom to Top Stack Optimization - CICON2011
Bottom to Top Stack Optimization - CICON2011
 
Hybrid Cloud PHPUK2012
Hybrid Cloud PHPUK2012Hybrid Cloud PHPUK2012
Hybrid Cloud PHPUK2012
 
Web Application Development using PHP Chapter 7
Web Application Development using PHP Chapter 7Web Application Development using PHP Chapter 7
Web Application Development using PHP Chapter 7
 

Mehr von Scott Taylor

The New York Times: Moving to GraphQL
The New York Times: Moving to GraphQLThe New York Times: Moving to GraphQL
The New York Times: Moving to GraphQLScott Taylor
 
Internationalizing The New York Times
Internationalizing The New York TimesInternationalizing The New York Times
Internationalizing The New York TimesScott Taylor
 
A Day of REST
A Day of RESTA Day of REST
A Day of RESTScott Taylor
 
REST In Action: The Live Coverage Platform at the New York Times
REST In Action: The Live Coverage Platform at the New York TimesREST In Action: The Live Coverage Platform at the New York Times
REST In Action: The Live Coverage Platform at the New York TimesScott Taylor
 
WordPress 4.4 and Beyond
WordPress 4.4 and BeyondWordPress 4.4 and Beyond
WordPress 4.4 and BeyondScott Taylor
 
2015 WordCamp Maine Keynote
2015 WordCamp Maine Keynote2015 WordCamp Maine Keynote
2015 WordCamp Maine KeynoteScott Taylor
 
Live Coverage at The New York Times
Live Coverage at The New York TimesLive Coverage at The New York Times
Live Coverage at The New York TimesScott Taylor
 
WordPress: Getting Under the Hood
WordPress: Getting Under the HoodWordPress: Getting Under the Hood
WordPress: Getting Under the HoodScott Taylor
 
WordPress Media in a post-Koop Universe
WordPress Media in a post-Koop UniverseWordPress Media in a post-Koop Universe
WordPress Media in a post-Koop UniverseScott Taylor
 
eMusic: WordPress in the Enterprise
eMusic: WordPress in the EnterpriseeMusic: WordPress in the Enterprise
eMusic: WordPress in the EnterpriseScott Taylor
 
WordPress Front End Optimizations
WordPress Front End OptimizationsWordPress Front End Optimizations
WordPress Front End OptimizationsScott Taylor
 

Mehr von Scott Taylor (11)

The New York Times: Moving to GraphQL
The New York Times: Moving to GraphQLThe New York Times: Moving to GraphQL
The New York Times: Moving to GraphQL
 
Internationalizing The New York Times
Internationalizing The New York TimesInternationalizing The New York Times
Internationalizing The New York Times
 
A Day of REST
A Day of RESTA Day of REST
A Day of REST
 
REST In Action: The Live Coverage Platform at the New York Times
REST In Action: The Live Coverage Platform at the New York TimesREST In Action: The Live Coverage Platform at the New York Times
REST In Action: The Live Coverage Platform at the New York Times
 
WordPress 4.4 and Beyond
WordPress 4.4 and BeyondWordPress 4.4 and Beyond
WordPress 4.4 and Beyond
 
2015 WordCamp Maine Keynote
2015 WordCamp Maine Keynote2015 WordCamp Maine Keynote
2015 WordCamp Maine Keynote
 
Live Coverage at The New York Times
Live Coverage at The New York TimesLive Coverage at The New York Times
Live Coverage at The New York Times
 
WordPress: Getting Under the Hood
WordPress: Getting Under the HoodWordPress: Getting Under the Hood
WordPress: Getting Under the Hood
 
WordPress Media in a post-Koop Universe
WordPress Media in a post-Koop UniverseWordPress Media in a post-Koop Universe
WordPress Media in a post-Koop Universe
 
eMusic: WordPress in the Enterprise
eMusic: WordPress in the EnterpriseeMusic: WordPress in the Enterprise
eMusic: WordPress in the Enterprise
 
WordPress Front End Optimizations
WordPress Front End OptimizationsWordPress Front End Optimizations
WordPress Front End Optimizations
 

KĂźrzlich hochgeladen

The Codex of Business Writing Software for Real-World Solutions 2.pptx
The Codex of Business Writing Software for Real-World Solutions 2.pptxThe Codex of Business Writing Software for Real-World Solutions 2.pptx
The Codex of Business Writing Software for Real-World Solutions 2.pptxMalak Abu Hammad
 
A Call to Action for Generative AI in 2024
A Call to Action for Generative AI in 2024A Call to Action for Generative AI in 2024
A Call to Action for Generative AI in 2024Results
 
Boost PC performance: How more available memory can improve productivity
Boost PC performance: How more available memory can improve productivityBoost PC performance: How more available memory can improve productivity
Boost PC performance: How more available memory can improve productivityPrincipled Technologies
 
Salesforce Community Group Quito, Salesforce 101
Salesforce Community Group Quito, Salesforce 101Salesforce Community Group Quito, Salesforce 101
Salesforce Community Group Quito, Salesforce 101Paola De la Torre
 
Unblocking The Main Thread Solving ANRs and Frozen Frames
Unblocking The Main Thread Solving ANRs and Frozen FramesUnblocking The Main Thread Solving ANRs and Frozen Frames
Unblocking The Main Thread Solving ANRs and Frozen FramesSinan KOZAK
 
Breaking the Kubernetes Kill Chain: Host Path Mount
Breaking the Kubernetes Kill Chain: Host Path MountBreaking the Kubernetes Kill Chain: Host Path Mount
Breaking the Kubernetes Kill Chain: Host Path MountPuma Security, LLC
 
Swan(sea) Song – personal research during my six years at Swansea ... and bey...
Swan(sea) Song – personal research during my six years at Swansea ... and bey...Swan(sea) Song – personal research during my six years at Swansea ... and bey...
Swan(sea) Song – personal research during my six years at Swansea ... and bey...Alan Dix
 
A Domino Admins Adventures (Engage 2024)
A Domino Admins Adventures (Engage 2024)A Domino Admins Adventures (Engage 2024)
A Domino Admins Adventures (Engage 2024)Gabriella Davis
 
08448380779 Call Girls In Friends Colony Women Seeking Men
08448380779 Call Girls In Friends Colony Women Seeking Men08448380779 Call Girls In Friends Colony Women Seeking Men
08448380779 Call Girls In Friends Colony Women Seeking MenDelhi Call girls
 
CNv6 Instructor Chapter 6 Quality of Service
CNv6 Instructor Chapter 6 Quality of ServiceCNv6 Instructor Chapter 6 Quality of Service
CNv6 Instructor Chapter 6 Quality of Servicegiselly40
 
FULL ENJOY 🔝 8264348440 🔝 Call Girls in Diplomatic Enclave | Delhi
FULL ENJOY 🔝 8264348440 🔝 Call Girls in Diplomatic Enclave | DelhiFULL ENJOY 🔝 8264348440 🔝 Call Girls in Diplomatic Enclave | Delhi
FULL ENJOY 🔝 8264348440 🔝 Call Girls in Diplomatic Enclave | Delhisoniya singh
 
🐬 The future of MySQL is Postgres 🐘
🐬  The future of MySQL is Postgres   🐘🐬  The future of MySQL is Postgres   🐘
🐬 The future of MySQL is Postgres 🐘RTylerCroy
 
Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...
Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...
Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...Drew Madelung
 
Injustice - Developers Among Us (SciFiDevCon 2024)
Injustice - Developers Among Us (SciFiDevCon 2024)Injustice - Developers Among Us (SciFiDevCon 2024)
Injustice - Developers Among Us (SciFiDevCon 2024)Allon Mureinik
 
Neo4j - How KGs are shaping the future of Generative AI at AWS Summit London ...
Neo4j - How KGs are shaping the future of Generative AI at AWS Summit London ...Neo4j - How KGs are shaping the future of Generative AI at AWS Summit London ...
Neo4j - How KGs are shaping the future of Generative AI at AWS Summit London ...Neo4j
 
[2024]Digital Global Overview Report 2024 Meltwater.pdf
[2024]Digital Global Overview Report 2024 Meltwater.pdf[2024]Digital Global Overview Report 2024 Meltwater.pdf
[2024]Digital Global Overview Report 2024 Meltwater.pdfhans926745
 
Handwritten Text Recognition for manuscripts and early printed texts
Handwritten Text Recognition for manuscripts and early printed textsHandwritten Text Recognition for manuscripts and early printed texts
Handwritten Text Recognition for manuscripts and early printed textsMaria Levchenko
 
My Hashitalk Indonesia April 2024 Presentation
My Hashitalk Indonesia April 2024 PresentationMy Hashitalk Indonesia April 2024 Presentation
My Hashitalk Indonesia April 2024 PresentationRidwan Fadjar
 
Data Cloud, More than a CDP by Matt Robison
Data Cloud, More than a CDP by Matt RobisonData Cloud, More than a CDP by Matt Robison
Data Cloud, More than a CDP by Matt RobisonAnna Loughnan Colquhoun
 
Presentation on how to chat with PDF using ChatGPT code interpreter
Presentation on how to chat with PDF using ChatGPT code interpreterPresentation on how to chat with PDF using ChatGPT code interpreter
Presentation on how to chat with PDF using ChatGPT code interpreternaman860154
 

KĂźrzlich hochgeladen (20)

The Codex of Business Writing Software for Real-World Solutions 2.pptx
The Codex of Business Writing Software for Real-World Solutions 2.pptxThe Codex of Business Writing Software for Real-World Solutions 2.pptx
The Codex of Business Writing Software for Real-World Solutions 2.pptx
 
A Call to Action for Generative AI in 2024
A Call to Action for Generative AI in 2024A Call to Action for Generative AI in 2024
A Call to Action for Generative AI in 2024
 
Boost PC performance: How more available memory can improve productivity
Boost PC performance: How more available memory can improve productivityBoost PC performance: How more available memory can improve productivity
Boost PC performance: How more available memory can improve productivity
 
Salesforce Community Group Quito, Salesforce 101
Salesforce Community Group Quito, Salesforce 101Salesforce Community Group Quito, Salesforce 101
Salesforce Community Group Quito, Salesforce 101
 
Unblocking The Main Thread Solving ANRs and Frozen Frames
Unblocking The Main Thread Solving ANRs and Frozen FramesUnblocking The Main Thread Solving ANRs and Frozen Frames
Unblocking The Main Thread Solving ANRs and Frozen Frames
 
Breaking the Kubernetes Kill Chain: Host Path Mount
Breaking the Kubernetes Kill Chain: Host Path MountBreaking the Kubernetes Kill Chain: Host Path Mount
Breaking the Kubernetes Kill Chain: Host Path Mount
 
Swan(sea) Song – personal research during my six years at Swansea ... and bey...
Swan(sea) Song – personal research during my six years at Swansea ... and bey...Swan(sea) Song – personal research during my six years at Swansea ... and bey...
Swan(sea) Song – personal research during my six years at Swansea ... and bey...
 
A Domino Admins Adventures (Engage 2024)
A Domino Admins Adventures (Engage 2024)A Domino Admins Adventures (Engage 2024)
A Domino Admins Adventures (Engage 2024)
 
08448380779 Call Girls In Friends Colony Women Seeking Men
08448380779 Call Girls In Friends Colony Women Seeking Men08448380779 Call Girls In Friends Colony Women Seeking Men
08448380779 Call Girls In Friends Colony Women Seeking Men
 
CNv6 Instructor Chapter 6 Quality of Service
CNv6 Instructor Chapter 6 Quality of ServiceCNv6 Instructor Chapter 6 Quality of Service
CNv6 Instructor Chapter 6 Quality of Service
 
FULL ENJOY 🔝 8264348440 🔝 Call Girls in Diplomatic Enclave | Delhi
FULL ENJOY 🔝 8264348440 🔝 Call Girls in Diplomatic Enclave | DelhiFULL ENJOY 🔝 8264348440 🔝 Call Girls in Diplomatic Enclave | Delhi
FULL ENJOY 🔝 8264348440 🔝 Call Girls in Diplomatic Enclave | Delhi
 
🐬 The future of MySQL is Postgres 🐘
🐬  The future of MySQL is Postgres   🐘🐬  The future of MySQL is Postgres   🐘
🐬 The future of MySQL is Postgres 🐘
 
Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...
Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...
Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...
 
Injustice - Developers Among Us (SciFiDevCon 2024)
Injustice - Developers Among Us (SciFiDevCon 2024)Injustice - Developers Among Us (SciFiDevCon 2024)
Injustice - Developers Among Us (SciFiDevCon 2024)
 
Neo4j - How KGs are shaping the future of Generative AI at AWS Summit London ...
Neo4j - How KGs are shaping the future of Generative AI at AWS Summit London ...Neo4j - How KGs are shaping the future of Generative AI at AWS Summit London ...
Neo4j - How KGs are shaping the future of Generative AI at AWS Summit London ...
 
[2024]Digital Global Overview Report 2024 Meltwater.pdf
[2024]Digital Global Overview Report 2024 Meltwater.pdf[2024]Digital Global Overview Report 2024 Meltwater.pdf
[2024]Digital Global Overview Report 2024 Meltwater.pdf
 
Handwritten Text Recognition for manuscripts and early printed texts
Handwritten Text Recognition for manuscripts and early printed textsHandwritten Text Recognition for manuscripts and early printed texts
Handwritten Text Recognition for manuscripts and early printed texts
 
My Hashitalk Indonesia April 2024 Presentation
My Hashitalk Indonesia April 2024 PresentationMy Hashitalk Indonesia April 2024 Presentation
My Hashitalk Indonesia April 2024 Presentation
 
Data Cloud, More than a CDP by Matt Robison
Data Cloud, More than a CDP by Matt RobisonData Cloud, More than a CDP by Matt Robison
Data Cloud, More than a CDP by Matt Robison
 
Presentation on how to chat with PDF using ChatGPT code interpreter
Presentation on how to chat with PDF using ChatGPT code interpreterPresentation on how to chat with PDF using ChatGPT code interpreter
Presentation on how to chat with PDF using ChatGPT code interpreter
 

Cloud, Cache, and Configs at WordCamp NYC 2012

  • 1. Cloud, Cache, and Congs WordCamp NYC 2012 Saturday, June 9, 12
  • 2. Scott Taylor Lead PHP Developer, eMusic @wonderboymusic www.scotty-t.com #projectmanagementsoftware Saturday, June 9, 12
  • 3. eMusic Multisite Regionalized Content Regionalized Caching Shared Content across Sites Tons of Custom Post Types Post Formats Tons of Web Services Saturday, June 9, 12
  • 4. eMusic Architecture • 12-24 Amazon EC2 instances (CentOS) • 4 MySQL (1 Write, 3 Read) • 4 Memcached (~28GB RAM) • Amazon S3 / Cloudfront CDN • PHP 5.3 / MySQL 5.5 • PECL: APC, Memcached, HTTP • Batcache Saturday, June 9, 12
  • 5. APC = duh • Essential for PHP • Opcode cache • apc.shm_size = 64M or higher Saturday, June 9, 12
  • 6. Logs • Never allow any PHP-related notices, errors, warnings, exceptions, etc • Check access logs regularly for 404s, 500s etc • make a constant for toggling debug logging Saturday, June 9, 12
  • 7. Production Data • Always pull down, never push up • We only push imported legacy content up • output buffering • code ltering • more to come on this.... Saturday, June 9, 12
  • 8. Output Buffering ob_start( $callback ) ob_start(); echo ‘Daryl’; $daryl = ob_get_clean(); Saturday, June 9, 12
  • 9. ob_start( function ( $data ) { return str_replace( $urls_array, EMUSIC_CURRENT_HOST, $data ); } ); Saturday, June 9, 12
  • 10. Congs • Mandatory machine congs • HyperDB cong • Overridable sunrise.php Saturday, June 9, 12
  • 11. Machine Cong • DB credentials local to that machine • Amazon S3 bucket • Web Service Endpoints • Memcached Servers if ( le_exists( ‘/wp-cong/cong.php’ ) ) { require_once( ‘/wp-cong/cong.php’ ); } else { die( ‘You must have a local cong!’ ); } Saturday, June 9, 12
  • 12. Memcached • Memcached PHP Extension (not Memcache) • Can be used locally (127.0.0.1) • Memcached Redux supports wp_cache_get_/set_multi( ) • Johnny Cache Saturday, June 9, 12
  • 13. Batcache • Full-page caching • Can be congured • You can partition cache by unique values • Loads before plugins - any code you need has to be duped or added early (sunrise.php) Saturday, June 9, 12
  • 14. class batcache { // This is the base configuration. You can edit these variables or move them into your wp-config.php file. var $max_age = 300; // Expire batcache items aged this many seconds (zero to disable batcache) var $remote = 0; // Zero disables sending buffers to remote datacenters (req/sec is never sent) var $times = 5; // Only batcache a page after it is accessed this many times... (two or more) var $seconds = 120; // ...in this many seconds (zero to ignore this and use batcache immediately) var $group = 'batcache'; // Name of memcached group. You can simulate a cache flush by changing this. var $unique = array( BATCACHE_REGION, BATCACHE_COUNTRY ); // If you conditionally serve different content, put the variable values here. var $headers = array( 'X-nananana' => 'Batcache' ); . . . . } Saturday, June 9, 12
  • 15. Sunrise • Used to alter multisite context • sets $current_blog and $current_site • lters all URL functions to resolve all URLs to your current domain • registers custom locations for media • lters Admin URLs Saturday, June 9, 12
  • 16. switch_to_blog( $blog_id ) • All dynamic functions need to account for this • Shared content needs to resolve proper URLs • Different sites have different media locations Saturday, June 9, 12
  • 17. Fix switch_to_blog() $current_blog = new stdClass(); $current_blog->site_id = 1; $current_blog->archived = 0; $current_blog->mature = 0; $current_blog->spam = 0; $current_blog->deleted = 0; $current_blog->lang_id = 0; $current_blog->public = 1; $current_blog->registered = '2011-02-20 03:38:22'; $current_blog->last_updated = $_SERVER['REQUEST_TIME']; $current_blog->domain = EMUSIC_CURRENT_HOST; function emusic_switch_to_blog( $blog_id, $prev_blog_id = 0 ) { if ( $blog_id === $prev_blog_id ) return; global $current_blog, $emusic_paths; $current_blog->blog_id = $blog_id; $current_blog->path = $emusic_paths[$blog_id]; } emusic_switch_to_blog( $the_id ); add_action( 'switch_blog', 'emusic_switch_to_blog', 10, 2 ); $blog_id = $the_id; $site_id = 1; $current_site = new stdClass(); $current_site->blog_id = $the_id; $current_site->id = 1; $current_site->domain = EMUSIC_CURRENT_HOST; $current_site->site_name = 'eMusic'; $current_site->path = $the_path; Saturday, June 9, 12
  • 18. Filter URLs add_filter( 'pre_option_upload_path', function () { $id = get_current_blog_id(); if ( 1 < $id ) return $_SERVER['DOCUMENT_ROOT'] . "/blogs/{$id}/files"; return $_SERVER['DOCUMENT_ROOT'] . '/' . EMUSIC_UPLOADS; } ); add_filter( 'pre_option_upload_url_path', function () { $id = get_current_blog_id(); if ( 1 < $id ) return 'http://' . EMUSIC_CURRENT_HOST . "/blogs/{$id}/files"; return 'http://' . EMUSIC_CURRENT_HOST . '/' . EMUSIC_UPLOADS; } ); add_filter( 'pre_option_siteurl', function () { global $current_blog; $extra = rtrim( $current_blog->path, '/' ); return 'http://' . EMUSIC_CURRENT_HOST . $extra; } ); add_filter( 'pre_option_home', function () { global $current_blog; $extra = rtrim( $current_blog->path, '/' ); return 'http://' . EMUSIC_CURRENT_HOST . $extra; } ); Saturday, June 9, 12
  • 19. Plugins • Filter active network plugins (don’t rely on database being correct) • Filter each site’s plugins (if you have a manageable number) • Use classes, not a bunch of functions • Extend before you copy / paste Saturday, June 9, 12
  • 20. Site Congs require_once( 'site-configs/global.php' ); if ( $the_id > 1 ) { define( 'UPLOADBLOGSDIR', 0 ); define( 'UPLOADS', 0 ); define( 'BLOGUPLOADDIR', $_SERVER['DOCUMENT_ROOT'] . "/blogs/{$the_id}/files" ); switch ( $the_id ) { case 2: require_once( 'site-configs/bbpress.php' ); break; case 3: require_once( 'site-configs/dots.php' ); break; case 5: require_once( 'site-configs/support.php' ); break; } add_filter( 'pre_option_template', function () { return 'dark'; } ); } else { require_once( 'site-configs/emusic.php' ); } Saturday, June 9, 12
  • 21. Global Plugins add_filter( 'pre_site_option_active_sitewide_plugins', function () { return array( 'batcache/batcache.php' => 1, 'akismet/akismet.php' => 1, 'avatar/avatar.php' => 1, 'bundle/bundle.php' => 1, 'cloud/cloud.php' => 1, 'download/download.php' => 1, 'emusic-notifications/emusic-notifications.php' => 1, 'emusic-ratings/emusic-ratings.php' => 1, 'emusic-xml-rpc/emusic-xml-rpc.php' => 1, 'johnny-cache/johnny-cache.php' => 1, 'like-buttons/like-buttons.php' => 1, 'members/members.php' => 1, //'minify/minify.php' => 1, 'apc-admin/apc-admin.php' => 1 //'debug-bar/debug-bar.php' => 1 ); } ); Saturday, June 9, 12
  • 22. Site Plugins add_filter( 'pre_option_active_plugins', function () { return array( 'artist-images/artist-images.php', 'catalog-comments/catalog-comments.php', 'emusic-post-types/emusic-post-types.php', 'discography.php', 'emusic-radio/emusic-radio.php', 'gravityforms/gravityforms.php', 'super-ghetto/super-ghetto.php' //,'theme-check/theme-check.php' ); } ); Saturday, June 9, 12
  • 23. class MyPlugin { function init() { add_action( ‘init’, array( $this, ‘register’ ) ); } function register() {} } $my_plugin = new MyPlugin(); $my_plugin->init(); class MyPlugin { function init() { add_action( ‘init’, array( ‘MyPlugin’, ‘register’ ) ); } function register() {} } MyPlugin::init(); Saturday, June 9, 12
  • 24. Share code when possible class FeaturePack extends eMusicPostTypes implements PostType { .......... } Saturday, June 9, 12
  • 25. MySQL • Use 5.5, better handlng of weird multibyte strings • Use InnoDB, not MyISAM, pretty much in all cases • HyperDB handles scaling for you • Benchmark queries, don’t be afraid to roll your own SQL, tables, use $wpdb Saturday, June 9, 12
  • 26. HyperDB • Can be empty locally • Inherits wp-cong DB defaults • contains functions for replication lag detection (when used with mk-heartbeat) Saturday, June 9, 12
  • 27. Themes • Theme setup is a class • Extend before you repeat • Classes are better than prexing function names Saturday, June 9, 12
  • 28. Theme Cong in functions.php class Theme_17Dots extends Regionalization { function __construct() { global $dots_regions_tax_map, $dots_regions_map; $this->regions_map = $dots_regions_map; $this->regions_tax_map = $dots_regions_tax_map; parent::__construct(); } function init() { add_action( 'init', array( $this, 'register' ) ); add_action( 'after_setup_theme', array( $this, 'setup' ) ); add_action( 'add_meta_boxes_post', array( $this, 'boxes' ) ); add_action( 'save_post', array( $this, 'save' ), 10, 2 ); add_filter( 'embed_oembed_html', '_feature_youtube_add_wmode' ); } . . . . . . . } Saturday, June 9, 12
  • 29. Use base classes class Regionalization { var $regions_map; function __construct() { add_filter( 'manage_posts_columns', array( $this, 'manage_columns' ) ); add_action( 'manage_posts_custom_column', array( $this, 'manage_custom_column' ), 10, 2 ); add_filter( 'posts_clauses', array( $this, 'clauses' ), 10, 2 ); add_filter( 'manage_edit-post_sortable_columns',array( $this, 'sortables' ) ); add_filter( 'pre_get_posts', array( $this, 'pre_posts' ) ); } . . . . . . } Saturday, June 9, 12
  • 30. pre_get_posts function regionalize( $query ) { global $regions_map; if ( $query->is_main_query() && !is_admin() && ( is_search() || is_archive() || is_home() ) ) { $types = get_post_types( array( 'publicly_queryable' => true, '_builtin' => false ) ); $tax_region = array( 'taxonomy' => 'region', 'field' => 'term_id', 'terms' => array( $regions_map[ 'ALL' ], $regions_map[ THE_REGION ] ), 'operator' => 'IN' ); if ( is_home() ) { $query->set( 'posts_per_page', 10 ); } else if ( is_search() && get_option( 'editorial_search_enabled' ) ) { $ctx = get_query_var( 'search_context' ); $query->set( 's', stripslashes( urldecode( get_query_var( 's' ) ) ) ); if ( in_array( $ctx, array( 'features', 'features-books' ) ) ) { $query->set( 'posts_per_page', 48 ); } else { $query->set( 'posts_per_page', 12 ); } if ( in_array( $ctx, array( 'books', 'features-books' ) ) ) { foreach ( $types as $type ) if ( false === strpos( $type, 'book' ) ) unset( $types[$type] ); } if ( empty( $query->posts ) && $query->is_paged() ) $query->is_paged = false; } else if ( is_tag() ) { if ( empty( $query->posts ) && $query->is_paged() ) $query->is_paged = false; } //error_log( 'REGIONALIZING' ); if ( !is_post_type_archive() ) $query->set( 'post_type', array_keys( $types ) ); $query->set( 'tax_query', array( $tax_region ) ); } return $query; } Saturday, June 9, 12
  • 31. Assets • Use remote storage • Use a CDN • Replace hosts using output buffer • Cloud - pieces of W3 Total Cache Saturday, June 9, 12
  • 32. Minify • JS / CSS concatenation speed up your front-end loading / perceived loading • Minify is automagic • HTML5 CSS properties are automatically inflated • Admin tool to cache-bust URLs • Auto-locking while les are generated Saturday, June 9, 12
  • 33. Web Services • cURL PHP extension • curl and curl_multi() • Memcached is essential • hooks into parse_request to load data along with WordPress, allows us to cause WP to 404 and bail early when required data response fails • Parallelization with curl_multi() Saturday, June 9, 12
  • 34. class eMusicRequest { var $request; var $sub_request; var $path; var $page; function load( $page = '' ) { if ( !empty( $page ) ) $this->page = $page; $le = $this->path . $this->page . '.php'; if ( le_exists( $le ) ) require_once( $le ); } function parse() { $requests = array( $this->request, $this->sub_request ); foreach ( $requests as $request ) { if ( !empty( $request ) ) { $keys = array_keys( get_class_vars( get_class( $request ) ) ); foreach ( $keys as $var ) { if ( !empty( $request->$var ) ) { $GLOBALS[$var] = $request->$var; } } } } } } Saturday, June 9, 12
  • 35. class DarkRequest extends eMusicRequest { var $genre; var $post_type; function init( $request ) { global $_GENRES; $vars =& $request->query_vars; ......... } } Saturday, June 9, 12
  • 36. switch ( get_current_blog_id() ) { case 1: $_dark_request = new DarkRequest(); add_action( 'parse_request', array( $_dark_request, 'init' ) ); break; case 4: $_my_emusic_request = new MyEMusicRequest(); add_action( 'parse_request', array( $_my_emusic_request, 'init' ) ); break; } Saturday, June 9, 12
  • 37. <?php class HomeRequest extends RequestMap { var $recommendations; function __construct() { parent::__construct(); if ( !get_option( 'recs_enabled' ) ) return; if ( is_user_logged_in() && 'US' === THE_REGION ) { $user = wp_get_current_user(); $user_id = isset( $_GET['user_id'] ) ? $_GET['user_id'] : $user->ID; $params = array( 'userId' => $user_id, 'return' => true ); $this->add( get_user_recommendations( $params ), array( $this, 'parse_recommendations' ) ); $this->send(); } } function parse_recommendations( $data ) { if ( empty( $data['recommendations'] ) ) return; $data = $data['recommendations']; if ( !empty( $data ) && isset( $data[key($data)]['items'] ) && !empty( $data[key($data)]['items'] ) ) { $this->recommendations = array_slice( $data[key($data)]['items'], 0, 18 ); shuffle( $this->recommendations ); foreach ( $this->recommendations as &$rec ) { $rec = array( 'work_id' => $rec['catalogId'] ); } } } } Saturday, June 9, 12
  • 38. <?php class RequestMap extends API { private $requests; private $responses; private $ttl; private $useCache = true; protected $error = false; public function __construct() { $this->flush(); } public function is_error() { return $this->error; } public function flush() { $this->requests = array(); } public function getTtl() { if ( empty( $this->ttl ) ) { $this->ttl = CACHE::API_CACHE_TTL; } return $this->ttl; } public function add( $url, $callback, $vars = array() ) { $params = new stdClass(); $params->url = $url; $params->callback = $callback; $params->params = (array) $vars; $this->requests[] = $params; } private function exec( $item, $response ) { $params = array_merge( array( $response ), $item->params ); call_user_func_array( $item->callback, $params ); } public function send() { if ( !empty( $this->requests ) ) { $this->responses = self::batch( $this->getRequestUrls(), $this->getTtl(), $this->useCache ); if ( is_array( $this->responses ) ) { foreach ( $this->responses as $i => $response ) { if ( !empty( $this->requests[$i] ) ) { $this->exec( $this->requests[$i], self::parse_response( $response ) ); } } } } } } Saturday, June 9, 12
  • 39. class API { public static function batch( $urls, $ttl = '', $usecache = true ) { $response = array(); ob_start(); if ( empty( $ttl ) ) { $ttl = CACHE::API_CACHE_TTL; } if ( is_array( $urls ) ) { if ( $usecache ) { foreach ( $urls as $index => $url ) { $in = Cache::get( Cache::API, $url ); if ( $in ) { $response[$index] = $in; unset( $urls[$index] ); } } } $keys = array_keys( $urls ); } $calls = self::multi_request( $urls ); . . . . . . . . Saturday, June 9, 12
  • 40. if ( is_array( $calls ) && count( $calls ) > 0 ) { $calls = array_combine( $keys, array_values( $calls ) ); foreach ( $calls as $index => $c ) { if ( $c ) { $response[$index] = self::parse_response( $c ); if ( isset( $response[$index]['status']['code'] ) && $response[$index] ['status']['code'] < 400 ) { if ( isset( $response[$index]['results'] ) && ! empty( $response[$index]['results'] ) ) { Cache::put( Cache::API, $urls[$index], $response[$index], $ttl ); } else { Cache::put( Cache::API, $urls[$index], $response[$index], $ttl ); } } else if ( !isset( $response[$index]['status']['code'] ) ) { Cache::put( Cache::API, $urls[$index], $response[$index], $ttl ); } } else { $response[$index] = null; } } } else if ( !empty( $calls ) && isset( $urls[0] ) ) { $data = self::parse_response( $calls ); if ( isset( $data['status']['code'] ) && $data['status']['code'] < 400 ) { if ( isset( $data['results'] ) && !empty( $data['results'] ) ) { Cache::put( Cache::API, $urls[0], $data, $ttl ); } else { Cache::put( Cache::API, $urls[0], $data, $ttl ); } } else if ( !isset( $data['status']['code'] ) ) { Cache::put( Cache::API, $urls[0], $data, $ttl ); } $response[] = $data; } ob_end_clean(); return $response; Saturday, June 9, 12
  • 41. Because we used classes, everything is abstracted Saturday, June 9, 12
  • 42. Custom Authentication • WP stores slashed passwords • wp_authentication arguments are slashed • Inherit your base system’s rules • DO NOT sanitize email / password (WordPress does by default) • User tables in this case are really a transient cache Saturday, June 9, 12