Skip to content

Commit f90354a

Browse files
committed
feat: use db cache for caching across autoscaling instances
1 parent df8fe4a commit f90354a

File tree

3 files changed

+157
-12
lines changed

3 files changed

+157
-12
lines changed

_config/assets.yml

Lines changed: 4 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -6,22 +6,15 @@ After:
66
- '#assetsflysystem'
77
---
88
SilverStripe\Core\Injector\Injector:
9-
League\Flysystem\Adapter\Local.cache:
10-
class: League\Flysystem\Adapter\Local
11-
constructor:
12-
root: '`TEMP_PATH`'
139
FullscreenInteractive\SilverStripe\AzureStorage\Adapter\PublicAdapter:
1410
constructor:
1511
connectionUrl: '`AZURE_CONNECTION_URL`'
1612
containerName: '`AZURE_CONTAINER_NAME`'
1713
assetDomain: '`AZURE_PUBLIC_BLOB_DOMAIN`'
18-
League\Flysystem\Cached\Storage\Memory.public:
19-
class: League\Flysystem\Cached\Storage\Memory
2014
League\Flysystem\Cached\Storage\Adapter.public:
21-
class: League\Flysystem\Cached\Storage\Adapter
15+
class: FullscreenInteractive\SilverStripe\AzureStorage\Adapter\DBCache
2216
constructor:
23-
adapter: '%$League\Flysystem\Adapter\Local.cache'
24-
file: 'azuremetadata/public'
17+
key: 'azuremetadata/public'
2518
expire: 259200
2619
SilverStripe\Assets\Flysystem\PublicAdapter:
2720
class: FullscreenInteractive\SilverStripe\AzureStorage\Adapter\PublicCachedAdapter
@@ -33,10 +26,9 @@ SilverStripe\Core\Injector\Injector:
3326
connectionUrl: '`AZURE_CONNECTION_URL`'
3427
containerName: '`AZURE_PROTECTED_CONTAINER_NAME`'
3528
League\Flysystem\Cached\Storage\Adapter.protected:
36-
class: League\Flysystem\Cached\Storage\Adapter
29+
class: FullscreenInteractive\SilverStripe\AzureStorage\Adapter\DBCache
3730
constructor:
38-
adapter: '%$League\Flysystem\Adapter\Local.cache'
39-
file: 'azuremetadata/protected'
31+
key: 'azuremetadata/protected'
4032
expire: 259200
4133
SilverStripe\Assets\Flysystem\ProtectedAdapter:
4234
class: FullscreenInteractive\SilverStripe\AzureStorage\Adapter\ProtectedCachedAdapter

src/Adapter/DBCache.php

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
<?php
2+
3+
namespace FullscreenInteractive\SilverStripe\AzureStorage\Adapter;
4+
5+
use Exception;
6+
use FullscreenInteractive\SilverStripe\AzureStorage\Model\BlobCache;
7+
use League\Flysystem\Cached\Storage\AbstractCache;
8+
9+
class DBCache extends AbstractCache
10+
{
11+
/**
12+
* Cache key
13+
*
14+
* @var string|null
15+
*/
16+
protected $key = null;
17+
18+
/**
19+
* @var int|null seconds until cache expiration
20+
*/
21+
protected $expire = null;
22+
23+
protected $ready = false;
24+
25+
/**
26+
* Constructor.
27+
*
28+
* @param string $key storage key
29+
* @param int|null $expire
30+
*/
31+
public function __construct(string $key = 'flysystem', int $expire = null)
32+
{
33+
$this->key = $key;
34+
$this->expire = $expire;
35+
$this->ready = BlobCache::ready();
36+
}
37+
38+
/**
39+
* @throws Exception
40+
*/
41+
public function load()
42+
{
43+
if (!$this->ready) {
44+
return;
45+
}
46+
47+
$cache = BlobCache::instance($this->key, $this->expire);
48+
$contents = $cache->Contents;
49+
50+
if ($contents) {
51+
$this->setFromStorage($contents);
52+
}
53+
}
54+
55+
/**
56+
* @throws Exception
57+
*/
58+
public function save()
59+
{
60+
if (!$this->ready) {
61+
return;
62+
}
63+
64+
$contents = $this->getForStorage();
65+
$blobCache = BlobCache::instance($this->key, $this->expire);
66+
$blobCache->Contents = $contents;
67+
$blobCache->write();
68+
}
69+
}

src/Model/BlobCache.php

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
<?php
2+
3+
namespace FullscreenInteractive\SilverStripe\AzureStorage\Model;
4+
5+
use DateTimeImmutable;
6+
use Exception;
7+
use SilverStripe\Core\ClassInfo;
8+
use SilverStripe\ORM\DataObject;
9+
use SilverStripe\Security\Security;
10+
11+
/**
12+
* @property string $Key
13+
* @property string $Contents JSON cache
14+
*/
15+
class BlobCache extends DataObject
16+
{
17+
private static $table_name = 'Azure_BlobCache';
18+
19+
/**
20+
* @var array
21+
*/
22+
protected static $instances = [];
23+
24+
private static $db = [
25+
'Key' => 'Varchar(255)',
26+
'Contents' => 'Text',
27+
];
28+
29+
private static $indexes = [
30+
'UniqueKey' => [
31+
'type' => 'unique',
32+
'columns' => ['Key'],
33+
],
34+
];
35+
36+
/**
37+
* Get cache for key
38+
*
39+
* @param string $key
40+
* @param int $maxAge Max age
41+
* @return static
42+
* @throws Exception
43+
*/
44+
public static function instance(string $key, int $maxAge): self
45+
{
46+
if (isset(static::$instances[$key])) {
47+
return static::$instances[$key];
48+
}
49+
50+
// Query from DB, respecting max age
51+
/** @var self $cache */
52+
$cache = static::get()->filter('Key', $key)->first();
53+
if (!$cache) {
54+
// Create blank record
55+
$cache = new self();
56+
$cache->Key = $key;
57+
$cache->write();
58+
} elseif ($maxAge) {
59+
// Expire contents if older than maxAge
60+
$modified = new DateTimeImmutable($cache->LastEdited);
61+
$expires = (new DateTimeImmutable())
62+
->sub(new \DateInterval("PT{$maxAge}S"));
63+
64+
// Flush expired contents
65+
if ($modified < $expires) {
66+
$cache->Contents = null;
67+
$cache->write();
68+
}
69+
}
70+
71+
// Return
72+
static::$instances[$key] = $cache;
73+
return $cache;
74+
}
75+
76+
/**
77+
* Safely disable db cache if not built yet
78+
* @return bool
79+
*/
80+
public static function ready(): bool
81+
{
82+
return Security::database_is_ready() && ClassInfo::hasTable('Azure_BlobCache');
83+
}
84+
}

0 commit comments

Comments
 (0)