Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Caching in Symfony: An overview

Caching in Symfony: An overview

This talk is divided into 2 blocks. The first one takes a look at Symfony's Cache component and the underlying PSR-6 and PSR-16 standards. The second one is a quick glance at HTTP-Caching & ESI in Symfony.

Denis Brumann

October 27, 2017
Tweet

More Decks by Denis Brumann

Other Decks in Programming

Transcript

  1. The Cache component provides a strict PSR-6 implementation for adding

    cache to your applications. Denis Brumann // @dbrumann // Caching in Symfony SYMFONY 3.1 The Cache component provides an extended PSR-6 implementation as well as a PSR-16 "Simple Cache" implementation for adding cache to your applications. SYMFONY 3.3
  2. Denis Brumann // @dbrumann // Caching in Symfony ADAPTER •

    Symfony‘s terminology for concrete cache backends. • Configured once and then reused by pools • Never used directly by the end-user • Developers interact with adapter through a pool
  3. Denis Brumann // @dbrumann // Caching in Symfony APCU ADAPTER

    It is not recommended to use this adapter when performing a large number of write and delete operations, as these operations result in fragmentation of the APCu memory, resulting in significantly degraded performance.
 Note that this adapter's CRUD operations are specific to the PHP SAPI it is running under. This means adding a cache item using the CLI will not result in the item appearing under FPM. Likewise, deletion of an item using CGI will not result in the item being deleted under the CLI. https://symfony.com/doc/current/components/cache/adapters/apcu_adapter.html
  4. Denis Brumann // @dbrumann // Caching in Symfony MEMCACHED &

    REDIS ADAPTER • Unlike APCu adapter not limited to the current server's shared memory • Ability to utilize a cluster of servers to provide redundancy and/or fail-over
  5. Denis Brumann // @dbrumann // Caching in Symfony FILESYSTEM ADAPTER

    • Useful when APCu, Redis or Memcached are not available on the server • Stores content on the local filesystem This adapter is generally the slowest due to the overhead of file IO
  6. Denis Brumann // @dbrumann // Caching in Symfony ARRAY ADAPTER

    • Intended for testing purposes • Items are stored in memory and not persisted outside the running process
  7. Denis Brumann // @dbrumann // Caching in Symfony CHAIN ADAPTER

    • Combine any number of other available adapters • Items are fetched from the first adapter with hit • Items are saved in all the given adapters
  8. Denis Brumann // @dbrumann // Caching in Symfony PSR-16 INTEROPERABILITY

    • From PSR-16 to PSR-6:
 SimpleCacheAdapter (wraps PSR-16 CacheInterface)
  9. The goal of this PSR is to allow developers to

    create cache-aware libraries that can be integrated into existing frameworks and systems without the need for custom development. http://www.php-fig.org/psr/psr-6/ Denis Brumann // @dbrumann // Caching in Symfony GOALS
  10. Denis Brumann // @dbrumann // Caching in Symfony • Compatibility

    with all existing caching implementations • Advanced caching features such as namespaces and tags NON-GOALS
  11. The Pool represents a collection of items in a caching

    system. The pool is a logical Repository of all items it contains. All cacheable items are retrieved from the Pool as an Item object, and all interaction with the whole universe of cached objects happens through the Pool. http://www.php-fig.org/psr/psr-6/ Denis Brumann // @dbrumann // Caching in Symfony CACHE ITEM POOL
  12. http://www.php-fig.org/psr/psr-6/ Denis Brumann // @dbrumann // Caching in Symfony CACHE

    ITEM An Item represents a single key/value pair within a Pool. The key is the primary unique identifier for an Item and MUST be immutable. The Value MAY be changed at any time.
  13. http://www.php-fig.org/psr/psr-6/ Denis Brumann // @dbrumann // Caching in Symfony DEFERRED

    SAVE A deferred cache save indicates that a cache item may not be persisted immediately by the pool. A Pool object MAY delay persisting a deferred cache item in order to take advantage of bulk-set operations supported by some storage engines.
  14. http://www.php-fig.org/psr/psr-6/ Denis Brumann // @dbrumann // Caching in Symfony DEFERRED

    SAVE A Pool MUST ensure that any deferred cache items are eventually persisted and data is not lost, and MAY persist them before a Calling Library requests that they be persisted.
  15. Denis Brumann // @dbrumann // Caching in Symfony KEYS /

    REQUIREMENTS • UTF-8 encoded string of at least one character • Uniquely identifies a cached item • must support ‘A-Z’, ‘a-z’, ‘0-9’, ‘_’ and ‘.’ • reserved characters: ‘{}()/\@:’ • Length up to 64 characters (supporting more is allowed)
  16. Denis Brumann // @dbrumann // Caching in Symfony VALUES /

    ALLOWED TYPES • Skalare Datentypen: Strings, Integer, Floats, Booleans • NULL • Arrays • Objects
 implement Serializable interface
 or __sleep() & __wakeup()
  17. Denis Brumann // @dbrumann // Caching in Symfony DATA All

    data passed into the Implementing Library MUST be returned exactly as passed. That includes the variable type. That is, it is an error to return (string) 5 if (int) 5 was the value saved. […] If it is not possible to return the exact saved value for any reason, implementing libraries MUST respond with a cache miss rather than corrupted data. http://www.php-fig.org/psr/psr-6/
  18. Denis Brumann // @dbrumann // Caching in Symfony <?php namespace

    Psr\Cache; interface CacheItemPoolInterface { public function getItem($key); public function getItems(array $keys = array()); public function hasItem($key); // … }
  19. Denis Brumann // @dbrumann // Caching in Symfony <?php namespace

    Psr\Cache; interface CacheItemPoolInterface { // … public function clear(); public function deleteItem($key); public function deleteItems(array $keys); // … }
  20. Denis Brumann // @dbrumann // Caching in Symfony <?php namespace

    Psr\Cache; interface CacheItemPoolInterface { // … public function save(CacheItemInterface $item); public function saveDeferred(CacheItemInterface $item); public function commit(); }
  21. Denis Brumann // @dbrumann // Caching in Symfony <?php namespace

    Psr\Cache; interface CacheItemInterface { public function getKey(); public function get(); public function isHit(); // … }
  22. Denis Brumann // @dbrumann // Caching in Symfony <?php namespace

    Psr\Cache; interface CacheItemInterface { // … public function set($value); // … }
  23. Denis Brumann // @dbrumann // Caching in Symfony <?php namespace

    Psr\Cache; interface CacheItemInterface { // … public function expiresAt($expiration); public function expiresAfter($time); }
  24. Denis Brumann // @dbrumann // Caching in Symfony public function

    someHeavyMethod() { $key = 'some_unique_key’; /** @var CacheItemInterface $cacheItem */ $cacheItem = $this->cachePool->getItem($key); if ($cacheItem->isHit()) { return $cacheItem->get(); } $data = // Result from heavy operation $cacheItem->set($data); $cacheItem->expiresAfter(3600); $this->cachePool->save($cacheItem); return $cacheItem->get(); }
  25. Denis Brumann // @dbrumann // Caching in Symfony public function

    someHeavyMethod(array $keys) { $cacheItems = $this->cachePool->getItems($keys); $result = []; foreach ($cacheItems as $cacheItem) { if (!$cacheItem->isHit()) { $data = $this->something($cacheItem->getKey()); $cacheItem->set($data); $cacheItem->expiresAt(new DateTime('+1 hour')); $this->cachePool->saveDeferred($cacheItem); } $result[] = $cacheItem->get(); } $this->cachePool->commit(); return $result; }
  26. This simpler approach aims to build a standardized streamlined interface

    for common cases. It is independent of PSR-6 but has been designed to make compatibility with PSR-6 as straightforward as possible. http://www.php-fig.org/psr/psr-16/ Denis Brumann // @dbrumann // Caching in Symfony SIMPLE CACHE
  27. Denis Brumann // @dbrumann // Caching in Symfony /** *

    @var CacheInterface */ private $cache; public function someHeavyMethod() { $key = ‘some.unique.key'; if ($this->cache->has($key)) { return $this->cache->get($key); } $data = $this->something(); $this->cache->set($key, $data, 3600); return $data; }
  28. Denis Brumann // @dbrumann // Caching in Symfony framework: cache:

    app: cache.adapter.filesystem system: cache.adapter.system directory: /path/to/project/var/cache/dev/pools default_redis_provider: 'redis://localhost' default_memcached_provider: 'memcached://localhost' #pools: # add your custom cache pools here APP/CONFIG/CONFIG.YML
  29. Denis Brumann // @dbrumann // Caching in Symfony framework: #

    … cache: pools: app.cache.api: adapter: cache.app APP/CONFIG/CONFIG.YML
  30. Denis Brumann // @dbrumann // Caching in Symfony services: cache.adapter.my_pdo:

    class: Symfony\Component\Cache\Adapter\PdoAdapter arguments: - '@doctrine.dbal.default_connection' APP/CONFIG/SERVICES.YML
  31. Denis Brumann // @dbrumann // Caching in Symfony framework: #

    … cache: pools: app.cache.api: adapter: cache.adapter.my_pdo APP/CONFIG/CONFIG.YML
  32. Denis Brumann // @dbrumann // Caching in Symfony services: app.some_service.cached:

    class: App\Service\SomeCachedService decorates: '@app.some_service.concrete' arguments: - '@app.some_service.cached.inner' - ‘@app.cache.api’ public: false app.some_service: '@app.some_service.cached' https://symfony.com/doc/current/service_container/service_decoration.html
  33. Denis Brumann // @dbrumann // Caching in Symfony App\Meetup\CachedClient: decorates:

    App\Meetup\Client arguments: $client: '@App\Meetup\CachedClient.inner' $clientPool: '@app.cache.api' App\Meetup\ClientInterface: '@App\Meetup\CachedClient' https://symfony.com/doc/current/service_container/service_decoration.html
  34. Denis Brumann // @dbrumann // Caching in Symfony $key =

    'unique.identifier'; $cacheItem = $this->cachePool->getItem($key); $cacheItem->set($data); $cacheItem->expiresAfter(3600); $cacheItem->tag(['tag', 'other_tag', 'something1']); $this->cachePool->save($cacheItem); return $cacheItem->get();
  35. Denis Brumann // @dbrumann // Caching in Symfony User Browser

    Web Server Request: / Request: / Response Response
  36. Denis Brumann // @dbrumann // Caching in Symfony namespace AppBundle\Controller;

    use Sensio\Bundle\FrameworkExtraBundle\Configuration\Cache; use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route; use Symfony\Bundle\FrameworkBundle\Controller\Controller; class HomepageController extends Controller { /** * @Route("/", name="home") * @Cache(maxage=600) */ public function indexAction() { return $this->render('home.html.twig'); } }
  37. Denis Brumann // @dbrumann // Caching in Symfony User Browser

    Web Server Request: / Request: / Response* Response*
  38. Denis Brumann // @dbrumann // Caching in Symfony namespace AppBundle\Controller;

    use Sensio\Bundle\FrameworkExtraBundle\Configuration\Cache; use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route; use Symfony\Bundle\FrameworkBundle\Controller\Controller; class HomepageController extends Controller { /** * @Route("/", name="home") * @Cache(expires=“+1 hours") */ public function indexAction() { return $this->render('home.html.twig'); } }
  39. Denis Brumann // @dbrumann // Caching in Symfony namespace AppBundle\Controller;

    use Sensio\Bundle\FrameworkExtraBundle\Configuration\Cache; use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route; use Symfony\Bundle\FrameworkBundle\Controller\Controller; class HomepageController extends Controller { /** * @Route("/", name="home") * @Cache(maxage=600, smaxage=600) */ public function indexAction() { return $this->render('home.html.twig'); } }
  40. Denis Brumann // @dbrumann // Caching in Symfony /** *

    @Route("/{urlname}/event/{eventId}", name="event") */ public function eventAction(
 Request $request, string $urlname, string $eventId
 ): Response { // Get event data $response = $this->render( 'event/show.html.twig', ['event' => $event] ); $response->setEtag(md5($response->getContent())); $response->setPublic(); $response->isNotModified($request); return $response; }
  41. Denis Brumann // @dbrumann // Caching in Symfony try {

    $event = // Get Event-data from API-gateway } catch (GatewayException $exception) { throw new ServiceUnavailableHttpException(); } $response = new Response(); $response->setPublic(); $response->setLastModified($event->getUpdated()); if ($response->isNotModified($request)) { return $response; } return $this->render( 'event/show.html.twig', ['event' => $event], $response );
  42. Denis Brumann // @dbrumann // Caching in Symfony User Browser

    Web Server Cache Request: / Response Response will be an empty
 304 Not Modified
 when cache was hit
  43. Denis Brumann // @dbrumann // Caching in Symfony {% block

    content %} <h1>Upcoming Meetups</h1> {{ render(controller('AppBundle:Event:list')) }} {% endblock %} {% block sidebar %} <h2>Featured User Groups</h2> {{ render_esi(controller('AppBundle:Group:list')) }} {% endblock %}