Cakefest 2015

New York, NY

@dchancogne

TR

CKR

TR

CKR

Custom Cache

Custom Cache

Redis Cache

Custom Cache

Redis Cache

Fallback Cache

traackr/cakephp-cache-engines

dvlp

packages/traackr/cache-engines

cake3

CUSTOM

CACHE ENGINE

class 
CacheEngine
class CustomCacheEngine
class Cake\Cache\
CacheEngine
class CustomCacheEngine

CakePHP 3.0

write
($key,$value)
read
increment
::
($key)
($key,$offset=1)
::
::
delete
($key)
::
decrement
($key,$offset=1)
::
clear
($check)
::
clearGroup
($group)
::
key
($key)
::

Project  Name

project_name
init
($config=array())
::
/**
 * Initialize the cache engine
 *
 * Called automatically by the cache frontend.
 * Merge the runtime config with the defaults
 * before use.
 *
 * @param array $config Associative array of parameters
 *    for the engine
 * @return bool True if the engine has been successfully
 *    initialized, false if not
 */
init
($config=array())
::
Plugin/<plugin-name>

composer/installers

+

vendor/<vendor>/<plugin>

cakephp/plugin-installer

CakePHP 3.0

+

Recap

Extend CacheEngine class

Implement init() & key()

Install as plugin

A Redis

Cache Engine

Cache

Invalidation

project:<id>:<data>

PROJECT

123

456

META

ASSETS

USERS

/

\

/

\

/

\

|

|

...

...

project : 123 : users

project : 123 : assets

project : 123 : meta

project : 123 : meta

project : 123 : assets

project : 123 : users

PROJECT

123

456

META

ASSETS

USERS

/

\

/

\

/

\

|

|

...

...

project : 123 : *

project : 123 : users

project : 123 : assets

project : 123 : meta

project : 123

project : 123 : users

project : 123 : assets

project : 123 : meta

RedisTree

Cache Engine

delete
("project:123")
::

SET

key   value

GET

key

DEL

key  [key ...]

KEYS

pattern

SADD

key member

KEYS

pattern

public function write($key, $value) {
      
   $key_elms = explode(":", $key);

   // Create an array of all nodes
   $path = '';
   for ( $i = 0; $i < sizeof($key_elms)-1; $i++) {
     $path .= ( $i == 0 ? '' : ":" ) . $key_elms[$i];
     $this->redis->sadd("redis_tree_nodes", $path);

   }
   
   return $this->redis->set($key, $value);
      
} // End function write()
public function write($key, $value) {
      
   
   
   
   
   
     
     
     
   
   
   
      
} // End function write()
public function write($key, $value) {
      
   
   
   
  

     
     
    
  
   
   return $this->redis->set($key, $value);
      
} // End function write()
public function write($key, $value) {
      
   $key_elms = explode(":", $key);
   
   
  
   
     
     
     
   
   
   return $this->redis->set($key, $value);
      
} // End function write()
public function write($key, $value) {
      
   $key_elms = explode(":", $key);
   
   
   
   
     
     $this->redis->sadd("redis_tree_nodes", $path);
     
  
   
   return $this->redis->set($key, $value);
      
} // End function write()

Redis Tree Cache Engine

Handles all nodes in key

Redis Tree Cache Engine

project : 123 : users : dave

project,

project:123, project:123:users

public function delete($key) {
   
   // Determine if the key is a node
   if ( $this->redis->sismember("redis_tree_nodes", $key) === 1 ) {
     $key_node = $key;
     $key .= ':*';
   }
   
   // Retrieve all keys to delete
   $keys = $this->redis->keys($key);

   // Delete node from list of nodes
   $this->redis->srem("redis_tree_nodes", $key_node);

   // Delete
   return $this->redis->del($keys);
   
} // End function delete()
public function delete($key) {
   
   
   
     
     
   
   
  
   

   
   

   // Delete
   return $this->redis->del($keys);
   
} // End function delete()
public function delete($key) {
   
   // Determine if the key is a node
   if ( $this->redis->sismember("redis_tree_nodes", $key) === 1 ) {
     
     
   }
   
   
   

   
  

   // Delete
   return $this->redis->del($keys);
   
} // End function delete()
public function delete($key) {
   
   // Determine if the key is a node
   if ( $this->redis->sismember("redis_tree_nodes", $key) === 1 ) {
     $key_node = $key;
     $key .= ':*';
   }
   
   
   

   
  

   // Delete
   return $this->redis->del($keys);
   
} // End function delete()
public function delete($key) {
   
   // Determine if the key is a node
   if ( $this->redis->sismember("redis_tree_nodes", $key) === 1 ) {
     $key_node = $key;
     $key .= ':*';
   }
   
   // Retrieve all keys to delete
   $keys = $this->redis->keys($key);

   
   

   // Delete
   return $this->redis->del($keys);
   
} // End function delete()

Redis Tree Cache Engine

"Consider KEYS as a command that should only be used in production environments with extreme care"

clear
($check)
::

SCAN

Recap

Facilitate cache invalidation

write() & delete()

Production considerations

Fallback

Cache Engine

Fallback

Cache Engine

Fallback

Cache Engine

Secondary

passive

Primary

active

/

\

Secondary

active

Fallback

Cache Engine

Secondary

FILE

Primary

REDIS

/

\

Distributed, uncertain & fast      vs      Server side, reliable & slower



public function init($settings = array()) {








} // End function init()


public function init($settings = array()) {

   parent::init($settings);


 

 

} // End function init()


public function init($settings = array()) {

   parent::init($settings);

   Cache::config("Primary", $this->settings['primary']); 
   Cache::config("Secondary", $this->settings['secondary']);



} // End function init()


public function init($settings = array()) {

   parent::init($settings);

   Cache::config("Primary", $this->settings['primary']); 
   Cache::config("Secondary", $this->settings['secondary']);

   $this->activeCache = "Primary";

} // End function init()


public function write($key, $value) {
    








} // End function write()


public function write($key, $value) {
    

      return Cache::write($key, $value, $this->activeCache);






} // End function write()


public function write($key, $value) {
    
   try {
      return Cache::write($key, $value, $this->activeCache);
   }
   catch (Exception $e) {


   }

} // End function write()


public function write($key, $value) {
    
   try {
      return Cache::write($key, $value, $this->activeCache);
   }
   catch (Exception $e) {
      $this->fallback();

   }

} // End function write()


public function write($key, $value) {
    
   try {
      return Cache::write($key, $value, $this->activeCache);
   }
   catch (Exception $e) {
      $this->fallback();
      return Cache::write($key, $value, $this->activeCache);
   }

} // End function write()


protected function fallback($setPrimary = false) {

   if ( $setPrimary )
      $this->activeCache = "Primary";
   else
      $this->activeCache = "Secondary";

} // End function fallback()
// Sessions caching
Cache::config('sessions', array(
   'engine' => "Fallback",
   'name' => 'sessions',
   'primary' => array(
      'engine' => "RedisTree",
      'server' => "redis-server"
      'prefix' => 'app:sessions:'
   ),
   'secondary' => array(
      // alternate cache if Redis fails
      'engine' => "FileTree",
      'path' => TMP.'/sessions/',
      'prefix' => 'session_'
   )
));
// Sessions caching
Cache::config('sessions', array(
   'engine' => "Fallback",

   'primary' => array(



   ),
   'secondary' => array(




   )
));
// Sessions caching
Cache::config('sessions', array(
   'engine' => "Fallback",
   'name' => 'sessions',
   'primary' => array(
      'engine' => "RedisTree",
      'server' => "redis-server"
      'prefix' => 'app:sessions:'
   ),
   'secondary' => array(
      // alternate cache if Redis fails
      'engine' => "FileTree",
      'path' => TMP.'/sessions/',
      'prefix' => 'session_'
   )
));

Recap

Simple, fault tolerant cache

Active + Passive approach

Thank You
https://slides.com/dchancogne/cache-engine
Made with Slides.com