php[world] 2015
Washington, D.C.
Custom Cache
Custom Cache
Redis Cache
Custom Cache
Redis Cache
Fallback Cache
@sprak
github.com/sprak3000
TR
CKR
TR
CKR
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
traackr/cakephp-cache-engines
dvlp
composer require traackr/cache-engines
cake3
Thank You
https://slides.com/sprak/cache-engine
https://joind.in/talk/view/14777
Build a wicked smaht cache engine - php[world] 2015
By Luis Cruz
Build a wicked smaht cache engine - php[world] 2015
Build a wicked smaht cache engine
- 1,768