Repository navigation
FOUR-20532 clear cache by pattern #7795
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
Changes from 2 commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,9 @@ | ||
| <?php | ||
|
|
||
| namespace ProcessMaker\Cache\Settings; | ||
|
|
||
| use Exception; | ||
|
|
||
| class SettingCacheException extends Exception | ||
| { | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -3,15 +3,19 @@ | |
| namespace ProcessMaker\Cache\Settings; | ||
|
|
||
| use Illuminate\Cache\CacheManager; | ||
| use Illuminate\Contracts\Cache\Repository; | ||
| use Illuminate\Support\Facades\Redis; | ||
| use ProcessMaker\Cache\CacheInterface; | ||
|
|
||
| class SettingCacheManager implements CacheInterface | ||
| { | ||
| protected CacheManager $cacheManager; | ||
| protected Repository $cacheManager; | ||
|
|
||
| public function __construct(CacheManager $cacheManager) | ||
| { | ||
| $this->cacheManager = $cacheManager; | ||
| $driver = env('CACHE_SETTING_DRIVER') ?? env('CACHE_DRIVER', 'redis'); | ||
|
|
||
| $this->cacheManager = $cacheManager->store($driver); | ||
| } | ||
|
|
||
| /** | ||
|
|
@@ -108,6 +112,34 @@ public function clear(): bool | |
| return $this->cacheManager->flush(); | ||
| } | ||
|
|
||
| /** | ||
| * Remove items from the settings cache by a given pattern. | ||
| * | ||
| * @param string $pattern | ||
| * | ||
| * @throws \Exception | ||
| * @return void | ||
| */ | ||
| public function clearBy(string $pattern): void | ||
| { | ||
| try { | ||
| // get the connection name from the cache manager | ||
| $connection = $this->cacheManager->connection()->getName(); | ||
| // Get all keys | ||
| $keys = Redis::connection($connection)->keys('*'); | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What if the cache drive is not Redis?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. A validation was added |
||
| // Filter keys by pattern | ||
| $matchedKeys = array_filter($keys, fn($key) => preg_match('/' . $pattern . '/', $key)); | ||
|
|
||
| if (!empty($matchedKeys)) { | ||
| Redis::connection($connection)->del($matchedKeys); | ||
| } | ||
| } catch (\Exception $e) { | ||
| \Log::error('SettingCacheException' . $e->getMessage()); | ||
|
|
||
| throw new SettingCacheException('Failed to delete keys.'); | ||
| } | ||
| } | ||
|
|
||
| /** | ||
| * Check if a value exists in the settings cache. | ||
| * | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -3,8 +3,12 @@ | |
| namespace Tests\Feature\Cache; | ||
|
|
||
| use Illuminate\Foundation\Testing\RefreshDatabase; | ||
| use Illuminate\Support\Facades\Cache; | ||
| use Illuminate\Support\Facades\DB; | ||
| use Illuminate\Support\Facades\Redis; | ||
| use ProcessMaker\Cache\Settings\SettingCacheException; | ||
| use ProcessMaker\Models\Setting; | ||
| use ProcessMaker\Models\User; | ||
| use Tests\Feature\Shared\RequestHelper; | ||
| use Tests\TestCase; | ||
|
|
||
|
|
@@ -13,6 +17,25 @@ class SettingCacheTest extends TestCase | |
| use RequestHelper; | ||
| use RefreshDatabase; | ||
|
|
||
| protected function setUp(): void | ||
| { | ||
| parent::setUp(); | ||
|
|
||
| $this->user = User::factory()->create([ | ||
| 'is_administrator' => true, | ||
| ]); | ||
|
|
||
| putenv('CACHE_SETTING_DRIVER=cache_settings'); | ||
| } | ||
|
|
||
| protected function tearDown(): void | ||
| { | ||
| \SettingCache::clear(); | ||
|
|
||
| putenv('CACHE_SETTING_DRIVER'); | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. consider to use config instead of env
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It was fixed |
||
| parent::tearDown(); | ||
| } | ||
|
|
||
| private function upgrade() | ||
| { | ||
| $this->artisan('migrate', [ | ||
|
|
@@ -115,4 +138,104 @@ public function testGetSettingByNotExistingKey() | |
|
|
||
| $this->assertNull($setting); | ||
| } | ||
|
|
||
| public function testClearByPattern() | ||
| { | ||
| \SettingCache::set('password-policies.users_can_change', 1); | ||
| \SettingCache::set('password-policies.numbers', 2); | ||
| \SettingCache::set('password-policies.uppercase', 3); | ||
| Cache::put('session-control.ip_restriction', 0); | ||
|
|
||
| $this->assertEquals(1, \SettingCache::get('password-policies.users_can_change')); | ||
| $this->assertEquals(2, \SettingCache::get('password-policies.numbers')); | ||
| $this->assertEquals(3, \SettingCache::get('password-policies.uppercase')); | ||
|
|
||
| $pattern = 'password-policies'; | ||
|
|
||
| \SettingCache::clearBy($pattern); | ||
|
|
||
| $this->assertNull(\SettingCache::get('password-policies.users_can_change')); | ||
| $this->assertNull(\SettingCache::get('password-policies.numbers')); | ||
| $this->assertNull(\SettingCache::get('password-policies.uppercase')); | ||
| } | ||
|
|
||
| public function testClearByPatternRemainUnmatched() | ||
| { | ||
| \SettingCache::set('session-control.ip_restriction', 0); | ||
| \SettingCache::set('password-policies.users_can_change', 1); | ||
| \SettingCache::set('password-policies.numbers', 2); | ||
| \SettingCache::set('password-policies.uppercase', 3); | ||
|
|
||
| $this->assertEquals(0, \SettingCache::get('session-control.ip_restriction')); | ||
| $this->assertEquals(1, \SettingCache::get('password-policies.users_can_change')); | ||
| $this->assertEquals(2, \SettingCache::get('password-policies.numbers')); | ||
| $this->assertEquals(3, \SettingCache::get('password-policies.uppercase')); | ||
|
|
||
| $pattern = 'password-policies'; | ||
|
|
||
| \SettingCache::clearBy($pattern); | ||
|
|
||
| $this->assertEquals(0, \SettingCache::get('session-control.ip_restriction')); | ||
| $this->assertNull(\SettingCache::get('password-policies.users_can_change')); | ||
| $this->assertNull(\SettingCache::get('password-policies.numbers')); | ||
| $this->assertNull(\SettingCache::get('password-policies.uppercase')); | ||
| } | ||
|
|
||
| public function testClearByPatternWithFailedDeletion() | ||
| { | ||
| $pattern = 'test_pattern'; | ||
| $keys = [ | ||
| 'settings:test_pattern:1', | ||
| 'settings:test_pattern:2' | ||
| ]; | ||
| \SettingCache::set('test_pattern:1', 1); | ||
| \SettingCache::set('test_pattern:2', 2); | ||
|
|
||
| Redis::shouldReceive('keys') | ||
| ->with('*settings:*') | ||
| ->andReturn($keys); | ||
|
|
||
| Redis::shouldReceive('del') | ||
| ->with($keys) | ||
| ->andThrow(new SettingCacheException('Failed to delete keys.')); | ||
|
|
||
| $this->expectException(SettingCacheException::class); | ||
| $this->expectExceptionMessage('Failed to delete keys.'); | ||
|
|
||
| \SettingCache::clearBy($pattern); | ||
| } | ||
|
|
||
| public function testClearAllSettings() | ||
| { | ||
| \SettingCache::set('password-policies.users_can_change', 1); | ||
| \SettingCache::set('password-policies.numbers', 2); | ||
| \SettingCache::set('password-policies.uppercase', 3); | ||
|
|
||
| $this->assertEquals(1, \SettingCache::get('password-policies.users_can_change')); | ||
| $this->assertEquals(2, \SettingCache::get('password-policies.numbers')); | ||
| $this->assertEquals(3, \SettingCache::get('password-policies.uppercase')); | ||
|
|
||
| \SettingCache::clear(); | ||
|
|
||
| $this->assertNull(\SettingCache::get('password-policies.users_can_change')); | ||
| $this->assertNull(\SettingCache::get('password-policies.numbers')); | ||
| $this->assertNull(\SettingCache::get('password-policies.uppercase')); | ||
| } | ||
|
|
||
| public function testClearOnlySettings() | ||
| { | ||
| \SettingCache::set('password-policies.users_can_change', 1); | ||
| \SettingCache::set('password-policies.numbers', 2); | ||
| Cache::put('password-policies.uppercase', 3); | ||
|
|
||
| $this->assertEquals(1, \SettingCache::get('password-policies.users_can_change')); | ||
| $this->assertEquals(2, \SettingCache::get('password-policies.numbers')); | ||
| $this->assertEquals(3, Cache::get('password-policies.uppercase')); | ||
|
|
||
| \SettingCache::clear(); | ||
|
|
||
| $this->assertNull(\SettingCache::get('password-policies.users_can_change')); | ||
| $this->assertNull(\SettingCache::get('password-policies.numbers')); | ||
| $this->assertEquals(3, Cache::get('password-policies.uppercase')); | ||
| } | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Use config instead of env.
In a production environment the .env file is not loaded, only the config from cache.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It was fixed