Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

CachedConfigCollection is written to disk on every page load #89

Merged
merged 9 commits into from
Jul 18, 2023
21 changes: 18 additions & 3 deletions src/Collections/CachedConfigCollection.php
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,11 @@ class CachedConfigCollection implements ConfigCollectionInterface
*/
protected $collection;

/**
* Stores a hash to allow comparison
*/
private ?string $collectionHash = null;

/**
* @var callable
*/
Expand Down Expand Up @@ -108,6 +113,11 @@ public function getHistory()
return $this->getCollection()->getHistory();
}

private function getHash(): string
{
return md5(serialize($this->collection));
}

/**
* Get or build collection
*
Expand All @@ -124,6 +134,7 @@ public function getCollection()
if (!$this->flush) {
$this->collection = $this->cache->get(self::CACHE_KEY);
if ($this->collection) {
$this->collectionHash = $this->getHash();
return $this->collection;
}
}
Expand All @@ -142,8 +153,9 @@ public function getCollection()
}

// Save immediately.
// Note additional deferred save will occur in _destruct()
// Note additional deferred save can occur in _destruct()
$this->cache->set(self::CACHE_KEY, $this->collection);
$this->collectionHash = $this->getHash();
return $this->collection;
}

Expand All @@ -153,11 +165,14 @@ public function getCollection()
public function __destruct()
{
// Ensure back-end cache is updated
if ($this->collection) {
$this->cache->set(self::CACHE_KEY, $this->collection);
if ($this->collection && $this->collectionHash) {
if ($this->getHash() !== $this->collectionHash) {
$this->cache->set(self::CACHE_KEY, $this->collection);
}

// Prevent double-destruct
$this->collection = null;
$this->collectionHash = null;
}
}

Expand Down
53 changes: 53 additions & 0 deletions tests/Collections/CachedConfigCollectionTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,59 @@ public function testCacheHit()
$collection->__destruct();
}

public function testCacheOnDestruct()
{
$mockCache = $this->getMockBuilder(CacheInterface::class)->getMock();

$mockCollection = $this->getMockBuilder(ConfigCollectionInterface::class)->getMock();

// Cache will be generated, saved, and NOT saved again on __destruct()
$mockCache
->expects($this->exactly(1))
->method('set')
->with(CachedConfigCollection::CACHE_KEY, $mockCollection);

$collection = new CachedConfigCollection();
$collection->setCollectionCreator(function () use ($mockCollection) {
return $mockCollection;
});
$collection->setCache($mockCache);

// Triggers cache set
$collection->getCollection();
// Call again to validate that cache set() is only called once
$collection->getCollection();

// Trigger __destruct() to validate that cache set() is not called again since there were no changes
$collection->__destruct();

$mockCache = $this->getMockBuilder(CacheInterface::class)->getMock();
lekoala marked this conversation as resolved.
Show resolved Hide resolved

$mockCollection = $this->getMockBuilder(ConfigCollectionInterface::class)->getMock();
$mockCollection
->expects($this->once())
->method('get')
->with('test', 'name', 0)
->willReturn('value');

// Cache will be generated, saved, and saved again on __destruct()
$mockCache
->expects($this->exactly(2))
->method('set')
->with(CachedConfigCollection::CACHE_KEY, $mockCollection);

$collection = new CachedConfigCollection();
$collection->setCollectionCreator(function () use ($mockCollection) {
return $mockCollection;
});
$collection->setCache($mockCache);

// Making a get call will update the callCache
$this->assertEquals('value', $collection->get('test', 'name'));

$collection->__destruct();
}

public function testCacheMiss()
{
$mockCache = $this->getMockBuilder(CacheInterface::class)->getMock();
Expand Down