Last active
July 21, 2021 23:11
-
-
Save Berdir/3fe65aa3b7ad86e16a5e to your computer and use it in GitHub Desktop.
Varnish cache tag purger
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
sub vcl_recv { | |
if (req.request == "BAN") { | |
if (!client.ip ~ internal) { | |
error 405 "Method not allowed"; | |
} | |
ban("obj.http.X-Drupal-Cache-Tags ~ " + req.http.X-Drupal-Cache-Tag-Clears); | |
error 200 "Banned" ; | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
/** | |
* @file | |
* Contains \Drupal\varnish_cache_tag_purger\TagDeletionListener. | |
*/ | |
namespace Drupal\varnish_cache_tag_purger; | |
use Drupal\Core\Cache\NullBackend; | |
use Drupal\Core\Site\Settings; | |
use GuzzleHttp\ClientInterface; | |
/** | |
* A fake cache backend that sends purge requests to varnish. | |
*/ | |
class TagDeletionListener extends NullBackend { | |
/** | |
* @var \GuzzleHttp\ClientInterface | |
*/ | |
protected $httpClient; | |
/** | |
* {@inheritdoc} | |
*/ | |
public function __construct(ClientInterface $http_client) { | |
parent::__construct('fake'); | |
$this->httpClient = $http_client; | |
} | |
/** | |
* {@inheritdoc} | |
*/ | |
public function deleteTags(array $tags) { | |
$this->purgeTags($tags); | |
} | |
/** | |
* {@inheritdoc} | |
*/ | |
public function invalidateTags(array $tags) { | |
$this->purgeTags($tags); | |
} | |
/** | |
* Purges caches with those tags from Varnish. | |
* | |
* @param array $tags | |
* Array of cache tags. | |
*/ | |
protected function purgeTags(array $tags) { | |
if ($varnish_url = Settings::get('varnish_url')) { | |
$request = $this->httpClient->createRequest('BAN', $varnish_url) | |
->addHeader('X-Drupal-Cache-Tag-Clears', $this->convertCacheTagsToClearRegex($tags)); | |
$this->httpClient->send($request); | |
} | |
} | |
/** | |
* Converts the cache tag array structure to a varnish compatible regex. | |
* | |
* @param array $tags | |
* Cache tags. | |
* | |
* @return string | |
* String in the form of "(\Dnode:1\D|\Dnodes\D)", taken from | |
* | |
* @see http://www.smashingmagazine.com/2014/04/23/cache-invalidation-strategies-with-varnish-cache/ | |
*/ | |
public function convertCacheTagsToClearRegex(array $tags) { | |
$flat_tags = array(); | |
foreach ($tags as $namespace => $values) { | |
if (is_array($values)) { | |
foreach ($values as $value) { | |
$flat_tags[] = "$namespace:$value"; | |
} | |
} | |
else { | |
$flat_tags[] = "$namespace:$values"; | |
} | |
} | |
return '(\D' . implode('\D|\D', $flat_tags) . '\D)'; | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
services: | |
# Define a fake cache bin by tagging it with cache.bin. This only implements | |
# methods to clear cache tags. | |
varnish_cache_tag_purger.tag_deletion_listener: | |
class: Drupal\varnish_cache_tag_purger\TagDeletionListener | |
arguments: ['@http_client'] | |
tags: | |
- { name: cache.bin } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
This is very, very nice and proves what we were all thinking already. Thanks Berdir!
So, as I'm working today (and next week on this), I'll translate and incorporate this into a VarnishPocPurger that'll do exactly what you are doing here. I was thinking of taking the same approach as you did by overloading the page cache, which will become a second (lightweight) module called 'purge_cachetags'. The module will be part of the purge project but allowing a separation of (API) concerns.