Skip to content

Commit 6046242

Browse files
authored
Improve cache clearing for Symfony and Laravel (#489)
* Improve cache clearing for Symfony and Laravel * hook on cache clear for laravel
1 parent 450225e commit 6046242

File tree

14 files changed

+256
-7
lines changed

14 files changed

+256
-7
lines changed
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
<?php
2+
3+
namespace Ecotone\Laravel;
4+
5+
use Illuminate\Console\Command;
6+
use Illuminate\Support\Facades\App;
7+
8+
/**
9+
* licence Apache-2.0
10+
*/
11+
class EcotoneCacheClear
12+
{
13+
public static function clearEcotoneCacheDirectories(string $storagePath): void
14+
{
15+
$laravelCacheDirectory = $storagePath . DIRECTORY_SEPARATOR . 'ecotone';
16+
if (is_dir($laravelCacheDirectory)) {
17+
self::clearDirectory($laravelCacheDirectory);
18+
}
19+
20+
// Clear EcotoneLite test cache directory
21+
$liteCacheDirectory = sys_get_temp_dir() . DIRECTORY_SEPARATOR . 'ecotone';
22+
if (is_dir($liteCacheDirectory)) {
23+
self::clearDirectory($liteCacheDirectory);
24+
}
25+
}
26+
27+
private static function clearDirectory(string $directory): void
28+
{
29+
if (! is_dir($directory)) {
30+
return;
31+
}
32+
33+
$files = array_diff(scandir($directory), ['.', '..']);
34+
35+
foreach ($files as $file) {
36+
$filePath = $directory . DIRECTORY_SEPARATOR . $file;
37+
38+
if (is_dir($filePath)) {
39+
self::clearDirectory($filePath);
40+
rmdir($filePath);
41+
} else {
42+
unlink($filePath);
43+
}
44+
}
45+
}
46+
}

packages/Laravel/src/EcotoneProvider.php

Lines changed: 24 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,6 @@
22

33
namespace Ecotone\Laravel;
44

5-
use function class_exists;
6-
7-
use const DIRECTORY_SEPARATOR;
8-
95
use Ecotone\AnnotationFinder\AnnotationFinderFactory;
106
use Ecotone\Messaging\Config\ConfiguredMessagingSystem;
117
use Ecotone\Messaging\Config\ConsoleCommandResultSet;
@@ -19,13 +15,16 @@
1915
use Ecotone\Messaging\ConfigurationVariableService;
2016
use Ecotone\Messaging\Gateway\ConsoleCommandRunner;
2117
use Ecotone\Messaging\Handler\Recoverability\RetryTemplateBuilder;
18+
use Illuminate\Console\Events\CommandFinished;
2219
use Illuminate\Foundation\Console\ClosureCommand;
2320
use Illuminate\Support\Facades\App;
2421
use Illuminate\Support\Facades\Artisan;
2522
use Illuminate\Support\Facades\Config;
2623
use Illuminate\Support\ServiceProvider;
2724
use InvalidArgumentException;
2825
use ReflectionMethod;
26+
use function class_exists;
27+
use const DIRECTORY_SEPARATOR;
2928

3029
/**
3130
* licence Apache-2.0
@@ -55,13 +54,13 @@ public function register()
5554

5655
$errorChannel = Config::get('ecotone.defaultErrorChannel');
5756

58-
$skippedModules = Config::get('ecotone.skippedModulePackageNames');
57+
$skippedModules = Config::get('ecotone.skippedModulePackageNames') ?? [];
5958
/** @TODO Ecotone 2.0 use ServiceContext to configure Laravel */
6059
$applicationConfiguration = ServiceConfiguration::createWithDefaults()
6160
->withEnvironment($environment)
6261
->withLoadCatalog(Config::get('ecotone.loadAppNamespaces') ? 'app' : '')
6362
->withFailFast(false)
64-
->withNamespaces(Config::get('ecotone.namespaces'))
63+
->withNamespaces(Config::get('ecotone.namespaces') ?? [])
6564
->withSkippedModulePackageNames($skippedModules)
6665
->withCacheDirectoryPath($cacheDirectory);
6766

@@ -182,6 +181,9 @@ public function boot()
182181
if (! $this->app->has('logger')) {
183182
$this->app->singleton('logger', LaravelLogger::class);
184183
}
184+
185+
// Hook into Laravel's optimization commands to clear Ecotone cache
186+
$this->registerOptimizationHooks();
185187
}
186188

187189
private function getCacheDirectoryPath(): string
@@ -296,4 +298,20 @@ public function prepareFromCache(mixed $useProductionCache, string $rootCatalog,
296298

297299
return [$serviceCacheConfiguration, $definitionHolder];
298300
}
301+
302+
/**
303+
* Register hooks to clear Ecotone cache when Laravel optimization commands are run
304+
*/
305+
private function registerOptimizationHooks(): void
306+
{
307+
$this->app['events']->listen(
308+
CommandFinished::class,
309+
function ($event) {
310+
// Clear Ecotone cache when optimize commands finishes successfully
311+
if (in_array($event->command, ['optimize', 'optimize:clear', 'cache:clear']) && $event->exitCode === 0) {
312+
EcotoneCacheClear::clearEcotoneCacheDirectories($this->getCacheDirectoryPath());
313+
}
314+
}
315+
);
316+
}
299317
}

packages/Laravel/tests/Application/Execution/ApplicationTest.php

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
namespace Test\Ecotone\Laravel\Application\Execution;
66

7+
use Ecotone\Laravel\EcotoneCacheClear;
78
use Ecotone\Lite\Test\MessagingTestSupport;
89
use Ecotone\Messaging\Config\ConfiguredMessagingSystem;
910
use Ecotone\Modelling\CommandBus;
@@ -31,6 +32,8 @@ public function test_boot_application_with_ecotone()
3132
CommandBus::class,
3233
$app->get(CommandBus::class)
3334
);
35+
36+
EcotoneCacheClear::clearEcotoneCacheDirectories($app->storagePath());
3437
}
3538

3639
public function createApplication()

packages/Laravel/tests/Licence/LicenceTest.php

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,13 @@
44

55
namespace Test\Ecotone\Laravel\Licence;
66

7+
use Ecotone\Laravel\EcotoneCacheClear;
78
use Ecotone\Modelling\CommandBus;
89
use Ecotone\Modelling\QueryBus;
910
use Ecotone\Test\LicenceTesting;
1011
use Illuminate\Foundation\Application;
1112
use Illuminate\Foundation\Http\Kernel;
13+
use Illuminate\Support\Facades\Artisan;
1214
use PHPUnit\Framework\TestCase;
1315

1416
/**
@@ -30,6 +32,8 @@ public function setUp(): void
3032
$this->app = $app;
3133
$this->queryBus = $app->get(QueryBus::class);
3234
$this->commandBus = $app->get(CommandBus::class);
35+
36+
EcotoneCacheClear::clearEcotoneCacheDirectories($app->storagePath());
3337
}
3438

3539
protected function tearDown(): void

packages/Laravel/tests/MultiTenant/MultiTenantTest.php

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,14 @@
55
namespace Test\Ecotone\Laravel\MultiTenant;
66

77
use App\MultiTenant\Application\Command\RegisterCustomer;
8+
use Ecotone\Laravel\EcotoneCacheClear;
89
use Ecotone\Modelling\CommandBus;
910
use Ecotone\Modelling\QueryBus;
1011
use Illuminate\Foundation\Application;
1112
use Illuminate\Foundation\Http\Kernel;
1213
use Illuminate\Support\Facades\Artisan;
1314
use Illuminate\Support\Facades\DB;
15+
use Illuminate\Support\Facades\File;
1416
use PHPUnit\Framework\TestCase;
1517
use RuntimeException;
1618

@@ -37,6 +39,35 @@ public function setUp(): void
3739
$this->app = $app;
3840
$this->queryBus = $app->get(QueryBus::class);
3941
$this->commandBus = $app->get(CommandBus::class);
42+
43+
EcotoneCacheClear::clearEcotoneCacheDirectories($app->storagePath());
44+
}
45+
46+
public function test_optimize_clear_triggers_ecotone_cache_clear_via_event(): void
47+
{
48+
$laravelCacheDirectory = storage_path('framework/cache/data/ecotone');
49+
$liteCacheDirectory = storage_path('framework/cache/data/ecotone');
50+
51+
// Create cache directories and test files
52+
if(!File::exists($laravelCacheDirectory)) {
53+
File::makeDirectory($laravelCacheDirectory, 0755, true);
54+
}
55+
File::put($laravelCacheDirectory . '/test_cache_file', 'test content');
56+
57+
if(!File::exists($liteCacheDirectory)) {
58+
File::makeDirectory($liteCacheDirectory, 0755, true);
59+
}
60+
File::put($liteCacheDirectory . '/test_lite_cache_file', 'lite test content');
61+
62+
$this->assertTrue(File::exists($laravelCacheDirectory . '/test_cache_file'));
63+
$this->assertTrue(File::exists($liteCacheDirectory . '/test_lite_cache_file'));
64+
65+
// Run optimize:clear command - this should trigger the CommandFinished event
66+
Artisan::call('optimize:clear');
67+
68+
// Verify both Ecotone cache files were removed via the event listener
69+
$this->assertFalse(File::exists($laravelCacheDirectory . '/test_cache_file'));
70+
$this->assertFalse(File::exists($liteCacheDirectory . '/test_lite_cache_file'));
4071
}
4172

4273
public function tearDown(): void
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
<?php
2+
3+
namespace Ecotone\SymfonyBundle\DependencyInjection\Compiler;
4+
5+
use Ecotone\Messaging\Config\ConfiguredMessagingSystem;
6+
use Ecotone\Messaging\Config\ServiceCacheConfiguration;
7+
use Ecotone\Messaging\Handler\Gateway\ProxyFactory;
8+
use Symfony\Component\HttpKernel\CacheClearer\CacheClearerInterface;
9+
use Symfony\Component\HttpKernel\CacheWarmer\CacheWarmerInterface;
10+
11+
/**
12+
* licence Apache-2.0
13+
*/
14+
class CacheClearer implements CacheClearerInterface
15+
{
16+
public function __construct(
17+
private ServiceCacheConfiguration $serviceCacheConfiguration
18+
) {
19+
}
20+
21+
public function isOptional(): bool
22+
{
23+
return true;
24+
}
25+
26+
/**
27+
* Clears any caches necessary.
28+
*/
29+
public function clear(string $cacheDir): void
30+
{
31+
$this->deleteDirectory($this->serviceCacheConfiguration->getPath());
32+
$this->deleteDirectory(sys_get_temp_dir() . DIRECTORY_SEPARATOR . 'ecotone');
33+
}
34+
35+
private function deleteDirectory(string $directory): void
36+
{
37+
if (! is_dir($directory)) {
38+
return;
39+
}
40+
41+
$files = array_diff(scandir($directory), ['.', '..']);
42+
43+
foreach ($files as $file) {
44+
$filePath = $directory . DIRECTORY_SEPARATOR . $file;
45+
46+
if (is_dir($filePath)) {
47+
$this->deleteDirectory($filePath);
48+
rmdir($filePath);
49+
} else {
50+
unlink($filePath);
51+
}
52+
}
53+
}
54+
}

packages/Symfony/DependencyInjection/EcotoneExtension.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
use Ecotone\Messaging\Gateway\ConsoleCommandRunner;
1010
use Ecotone\Messaging\Handler\Logger\LoggingGateway;
1111
use Ecotone\Messaging\Handler\Recoverability\RetryTemplateBuilder;
12+
use Ecotone\SymfonyBundle\DependencyInjection\Compiler\CacheClearer;
1213
use Ecotone\SymfonyBundle\DependencyInjection\Compiler\CacheWarmer;
1314
use Ecotone\SymfonyBundle\DependencyInjection\Compiler\SymfonyConfigurationVariableService;
1415
use Symfony\Component\DependencyInjection\ContainerBuilder;
@@ -82,6 +83,7 @@ public function load(array $configs, ContainerBuilder $container): void
8283
]);
8384

8485
$container->register(CacheWarmer::class)->setAutowired(true)->addTag('kernel.cache_warmer');
86+
$container->register(CacheClearer::class)->setAutowired(true)->addTag('kernel.cache_clearer')->setPublic(true);
8587

8688
$messagingConfiguration = MessagingSystemConfiguration::prepare(
8789
realpath(($container->hasParameter('kernel.project_dir') ? $container->getParameter('kernel.project_dir') : $container->getParameter('kernel.root_dir') . '/..')),
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
<?php
2+
3+
namespace Test;
4+
5+
use Ecotone\Messaging\Config\ServiceCacheConfiguration;
6+
use Ecotone\SymfonyBundle\DependencyInjection\Compiler\CacheClearer;
7+
use Symfony\Bundle\FrameworkBundle\Console\Application;
8+
use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase;
9+
use Symfony\Component\Console\Tester\CommandTester;
10+
use Symfony\Component\Filesystem\Filesystem;
11+
12+
/**
13+
* @internal
14+
*/
15+
/**
16+
* licence Apache-2.0
17+
* @internal
18+
*/
19+
class CacheClearingTest extends KernelTestCase
20+
{
21+
private string $symfonyEcotoneCacheDir;
22+
private string $ecotoneLiteCacheDir;
23+
private Filesystem $filesystem;
24+
25+
protected function setUp(): void
26+
{
27+
parent::setUp();
28+
29+
self::bootKernel([
30+
'environment' => 'test',
31+
]);
32+
33+
$this->filesystem = new Filesystem();
34+
35+
// Get the actual cache directories
36+
/** @var ServiceCacheConfiguration $cacheConfig */
37+
$cacheConfig = self::getContainer()->get(ServiceCacheConfiguration::REFERENCE_NAME);
38+
$this->symfonyEcotoneCacheDir = $cacheConfig->getPath();
39+
$this->ecotoneLiteCacheDir = sys_get_temp_dir() . DIRECTORY_SEPARATOR . 'ecotone';
40+
}
41+
42+
public function test_cache_clear_command_triggers_ecotone_cache_clearing(): void
43+
{
44+
// Create cache directories and test files
45+
$this->filesystem->mkdir($this->symfonyEcotoneCacheDir);
46+
$this->filesystem->dumpFile($this->symfonyEcotoneCacheDir . '/test_symfony_cache.txt', 'Symfony Ecotone cache content');
47+
48+
$this->filesystem->mkdir($this->ecotoneLiteCacheDir);
49+
$this->filesystem->dumpFile($this->ecotoneLiteCacheDir . '/test_lite_cache.txt', 'EcotoneLite cache content');
50+
51+
// Verify files exist
52+
$this->assertTrue($this->filesystem->exists($this->symfonyEcotoneCacheDir . '/test_symfony_cache.txt'));
53+
$this->assertTrue($this->filesystem->exists($this->ecotoneLiteCacheDir . '/test_lite_cache.txt'));
54+
55+
// Run cache:clear command with --no-warmup to avoid cache regeneration issues
56+
$application = new Application(self::$kernel);
57+
$command = $application->find('cache:clear');
58+
$commandTester = new CommandTester($command);
59+
$commandTester->execute(['--no-warmup' => true]);
60+
61+
// Verify both Ecotone cache files were removed by the cache:clear command
62+
$this->assertFalse($this->filesystem->exists($this->symfonyEcotoneCacheDir . '/test_symfony_cache.txt'));
63+
$this->assertFalse($this->filesystem->exists($this->ecotoneLiteCacheDir . '/test_lite_cache.txt'));
64+
65+
// Verify command executed successfully
66+
$this->assertEquals(0, $commandTester->getStatusCode());
67+
}
68+
}

packages/Symfony/tests/phpunit/Licence/LicenceTest.php

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66

77
use Ecotone\Modelling\CommandBus;
88
use Ecotone\Modelling\QueryBus;
9+
use Ecotone\SymfonyBundle\DependencyInjection\Compiler\CacheClearer;
910
use Ecotone\Test\LicenceTesting;
1011
use PHPUnit\Framework\TestCase;
1112
use Symfony\App\Licence\Configuration\Kernel;
@@ -30,6 +31,8 @@ public function setUp(): void
3031
$this->commandBus = $app->get(CommandBus::class);
3132
$this->queryBus = $app->get(QueryBus::class);
3233
$this->kernel = $kernel;
34+
35+
$this->kernel->getContainer()->get(CacheClearer::class)->clear('');
3336
}
3437

3538
protected function tearDown(): void

packages/Symfony/tests/phpunit/MultiTenant/MultiTenantTest.php

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66

77
use Ecotone\Modelling\CommandBus;
88
use Ecotone\Modelling\QueryBus;
9+
use Ecotone\SymfonyBundle\DependencyInjection\Compiler\CacheClearer;
910
use PHPUnit\Framework\TestCase;
1011
use RuntimeException;
1112
use Symfony\App\MultiTenant\Application\Command\RegisterCustomer;
@@ -39,6 +40,8 @@ public function setUp(): void
3940
$this->commandBus = $app->get(CommandBus::class);
4041
$this->queryBus = $app->get(QueryBus::class);
4142
$this->kernel = $kernel;
43+
44+
$this->kernel->getContainer()->get(CacheClearer::class)->clear('');
4245
}
4346

4447
protected function tearDown(): void

0 commit comments

Comments
 (0)