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

Grâce aux tags Varnish, j'ai switché ma prod su...

Grâce aux tags Varnish, j'ai switché ma prod sur Raspberry Pi

Le moyen le plus rapide d'obtenir une réponse d'un Backend est de ne pas l'appeler ;-) Une solution fournie par les "reverse-proxy" me direz-vous, mais pas si simple d'invalider le cache... Ce talk aborde une fonctionnalité méconnue de Varnish: les tags. Nous verrons comment en tirer partie via les "event listeners" d'une application Symfony standard. Au menu, un cluster de Rasberry Pi, une API, et des données toujours fraîches sous la milliseconde

Jérémy Derussé

May 18, 2017
Tweet

More Decks by Jérémy Derussé

Other Decks in Programming

Transcript

  1. Who am I? Jérémy DERUSSÉ Web Technical Leader author of

    Symfony Lock Component 3.3 @jderusse
  2. Integration in Symfony use Sensio\Bundle\FrameworkExtraBundle\Configuration\Cache; /** * @Cache(smaxage="3600") */ public

    function indexAction() { // ... } Advanced usage with FriendsOfSymfony/FOSHttpCache
  3. Issue #1 Unsharable resources private resources (ie. invoice, shopping cart,

    ...) per role representations Solution vary cache on user/role/other Repousser les limites : HTTP cache et utilisateurs connectés
  4. Issue #2 Cache invalidation “ There are only two hard

    things in Computer Science: cache invalidation and naming things. Phil Karlton
  5. Cache models Validation model etag last-modified drawbacks application is booted

    hard to implement Expiration model expires cache-control drawback no control on invalidation
  6. cache model In a real world Backend can't validate every

    cache HIT Life time is not predictable BUT Varnish can be requested to partially invalidate responses Backend knows when resources change Responses are build on top of resources
  7. curl -I "http://varnish.myapp.com/c" curl -I "http://varnish.myapp.com/b" curl -I "http://varnish.myapp.com/a" HTTP/1.1

    200 OK Cache-Control: public, s-maxage=3600 X-Cache-Tags: Foo,Bar HTTP/1.1 200 OK Cache-Control: public, s-maxage=3600 X-Cache-Tags: Foo Varnish tags curl \ -X "BAN" \ -H "X-Cache-Tags: Foo" \ "http://varnish.myapp.com" HTTP/1.1 200 OK Cache-Control: public, s-maxage=3600 X-Cache-Tags: Bar,Qux
  8. Varnish tags curl "http://varnish.myapp.com/posts/42" HTTP/1.1 200 OK Cache-Control: public, s-maxage=86400

    X-Cache-Tags: Post:42,Author:12,Comment:314,Comment:1337 { "id": 42, "title": "My blog post.", "body": "Lorem Ipsum.", "author": { "id": 12, "username": "jderusse" }, "comments": [ { "id": 314, "message": "Wow such post" }, { "id": 1337, "message": "much performance" } ] }
  9. Automate Tagging Tagging Response - 1. Collect displayed resources namespace

    App\EventListener; use JMS\Serializer\EventDispatcher\Events; use JMS\Serializer\EventDispatcher\EventSubscriberInterface; use JMS\Serializer\EventDispatcher\ObjectEvent; class SerializationTagListener implements EventSubscriberInterface { public function onPostSerialize(ObjectEvent $event) { $resource = $event->getObject(); // TODO } public static function getSubscribedEvents() { return [ [ 'event' => Events::POST_SERIALIZE, 'format' => 'json', 'method' => 'onPostSerialize', ], ]; } }
  10. Automate Tagging Tagging Response - 2. Generate resource identifier namespace

    App\EventListener; use App\Tag\TagExtractorInterface; use JMS\Serializer\EventDispatcher\EventSubscriberInterface; use JMS\Serializer\EventDispatcher\ObjectEvent; class SerializationTagListener implements EventSubscriberInterface { private $tagExtractor; public function __construct(TagExtractorInterface $tagExtractor) { $this->tagExtractor = $tagExtractor; } public function onPostSerialize(ObjectEvent $event): void { //... $tags = $this->tagExtractor->extract($event->getObject()); } //... }
  11. Automate Tagging Tagging Response - 3. Tag response namespace App\EventListener;

    use App\Tag\TagExtractorInterface; use FOS\HttpCache\Handler\TagHandler; use JMS\Serializer\EventDispatcher\EventSubscriberInterface; use JMS\Serializer\EventDispatcher\ObjectEvent; class SerializationTagListener implements EventSubscriberInterface { private $tagExtractor; private $tagHandler; public function __construct(TagExtractorInterface $tagExtractor, TagHandler $tagHandler) { $this->tagExtractor = $tagExtractor; $this->tagHandler = $tagHandler; } public function onPostSerialize(ObjectEvent $event): void { $tags = $this->tagExtractor->extract($event->getObject()); $this->tagHandler->addTags($tags); } //... }
  12. Automate Tagging Tagging Response 1. Collect displayed resources 2. Generate

    resource identifier 3. Tag response Invalidate cache 1. Listen changes 2. Generate resource identifier 3. Call varnish
  13. Automate Tagging Invalidate Cache - 1. Listen changes namespace App\EventListener;

    use Doctrine\Common\EventSubscriber; use Doctrine\ORM\Event\OnFlushEventArgs; use Doctrine\ORM\Events; class DoctrineInvalidationTagListener implements EventSubscriber { public function getSubscribedEvents() { return [Events::onFlush]; } public function onFlush(OnFlushEventArgs $eventArgs) { $uow = $eventArgs->getEntityManager()->getUnitOfWork(); foreach ($uow->getScheduledEntityUpdates() as $resource) { // TODO } foreach ($uow->getScheduledEntityDeletions() as $resource) { // TODO } } }
  14. Automate Tagging Invalidate Cache - 2. Generate resource identifier namespace

    App\EventListener; use App\Tag\TagExtractorInterface; use Doctrine\Common\EventSubscriber; class DoctrineInvalidationTagListener implements EventSubscriber { private $tagExtractor; public function __construct(TagExtractorInterface $tagExtractor) { $this->tagExtractor = $tagExtractor; } public function onFlush(OnFlushEventArgs $eventArgs) { $uow = $eventArgs->getEntityManager()->getUnitOfWork(); $tags = []; foreach ($uow->getScheduledEntityUpdates() as $resource) { $tags = array_merge($tags, $this->tagExtractor->extract($resource)); } foreach ($uow->getScheduledEntityDeletions() as $resource) { $tags = array_merge($tags, $this->tagExtractor->extract($resource)); } // TODO } }
  15. Automate Tagging Invalidate Cache - 3. Call varnish namespace App\EventListener;

    use App\Tag\TagExtractorInterface; use Doctrine\Common\EventSubscriber; use FOS\HttpCache\Handler\TagHandler; class DoctrineInvalidationTagListener implements EventSubscriber { private $tagExtractor; public function __construct(TagExtractorInterface $tagExtractor, TagHandler $tagHandler) { $this->tagExtractor = $tagExtractor; $this->tagHandler = $tagHandler; } public function onFlush(OnFlushEventArgs $eventArgs) { // ... $this->tagHandler->invalidateTags($tags); } }
  16. Automate Tagging Tagging Response 1. Collect displayed resources 2. Generate

    resource identifier 3. Tag response Invalidate cache 1. Listen changes 2. Generate resource identifier 3. Call varnish Enjoy
  17. Silver bullet? Works well when HIT >> MISS Read >>

    Write Application knows resources used to build response Drawback Operations are not Atomic B ackend handles writes Backend knows infrastructure It slows writes
  18. ab -n 8000 -c 26 192.168.1.17:81/comments/1 This is ApacheBench, Version

    2.3 <$Revision: 1757674 $> Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net Licensed to The Apache Software Foundation, http://www.apache.org/ Server Software: nginx/1.10.3 Server Hostname: 192.168.1.16 Server Port: 80 Document Path: /comments/1 Document Length: 576 bytes Concurrency Level: 26 Time taken for tests: 26.912 seconds Complete requests: 200 Failed requests: 0 Total transferred: 193400 bytes HTML transferred: 115200 bytes Requests per second: 7.43 [#/sec] (mean) Time per request: 3498.553 [ms] (mean) Time per request: 134.560 [ms] (mean, across all concurrent Transfer rate: 7.02 [Kbytes/sec] received Connection Times (ms) min mean[+/-sd] median max Connect: 1 2 1.4 1 6 Processing: 546 3342 1156.7 3020 7204 Waiting: 546 3342 1156.7 3020 7204 Total: 550 3344 1156.5 3021 7205 in numbers ab -n 8000 -c 26 192.168.1.17/comments/1 This is ApacheBench, Version 2.3 <$Revision: 1757674 $> Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net Licensed to The Apache Software Foundation, http://www.apache.org/ Server Software: nginx/1.10.3 Server Hostname: 192.168.1.17 Server Port: 80 Document Path: /comments/1 Document Length: 576 bytes Concurrency Level: 26 Time taken for tests: 2.340 seconds Complete requests: 8000 Failed requests: 0 Total transferred: 8948504 bytes HTML transferred: 4608000 bytes Requests per second: 3418.52 [#/sec] (mean) Time per request: 7.606 [ms] (mean) Time per request: 0.293 [ms] (mean, across all concurrent Transfer rate: 3734.21 [Kbytes/sec] received Connection Times (ms) min mean[+/-sd] median max Connect: 0 2 0.8 2 7 Processing: 2 5 3.3 5 55 Waiting: 2 5 3.3 4 55 Total: 2 8 3.3 7 56 app varnish