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.

6a1345d8e6dd15b2c78eff0c331963b1?s=128

Denis Brumann

October 27, 2017
Tweet

Transcript

  1. CACHING IN SYMFONY EIN ÜBERBLICK Denis Brumann // @dbrumann //

    Caching in Symfony
  2. SYMFONY CACHE Denis Brumann // @dbrumann // Caching in Symfony

  3. 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
  4. 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
  5. 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
  6. 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
  7. 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
  8. Denis Brumann // @dbrumann // Caching in Symfony ARRAY ADAPTER

    • Intended for testing purposes • Items are stored in memory and not persisted outside the running process
  9. 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
  10. Denis Brumann // @dbrumann // Caching in Symfony PSR-16 INTEROPERABILITY

    • From PSR-16 to PSR-6:
 SimpleCacheAdapter (wraps PSR-16 CacheInterface)
  11. PSR-6 Denis Brumann // @dbrumann // Caching in Symfony

  12. 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
  13. Denis Brumann // @dbrumann // Caching in Symfony • Compatibility

    with all existing caching implementations • Advanced caching features such as namespaces and tags NON-GOALS
  14. TERMINOLOGY Denis Brumann // @dbrumann // Caching in Symfony

  15. 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
  16. 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.
  17. 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.
  18. 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.
  19. 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)
  20. Denis Brumann // @dbrumann // Caching in Symfony VALUES /

    ALLOWED TYPES • Skalare Datentypen: Strings, Integer, Floats, Booleans • NULL • Arrays • Objects
 implement Serializable interface
 or __sleep() & __wakeup()
  21. 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/
  22. INTERFACES Denis Brumann // @dbrumann // Caching in Symfony

  23. 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); // … }
  24. Denis Brumann // @dbrumann // Caching in Symfony <?php namespace

    Psr\Cache; interface CacheItemPoolInterface { // … public function clear(); public function deleteItem($key); public function deleteItems(array $keys); // … }
  25. 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(); }
  26. Denis Brumann // @dbrumann // Caching in Symfony <?php namespace

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

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

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

  30. 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(); }
  31. 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; }
  32. PSR-16 Denis Brumann // @dbrumann // Caching in Symfony

  33. 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
  34. 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; }
  35. Denis Brumann // @dbrumann // Caching in Symfony if ($this->cache->has($key))

    { return $this->cache->get($key); } ⚠
  36. USAGE IN SYMFONY Denis Brumann // @dbrumann // Caching in

    Symfony
  37. 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
  38. Denis Brumann // @dbrumann // Caching in Symfony

  39. Denis Brumann // @dbrumann // Caching in Symfony framework: #

    … cache: pools: app.cache.api: adapter: cache.app APP/CONFIG/CONFIG.YML
  40. 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
  41. Denis Brumann // @dbrumann // Caching in Symfony framework: #

    … cache: pools: app.cache.api: adapter: cache.adapter.my_pdo APP/CONFIG/CONFIG.YML
  42. 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
  43. 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
  44. EXAMPLE Denis Brumann // @dbrumann // Caching in Symfony

  45. Denis Brumann // @dbrumann // Caching in Symfony

  46. Denis Brumann // @dbrumann // Caching in Symfony

  47. Denis Brumann // @dbrumann // Caching in Symfony

  48. Denis Brumann // @dbrumann // Caching in Symfony

  49. Denis Brumann // @dbrumann // Caching in Symfony

  50. Denis Brumann // @dbrumann // Caching in Symfony

  51. TAG AWARE ADAPTER Denis Brumann // @dbrumann // Caching in

    Symfony
  52. 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();
  53. Denis Brumann // @dbrumann // Caching in Symfony $cache->invalidateTags(['tag', 'another_tag']);

  54. NOT PART OF PSR-6 Denis Brumann // @dbrumann // Caching

    in Symfony ⚠
  55. HTTP CACHE Denis Brumann // @dbrumann // Caching in Symfony

  56. Denis Brumann // @dbrumann // Caching in Symfony User Browser

    Web Server Request: / Request: / Response Response
  57. Denis Brumann // @dbrumann // Caching in Symfony

  58. 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'); } }
  59. Denis Brumann // @dbrumann // Caching in Symfony User Browser

    Web Server Request: / Request: / Response* Response*
  60. Denis Brumann // @dbrumann // Caching in Symfony

  61. Denis Brumann // @dbrumann // Caching in Symfony User Browser

    Web Server Request: / Response*
  62. 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'); } }
  63. Denis Brumann // @dbrumann // Caching in Symfony

  64. SERVER-SIDE CACHING Denis Brumann // @dbrumann // Caching in Symfony

  65. Denis Brumann // @dbrumann // Caching in Symfony User Browser

    Web Server Cache
  66. Denis Brumann // @dbrumann // Caching in Symfony VARNISH

  67. Denis Brumann // @dbrumann // Caching in Symfony

  68. Denis Brumann // @dbrumann // Caching in Symfony

  69. 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'); } }
  70. Denis Brumann // @dbrumann // Caching in Symfony

  71. Denis Brumann // @dbrumann // Caching in Symfony User Browser

    Web Server Cache Request: / Response
  72. Denis Brumann // @dbrumann // Caching in Symfony

  73. Denis Brumann // @dbrumann // Caching in Symfony User Browser

    Web Server Cache Request: / Response
  74. VALIDATION CACHING Denis Brumann // @dbrumann // Caching in Symfony

  75. 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; }
  76. Denis Brumann // @dbrumann // Caching in Symfony

  77. 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 );
  78. Denis Brumann // @dbrumann // Caching in Symfony

  79. 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
  80. ESI Denis Brumann // @dbrumann // Caching in Symfony

  81. Denis Brumann // @dbrumann // Caching in Symfony

  82. Denis Brumann // @dbrumann // Caching in Symfony

  83. 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 %}
  84. Denis Brumann // @dbrumann // Caching in Symfony Expires: 60

    Expires: 3600 Expires: 86400
  85. THANK YOU Denis Brumann // @dbrumann // Caching in Symfony