Introduction to StashPHP

Written by Pim on Friday November 20, 2015 - Comment - Permalink
Categories: php, technology - Tags: php, symfony2, cache

Stash is a PHP library that makes it easy to cache the results of expensive code, like database queries or external API calls, in a hierarchical way with interchangeable back ends.

Why you should use Stash as your caching system

There are only two hard things in Computer Science: cache invalidation and naming things.
-- Phil Karlton

Naming things and cache invalidation was the main problem I encountered in a large PHP application with a lot of expensive code, like data aggregation and database queries. Caching these functions was rather easy and the first approach was to invalidate on TTL expiration which initially worked out OK... until the product owner changed some settings and he had to wait for the invalidation ;-)

So I had to invalidate these cache keys to do some new calculations, and this is a very hard thing if you do a lot of caching without a consistent naming strategy. The solution would be simple: use a file/directory based naming strategy and invalidate paths recursively.

This is where Stash comes into play! Stash enables me to cache data in a hierarchical way, like the folders in a file system, which allows me to group related items together and invalidate a group if something changes.

Example: I have a user with settings and I cache these settings in 'user/{userId}/settings'. If I want to invalidate the whole user I can invalid 'user/{userId}' and every cache item in this "folder" is invalidated.

There is more...

  • Stash supports interchangeable back ends: In Memory (the default), Memcache(d), Redis, APC and the file system
  • Stash supports even multiple back ends by using the multi driver!
  • Use the BlackHole driver for unit testing
  • It prevents dog piling or cache stampede by locking a cache item when refreshing it
  • Optionally regenerate cache items before it misses

Stash in action!

Stash consists of Pools with an injected Driver (or more drivers when using the MultiDriver) containing Items.

The Driver class implements a caching system and performs the actual get, set, delete, etc. task.

The Pool class is a specific grouping of cache items which you can perform a few tasks on like flushing, purging and creating items.

The Item class represents a single cache item in a pool having a unique key and can perform tasks like getting, setting and removing a cache item from the caching software used.

Everything glued together gives you a powerful cache implementation for your heavy used PHP application:

// Create a new pool and use Memcache as driver
$pool = new Pool(new Memcache([['127.0.0.1', '11211']]));
// Set a namespace for this pool
$pool->setNamespace('test');

// Get a cache representation from the pool
$test123 = $pool->getItem('model/test/123');

// Get the value from the storage (Memcache)
$data = $test123->get();

// Check if it is a hit or a miss
if ($test123->isMiss() === true) {
    // Lock the cache item to prevent dog piling
    $test123->lock();
    // Expensive function call
    $data = 'stuff';
    // Set the data and done!
    $test123->set($data);
}

return $data;

Nested invalidation is possible if the keys are defined as a file based system, in my eyes the real power of Stash:

$userDetail1 = $pool->getItem('model/user/profile');
$userDetail1->set('some data');
$userDetail2 = $pool->getItem('model/user/info');
$userDetail2->set('some data');
$userDetail3 = $pool->getItem('model/user/messages');
$userDetail3->set('some data');
$userDetail4 = $pool->getItem('model/user/settings');
$userDetail4->set('some data');

// Invalidate everything with one single invalidation call
$pool->getItem('model/user')->clear();

Stash details

The Stash PHP library and Stash Symfony2 Bundle are hosted on Github and can be installed with Composer tedivm/stash or tedivm/stash-bundle. Documentation is available at http://www.stashphp.com/.

I did a quick test with the Silex micro-framework and you can find my code at Github.