From a6102be1a01c1ef4c6b8bff702722dddd90cc527 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Samuel=20=C5=A0tancl?= Date: Sat, 14 Mar 2020 16:49:20 +0100 Subject: [PATCH] wip cache invalidation --- .../Database/CachedTenantResolver.php | 23 +++++ .../Database/DatabaseStorageDriver.php | 32 +++++-- .../Database/DomainRepository.php | 3 +- tests/CachedResolverTest.php | 86 ++++++++++++++++--- 4 files changed, 124 insertions(+), 20 deletions(-) diff --git a/src/StorageDrivers/Database/CachedTenantResolver.php b/src/StorageDrivers/Database/CachedTenantResolver.php index 0fac3e80..ce7090ea 100644 --- a/src/StorageDrivers/Database/CachedTenantResolver.php +++ b/src/StorageDrivers/Database/CachedTenantResolver.php @@ -42,5 +42,28 @@ class CachedTenantResolver return Tenant::fromStorage($data)->withDomains($domains); } + public function invalidateTenant(string $id): void + { + $this->invalidateTenantData($id); + $this->invalidateTenantDomains($id); + } + + public function invalidateTenantData(string $id): void + { + $this->cache->forget('_tenancy_id_to_data:' . $id); + } + + public function invalidateTenantDomains(string $id): void + { + $this->cache->forget('_tenancy_id_to_domains:' . $id); + } + + public function invalidateDomainToIdMapping(array $domains): void + { + foreach ($domains as $domain) { + $this->cache->forget('_tenancy_domain_to_id:' . $domain); + } + } + // todo update cache on writes to data & domains } diff --git a/src/StorageDrivers/Database/DatabaseStorageDriver.php b/src/StorageDrivers/Database/DatabaseStorageDriver.php index 457b8373..42698e3c 100644 --- a/src/StorageDrivers/Database/DatabaseStorageDriver.php +++ b/src/StorageDrivers/Database/DatabaseStorageDriver.php @@ -152,11 +152,18 @@ class DatabaseStorageDriver implements StorageDriver, CanDeleteKeys, CanFindByAn public function updateTenant(Tenant $tenant): void { - $this->centralDatabase->transaction(function () use ($tenant) { + $originalDomains = $this->domains->getTenantDomains($tenant); + + $this->centralDatabase->transaction(function () use ($tenant, $originalDomains) { $this->tenants->updateTenant($tenant); - $this->domains->updateTenantDomains($tenant); + $this->domains->updateTenantDomains($tenant, $originalDomains); }); + + if ($this->usesCache()) { + $this->cache->invalidateTenant($tenant->id); + $this->cache->invalidateDomainToIdMapping($originalDomains); + } } public function deleteTenant(Tenant $tenant): void @@ -203,17 +210,32 @@ class DatabaseStorageDriver implements StorageDriver, CanDeleteKeys, CanFindByAn public function put(string $key, $value, Tenant $tenant = null): void { - $this->tenants->put($key, $value, $tenant ?? $this->currentTenant()); + $tenant = $tenant ?? $this->currentTenant(); + $this->tenants->put($key, $value, $tenant); + + if ($this->usesCache()) { + $this->cache->invalidateTenantData($tenant->id); + } } public function putMany(array $kvPairs, Tenant $tenant = null): void { - $this->tenants->putMany($kvPairs, $tenant ?? $this->currentTenant()); + $tenant = $tenant ?? $this->currentTenant(); + $this->tenants->putMany($kvPairs, $tenant); + + if ($this->usesCache()) { + $this->cache->invalidateTenantData($tenant->id); + } } public function deleteMany(array $keys, Tenant $tenant = null): void { - $this->tenants->deleteMany($keys, $tenant ?? $this->currentTenant()); + $tenant = $tenant ?? $this->currentTenant(); + $this->tenants->deleteMany($keys, $tenant); + + if ($this->usesCache()) { + $this->cache->invalidateTenantData($tenant->id); + } } public function usesCache(): bool diff --git a/src/StorageDrivers/Database/DomainRepository.php b/src/StorageDrivers/Database/DomainRepository.php index e8ac2f13..4e21b9ad 100644 --- a/src/StorageDrivers/Database/DomainRepository.php +++ b/src/StorageDrivers/Database/DomainRepository.php @@ -33,9 +33,8 @@ class DomainRepository extends Repository }, $tenant->domains)); } - public function updateTenantDomains(Tenant $tenant) + public function updateTenantDomains(Tenant $tenant, array $originalDomains) { - $originalDomains = $this->getTenantDomains($tenant); $deletedDomains = array_diff($originalDomains, $tenant->domains); $newDomains = array_diff($tenant->domains, $originalDomains); diff --git a/tests/CachedResolverTest.php b/tests/CachedResolverTest.php index 188dcea3..4f618b8b 100644 --- a/tests/CachedResolverTest.php +++ b/tests/CachedResolverTest.php @@ -4,6 +4,8 @@ declare(strict_types=1); namespace Stancl\Tenancy\Tests; +use Illuminate\Support\Facades\Cache; +use Stancl\Tenancy\StorageDrivers\Database\DatabaseStorageDriver; use Stancl\Tenancy\Tenant; class CachedResolverTest extends TestCase @@ -23,43 +25,101 @@ class CachedResolverTest extends TestCase /** @test */ public function a_query_is_not_made_for_tenant_id_once_domain_is_cached() { - $tenant = Tenant::new()->withDomains(['foo.localhost'])->save(); + $tenant = Tenant::new() + ->withData(['foo' => 'bar']) + ->withDomains(['foo.localhost']) + ->save(); - // todo assert query is made: + // query is made $queried = tenancy()->findByDomain('foo.localhost'); + $this->assertEquals($tenant->data, $queried->data); + $this->assertSame($tenant->domains, $queried->domains); - // todo assert query is not made but cache call is made: + // cache is set + $this->assertEquals($tenant->data, Cache::get('_tenancy_id_to_data:' . $tenant->id)); + $this->assertSame($tenant->domains, Cache::get('_tenancy_id_to_domains:' . $tenant->id)); + + // query is not made + DatabaseStorageDriver::getCentralConnection()->enableQueryLog(); $cached = tenancy()->findByDomain('foo.localhost'); + $this->assertEquals($tenant->data, $cached->data); + $this->assertSame($tenant->domains, $cached->domains); + $this->assertSame([], DatabaseStorageDriver::getCentralConnection()->getQueryLog()); } /** @test */ public function a_query_is_not_made_for_tenant_once_id_is_cached() { - $tenant = Tenant::new()->withData(['id' => '123'])->save(); + $tenant = Tenant::new() + ->withData(['foo' => 'bar']) + ->withDomains(['foo.localhost']) + ->save(); - // todo assert query is made: - $queried = tenancy()->find('123'); + // query is made + $queried = tenancy()->find($tenant->id); + $this->assertEquals($tenant->data, $queried->data); + $this->assertSame($tenant->domains, $queried->domains); - // todo assert query is not made but cache call is made: - $cached = tenancy()->find('123'); + // cache is set + $this->assertEquals($tenant->data, Cache::get('_tenancy_id_to_data:' . $tenant->id)); + $this->assertSame($tenant->domains, Cache::get('_tenancy_id_to_domains:' . $tenant->id)); + + // query is not made + DatabaseStorageDriver::getCentralConnection()->enableQueryLog(); + $cached = tenancy()->find($tenant->id); + $this->assertEquals($tenant->data, $cached->data); + $this->assertSame($tenant->domains, $cached->domains); + $this->assertSame([], DatabaseStorageDriver::getCentralConnection()->getQueryLog()); } /** @test */ public function modifying_tenants_domains_updates_domains_in_the_cached_domain_to_id_mapping() { + // todo adding domain adds mapping + // todo removing domain removes mapping } /** @test */ public function modifying_tenants_data_updates_data_in_the_cached_id_to_tenant_data_mapping() { - $tenant = Tenant::new()->withData(['id' => '123', 'foo' => 'bar'])->save(); + $tenant = Tenant::new()->withData(['foo' => 'bar'])->save(); - // todo assert cache record is set - $this->assertSame('bar', tenancy()->find('123')->get('foo')); + // cache record is set + $this->assertSame('bar', tenancy()->find($tenant->id)->get('foo')); + $this->assertSame('bar', Cache::get('_tenancy_id_to_data:' . $tenant->id)['foo']); - // todo assert cache record is updated + // cache record is invalidated $tenant->set('foo', 'xyz'); + $this->assertSame(null, Cache::get('_tenancy_id_to_data:' . $tenant->id)); - $this->assertSame('xyz', tenancy()->find('123')->get('foo')); + // cache record is set + $this->assertSame('xyz', tenancy()->find($tenant->id)->get('foo')); + $this->assertSame('xyz', Cache::get('_tenancy_id_to_data:' . $tenant->id)['foo']); + + // cache record is invalidated + $tenant->foo = 'abc'; + $tenant->save(); + $this->assertSame(null, Cache::get('_tenancy_id_to_data:' . $tenant->id)); } + + /** @test */ + public function modifying_tenants_domains_updates_domains_in_the_cached_id_to_tenant_domains_mapping() + { + $tenant = Tenant::new() + ->withData(['foo' => 'bar']) + ->withDomains(['foo.localhost']) + ->save(); + + // cache record is set + $this->assertSame(['foo.localhost'], tenancy()->find($tenant->id)->domains); + $this->assertSame(['foo.localhost'], Cache::get('_tenancy_id_to_domains:' . $tenant->id)); + + // cache record is invalidated + $tenant->addDomains(['bar.localhost'])->save(); + $this->assertEquals(null, Cache::get('_tenancy_id_to_domains:' . $tenant->id)); + + $this->assertEquals(['foo.localhost', 'bar.localhost'], tenancy()->find($tenant->id)->domains); + } + + // todo deleting tenant invalidates all caches }