Skip to content

Commit

Permalink
Fix race condition on flushByTag function
Browse files Browse the repository at this point in the history
  • Loading branch information
weakbit committed Mar 6, 2025
1 parent bc718cf commit c17ea6d
Show file tree
Hide file tree
Showing 7 changed files with 33 additions and 82 deletions.
78 changes: 18 additions & 60 deletions Classes/Cache/Backend/SsiIncludeCacheBackend.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,23 +6,15 @@

use AUS\SsiInclude\Utility\FilenameUtility;
use InvalidArgumentException;
use TYPO3\CMS\Core\Cache\Backend\AbstractBackend;
use TYPO3\CMS\Core\Cache\Backend\BackendInterface;
use TYPO3\CMS\Core\Cache\Backend\TaggableBackendInterface;
use TYPO3\CMS\Core\Cache\CacheManager;
use TYPO3\CMS\Core\Cache\Backend\Typo3DatabaseBackend;
use TYPO3\CMS\Core\Cache\Exception\NoSuchCacheException;
use TYPO3\CMS\Core\Cache\Frontend\FrontendInterface;
use TYPO3\CMS\Core\Core\Environment;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use Webimpress\SafeWriter\Exception\ExceptionInterface;
use Webimpress\SafeWriter\FileWriter;

class SsiIncludeCacheBackend extends AbstractBackend implements TaggableBackendInterface
class SsiIncludeCacheBackend extends Typo3DatabaseBackend
{
private readonly TaggableBackendInterface $concrete;

private string $concreteCache = 'aus_ssi_include_concrete_cache';

private readonly FilenameUtility $filenameUtility;

/**
Expand All @@ -35,30 +27,26 @@ class SsiIncludeCacheBackend extends AbstractBackend implements TaggableBackendI
/**
* @inheritdoc
* @param array<string, mixed> $options
* @throws NoSuchCacheException
*/
public function __construct($context, array $options = [])
{
parent::__construct($context, $options);

$this->filenameUtility = GeneralUtility::makeInstance(FilenameUtility::class);
}

$cacheManager = GeneralUtility::makeInstance(CacheManager::class);
assert($cacheManager instanceof CacheManager);
$concrete = $cacheManager->getCache($this->concreteCache)->getBackend();
assert($concrete instanceof BackendInterface);
assert($concrete instanceof TaggableBackendInterface);
$this->concrete = $concrete;
public function getSsiIncludeDir(): string
{
return $this->ssiIncludeDir;
}

/**
* The cache identifier of the concrete cache which is used to save
* the data. If storeData is false, it also creates an empty cache entry
* to include with caching framework and have the identifier tied to the cache tags and lifetime
* @return list<string>
*/
public function setConcreteCache(string $concreteCache): void
private function getSsiIncludeDirFiles(): array
{
$this->concreteCache = $concreteCache;
$publicIncludeDir = Environment::getPublicPath() . $this->ssiIncludeDir;
return glob($publicIncludeDir . '*.html') ?: [];
}

/**
Expand All @@ -78,19 +66,6 @@ public function setStoreData(bool $storeData): void
$this->storeData = $storeData;
}

public function getSsiIncludeDir(): string
{
return $this->ssiIncludeDir;
}

/**
* @inheritdoc
*/
public function setCache(FrontendInterface $cache): void
{
$this->cache = $cache;
}

/**
* @inheritdoc
* @param array<string> $tags
Expand All @@ -102,23 +77,15 @@ public function set($entryIdentifier, $data, array $tags = [], $lifetime = null)
throw new InvalidArgumentException('Data must be a string', 1616420133);
}

parent::set($entryIdentifier, $this->storeData ? $data : '', $tags, $lifetime);

$absolutePath = $this->filenameUtility->getAbsoluteFilename($entryIdentifier);

GeneralUtility::mkdir_deep(dirname($absolutePath));
GeneralUtility::fixPermissions(dirname($absolutePath));

FileWriter::writeFile($absolutePath, $data);
GeneralUtility::fixPermissions($absolutePath);

$this->concrete->set($entryIdentifier, $this->storeData ? $data : '', $tags, $lifetime);
}

/**
* @inheritdoc
*/
public function get($entryIdentifier): false|string
{
return $this->concrete->get($entryIdentifier);
}

/**
Expand All @@ -127,7 +94,7 @@ public function get($entryIdentifier): false|string
*/
public function has($entryIdentifier): bool
{
$data = $this->concrete->has($entryIdentifier);
$data = parent::has($entryIdentifier);
if (!$data) {
return false;
}
Expand All @@ -147,7 +114,7 @@ public function remove($entryIdentifier): bool
unlink($absoluteFile);
}

return $this->concrete->remove($entryIdentifier);
return parent::remove($entryIdentifier);
}

/**
Expand All @@ -159,7 +126,7 @@ public function flush(): void
unlink($file);
}

$this->concrete->flush();
parent::flush();
}

/**
Expand All @@ -169,7 +136,7 @@ public function flush(): void
public function collectGarbage(): void
{
// remove outdated things
$this->concrete->collectGarbage();
parent::collectGarbage();

// get all files, and the file that has no entry remove them
$files = $this->getSsiIncludeDirFiles();
Expand Down Expand Up @@ -200,7 +167,7 @@ public function flushByTag($tag): void
*/
public function flushByTags(array $tags): void
{
$this->concrete->flushByTags($tags);
parent::flushByTags($tags);
foreach ($tags as $tag) {
$this->flushByTag($tag);
}
Expand All @@ -213,15 +180,6 @@ public function flushByTags(array $tags): void
*/
public function findIdentifiersByTag($tag): array
{
return $this->concrete->findIdentifiersByTag($tag);
}

/**
* @return list<string>
*/
private function getSsiIncludeDirFiles(): array
{
$publicIncludeDir = Environment::getPublicPath() . $this->ssiIncludeDir;
return glob($publicIncludeDir . '*.html') ?: [];
return parent::findIdentifiersByTag($tag);
}
}
4 changes: 0 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -101,10 +101,6 @@ This defaults to on, but if you want to spare some space you can disable it.

Public directory where the SSI files are stored.

#### concreteCache

An configured taggable cache to use as the cache backends backbone

### Using the LazyDataProcessor to increase the Performance even more.

#### before:
Expand Down
10 changes: 3 additions & 7 deletions Tests/Classes/SsiIncludeCacheBackendTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -64,10 +64,6 @@ private function initializeCacheFramework(): void
$cacheManager = GeneralUtility::makeInstance(CacheManager::class);
assert($cacheManager instanceof CacheManager);
$cacheManager->setCacheConfigurations([
'aus_ssi_include_concrete_cache' => [
'frontend' => VariableFrontend::class,
'backend' => Typo3DatabaseBackend::class,
],
'aus_ssi_include_cache' => [
'frontend' => SsiIncludeCacheFrontend::class,
'backend' => SsiIncludeCacheBackend::class,
Expand All @@ -84,11 +80,11 @@ private function initializeCacheFramework(): void
*/
public function cacheTableExists(): void
{
$connection = $this->getConnectionPool()->getConnectionForTable('cache_aus_ssi_include_concrete_cache');
$query = $connection->executeQuery("SELECT name FROM sqlite_master WHERE type='table' AND name='cache_aus_ssi_include_concrete_cache'");
$connection = $this->getConnectionPool()->getConnectionForTable('cache_aus_ssi_include_cache');
$query = $connection->executeQuery("SELECT name FROM sqlite_master WHERE type='table' AND name='cache_aus_ssi_include_cache'");
$result = $query->fetchAssociative();

self::assertNotEmpty($result, 'The cache_ssi_cache table was not created.');
self::assertNotEmpty($result, 'The cache_aus_ssi_include_cache table was not created.');
}

/**
Expand Down
2 changes: 2 additions & 0 deletions Tests/Classes/SsiIncludeCacheFrontendTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@

class SsiIncludeCacheFrontendTest extends UnitTestCase
{
protected bool $resetSingletonInstances = true;

/**
* @test
*/
Expand Down
4 changes: 2 additions & 2 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@
],
"require": {
"php": "~8.1.0 || ~8.2.0 || ~8.3.0",
"typo3/cms-fluid": "^10.4.0 || ^11.5.0 || ^12.4.0",
"typo3/cms-frontend": "^10.4.0 || ^11.5.0 || ^12.4.0",
"typo3/cms-fluid": "^11.5.0 || ^12.4.0",
"typo3/cms-frontend": "^11.5.0 || ^12.4.0",
"webimpress/safe-writer": "^2.2.0"
},
"require-dev": {
Expand Down
9 changes: 0 additions & 9 deletions ext_localconf.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,15 +13,6 @@

$GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_tcemain.php']['clearCachePostProc'][] = ClearCache::class . '->clearCache';

// we might want to have a default concrete cache if not already defined in another way
if (!isset($GLOBALS['TYPO3_CONF_VARS']['SYS']['caching']['cacheConfigurations']['aus_ssi_include_cache']['options']['concreteCache'])) {
$GLOBALS['TYPO3_CONF_VARS']['SYS']['caching']['cacheConfigurations']['aus_ssi_include_concrete_cache'] = [
'frontend' => VariableFrontend::class,
'backend' => Typo3DatabaseBackend::class,
];
}

// TODO nochmal testen wie die ergebnisse sind jenachdem was ich im projekt hinterlege
// define the main cache with the possibility to have a partial configuration already
$GLOBALS['TYPO3_CONF_VARS']['SYS']['caching']['cacheConfigurations']['aus_ssi_include_cache'] = array_merge([
'frontend' => SsiIncludeCacheFrontend::class,
Expand Down
8 changes: 8 additions & 0 deletions runTest.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
#!/bin/sh

docker run -u1000 -v ./:/app -w /app ghcr.io/pluswerk/php-dev:nginx-8.1-alpine sh -c 'git config --global --add safe.directory /app && composer require typo3/testing-framework typo3/minimal="^11" -q -n -W --dev && composer test'
docker run -u1000 -v ./:/app -w /app ghcr.io/pluswerk/php-dev:nginx-8.1-alpine sh -c 'git config --global --add safe.directory /app && composer require typo3/testing-framework typo3/minimal="^12" -q -n -W --dev && composer test'
docker run -u1000 -v ./:/app -w /app ghcr.io/pluswerk/php-dev:nginx-8.2-alpine sh -c 'git config --global --add safe.directory /app && composer require typo3/testing-framework typo3/minimal="^11" -q -n -W --dev && composer test'
docker run -u1000 -v ./:/app -w /app ghcr.io/pluswerk/php-dev:nginx-8.2-alpine sh -c 'git config --global --add safe.directory /app && composer require typo3/testing-framework typo3/minimal="^12" -q -n -W --dev && composer test'
docker run -u1000 -v ./:/app -w /app ghcr.io/pluswerk/php-dev:nginx-8.3-alpine sh -c 'git config --global --add safe.directory /app && composer require typo3/testing-framework typo3/minimal="^11" -q -n -W --dev && composer test'
docker run -u1000 -v ./:/app -w /app ghcr.io/pluswerk/php-dev:nginx-8.3-alpine sh -c 'git config --global --add safe.directory /app && composer require typo3/testing-framework typo3/minimal="^12" -q -n -W --dev && composer test'

0 comments on commit c17ea6d

Please sign in to comment.