mirror of
https://github.com/archtechx/tenancy.git
synced 2026-02-05 17:34:03 +00:00
Begin work on cached lookup
This commit is contained in:
parent
142912edc5
commit
c52d12cb99
6 changed files with 149 additions and 10 deletions
|
|
@ -16,6 +16,8 @@ return [
|
||||||
'tenants' => 'tenants',
|
'tenants' => 'tenants',
|
||||||
'domains' => 'domains',
|
'domains' => 'domains',
|
||||||
],
|
],
|
||||||
|
'cache_store' => false, // What store should be used to cache tenant resolution. Set to false to disable cache, null to use default store, or a string with a specific cache store name.
|
||||||
|
'cache_ttl' => 3600, // seconds
|
||||||
],
|
],
|
||||||
'redis' => [
|
'redis' => [
|
||||||
'driver' => Stancl\Tenancy\StorageDrivers\RedisStorageDriver::class,
|
'driver' => Stancl\Tenancy\StorageDrivers\RedisStorageDriver::class,
|
||||||
|
|
|
||||||
45
src/StorageDrivers/Database/CachedTenantResolver.php
Normal file
45
src/StorageDrivers/Database/CachedTenantResolver.php
Normal file
|
|
@ -0,0 +1,45 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Stancl\Tenancy\StorageDrivers\Database;
|
||||||
|
|
||||||
|
use Closure;
|
||||||
|
use Illuminate\Cache\CacheManager;
|
||||||
|
use Illuminate\Config\Repository as ConfigRepository;
|
||||||
|
use Illuminate\Cache\Repository as CacheRepository;
|
||||||
|
use Illuminate\Contracts\Cache\Store;
|
||||||
|
use Stancl\Tenancy\Tenant;
|
||||||
|
|
||||||
|
class CachedTenantResolver
|
||||||
|
{
|
||||||
|
/** @var CacheRepository */
|
||||||
|
protected $cache;
|
||||||
|
|
||||||
|
/** @var ConfigRepository */
|
||||||
|
protected $config;
|
||||||
|
|
||||||
|
public function __construct(CacheManager $cacheManager, ConfigRepository $config)
|
||||||
|
{
|
||||||
|
$this->cache = $cacheManager->store($config->get('tenancy.storage_drivers.db.cache_store'));
|
||||||
|
$this->config = $config;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function ttl(): int
|
||||||
|
{
|
||||||
|
return $this->config->get('tenancy.storage_drivers.db.cache_ttl');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getTenantIdByDomain(string $domain, Closure $query): string
|
||||||
|
{
|
||||||
|
return $this->cache->remember('_tenancy_domain_to_id:' . $domain, $this->ttl(), $query);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function findById(string $id, Closure $dataQuery, Closure $domainsQuery): Tenant
|
||||||
|
{
|
||||||
|
$data = $this->cache->remember('_tenancy_id_to_data:' . $id, $this->ttl(), $dataQuery);
|
||||||
|
$domains = $this->cache->remember('_tenancy_id_to_domains:' . $id, $this->ttl(), $domainsQuery);
|
||||||
|
|
||||||
|
return Tenant::fromStorage($data)->withDomains($domains);
|
||||||
|
}
|
||||||
|
|
||||||
|
// todo update cache on writes to data & domains
|
||||||
|
}
|
||||||
|
|
@ -32,12 +32,16 @@ class DatabaseStorageDriver implements StorageDriver, CanDeleteKeys, CanFindByAn
|
||||||
/** @var DomainRepository */
|
/** @var DomainRepository */
|
||||||
protected $domains;
|
protected $domains;
|
||||||
|
|
||||||
|
/** @var CachedTenantResolver */
|
||||||
|
protected $cache;
|
||||||
|
|
||||||
/** @var Tenant The default tenant. */
|
/** @var Tenant The default tenant. */
|
||||||
protected $tenant;
|
protected $tenant;
|
||||||
|
|
||||||
public function __construct(Application $app, ConfigRepository $config)
|
public function __construct(Application $app, ConfigRepository $config, CachedTenantResolver $cache)
|
||||||
{
|
{
|
||||||
$this->app = $app;
|
$this->app = $app;
|
||||||
|
$this->cache = $cache;
|
||||||
$this->centralDatabase = $this->getCentralConnection();
|
$this->centralDatabase = $this->getCentralConnection();
|
||||||
$this->tenants = new TenantRepository($config);
|
$this->tenants = new TenantRepository($config);
|
||||||
$this->domains = new DomainRepository($config);
|
$this->domains = new DomainRepository($config);
|
||||||
|
|
@ -60,7 +64,16 @@ class DatabaseStorageDriver implements StorageDriver, CanDeleteKeys, CanFindByAn
|
||||||
|
|
||||||
public function findByDomain(string $domain): Tenant
|
public function findByDomain(string $domain): Tenant
|
||||||
{
|
{
|
||||||
$id = $this->domains->getTenantIdByDomain($domain);
|
$query = function () use ($domain) {
|
||||||
|
return $this->domains->getTenantIdByDomain($domain);
|
||||||
|
};
|
||||||
|
|
||||||
|
if ($this->usesCache()) {
|
||||||
|
$id = $this->cache->getTenantIdByDomain($domain, $query);
|
||||||
|
} else {
|
||||||
|
$id = $query();
|
||||||
|
}
|
||||||
|
|
||||||
if (! $id) {
|
if (! $id) {
|
||||||
throw new TenantCouldNotBeIdentifiedException($domain);
|
throw new TenantCouldNotBeIdentifiedException($domain);
|
||||||
}
|
}
|
||||||
|
|
@ -70,14 +83,25 @@ class DatabaseStorageDriver implements StorageDriver, CanDeleteKeys, CanFindByAn
|
||||||
|
|
||||||
public function findById(string $id): Tenant
|
public function findById(string $id): Tenant
|
||||||
{
|
{
|
||||||
$tenant = $this->tenants->find($id);
|
$dataQuery = function () use ($id) {
|
||||||
|
return $this->tenants->decodeData($this->tenants->find($id));
|
||||||
|
};
|
||||||
|
$domainsQuery = function () use ($id) {
|
||||||
|
return $this->domains->getTenantDomains($id);
|
||||||
|
};
|
||||||
|
|
||||||
if (! $tenant) {
|
if ($this->usesCache()) {
|
||||||
|
return $this->cache->findById($id, $dataQuery, $domainsQuery);
|
||||||
|
} else {
|
||||||
|
$data = $dataQuery();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (! $data) {
|
||||||
throw new TenantDoesNotExistException($id);
|
throw new TenantDoesNotExistException($id);
|
||||||
}
|
}
|
||||||
|
|
||||||
return Tenant::fromStorage($this->tenants->decodeData($tenant))
|
return Tenant::fromStorage($data)
|
||||||
->withDomains($this->domains->getTenantDomains($id));
|
->withDomains($domainsQuery());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -191,4 +215,10 @@ class DatabaseStorageDriver implements StorageDriver, CanDeleteKeys, CanFindByAn
|
||||||
{
|
{
|
||||||
$this->tenants->deleteMany($keys, $tenant ?? $this->currentTenant());
|
$this->tenants->deleteMany($keys, $tenant ?? $this->currentTenant());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function usesCache(): bool
|
||||||
|
{
|
||||||
|
// null is also truthy here
|
||||||
|
return $this->app['config']['tenancy.storage_drivers.db.cache_store'] !== false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
8
test
8
test
|
|
@ -1,7 +1,7 @@
|
||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
set -e
|
set -e
|
||||||
|
|
||||||
printf "Variant 1\n\n"
|
printf "Variant 1 (DB)\n\n"
|
||||||
docker-compose exec test env TENANCY_TEST_STORAGE_DRIVER=db vendor/bin/phpunit --coverage-php coverage/2.cov "$@"
|
docker-compose exec test env TENANCY_TEST_STORAGE_DRIVER=db vendor/bin/phpunit --coverage-php coverage/1.cov "$@"
|
||||||
printf "Variant 2\n\n"
|
printf "Variant 2 (Redis)\n\n"
|
||||||
docker-compose exec test env TENANCY_TEST_STORAGE_DRIVER=redis vendor/bin/phpunit --coverage-php coverage/1.cov "$@"
|
docker-compose exec test env TENANCY_TEST_STORAGE_DRIVER=redis vendor/bin/phpunit --coverage-php coverage/2.cov "$@"
|
||||||
|
|
|
||||||
61
tests/CachedResolverTest.php
Normal file
61
tests/CachedResolverTest.php
Normal file
|
|
@ -0,0 +1,61 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Stancl\Tenancy\Tests;
|
||||||
|
|
||||||
|
use Stancl\Tenancy\Tenant;
|
||||||
|
|
||||||
|
class CachedResolverTest extends TestCase
|
||||||
|
{
|
||||||
|
public function setUp(): void
|
||||||
|
{
|
||||||
|
parent::setUp();
|
||||||
|
|
||||||
|
if (config('tenancy.storage_driver') !== 'db') {
|
||||||
|
$this->markTestSkipped('This test is only relevant for the DB storage driver.');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @test */
|
||||||
|
public function a_query_is_not_made_for_tenant_id_once_domain_is_cached()
|
||||||
|
{
|
||||||
|
$tenant = Tenant::new()->withDomains(['foo.localhost']);
|
||||||
|
|
||||||
|
// todo assert query is made:
|
||||||
|
$queried = tenancy()->findByDomain('foo.localhost');
|
||||||
|
|
||||||
|
// todo assert query is not made but cache call is made:
|
||||||
|
$cached = tenancy()->findByDomain('foo.localhost');
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @test */
|
||||||
|
public function a_query_is_not_made_for_tenant_once_id_is_cached()
|
||||||
|
{
|
||||||
|
$tenant = Tenant::new()->withData(['id' => '123']);
|
||||||
|
|
||||||
|
// todo assert query is made:
|
||||||
|
$queried = tenancy()->find('123');
|
||||||
|
|
||||||
|
// todo assert query is not made but cache call is made:
|
||||||
|
$cached = tenancy()->find('123');
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @test */
|
||||||
|
public function modifying_tenants_domains_updates_domains_in_the_cached_domain_to_id_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']);
|
||||||
|
|
||||||
|
// todo assert cache record is set
|
||||||
|
$this->assertSame('bar', tenancy()->find('123')->get('foo'));
|
||||||
|
|
||||||
|
// todo assert cache record is updated
|
||||||
|
$tenant->set('foo', 'xyz');
|
||||||
|
|
||||||
|
$this->assertSame('xyz', tenancy()->find('123')->get('foo'));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -99,6 +99,7 @@ abstract class TestCase extends \Orchestra\Testbench\TestCase
|
||||||
'tenancy.redis.prefixed_connections' => ['default'],
|
'tenancy.redis.prefixed_connections' => ['default'],
|
||||||
'tenancy.migration_paths' => [database_path('../migrations')],
|
'tenancy.migration_paths' => [database_path('../migrations')],
|
||||||
'tenancy.storage_drivers.db.connection' => 'central',
|
'tenancy.storage_drivers.db.connection' => 'central',
|
||||||
|
'tenancy.storage_drivers.db.cache_store' => null, // TODO REMOVE THIS BEFORE MERGING TO 2.X
|
||||||
'tenancy.bootstrappers.redis' => \Stancl\Tenancy\TenancyBootstrappers\RedisTenancyBootstrapper::class,
|
'tenancy.bootstrappers.redis' => \Stancl\Tenancy\TenancyBootstrappers\RedisTenancyBootstrapper::class,
|
||||||
'queue.connections.central' => [
|
'queue.connections.central' => [
|
||||||
'driver' => 'sync',
|
'driver' => 'sync',
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue