mirror of
https://github.com/archtechx/tenancy.git
synced 2026-06-20 22:54:05 +00:00
Revert "Merge branch 'fix-cache-invalidation' into boilerplate-dev"
This reverts commit32fa4cca20, reversing changes made to2dfbbef0f3.
This commit is contained in:
parent
32fa4cca20
commit
ba4c80c615
9 changed files with 7 additions and 147 deletions
|
|
@ -80,8 +80,8 @@ services:
|
||||||
mssql:
|
mssql:
|
||||||
image: mcr.microsoft.com/mssql/server:2022-latest
|
image: mcr.microsoft.com/mssql/server:2022-latest
|
||||||
environment:
|
environment:
|
||||||
ACCEPT_EULA: "Y"
|
- ACCEPT_EULA=Y
|
||||||
SA_PASSWORD: "P@ssword" # must be the same as TENANCY_TEST_SQLSRV_PASSWORD
|
- SA_PASSWORD=P@ssword # must be the same as TENANCY_TEST_SQLSRV_PASSWORD
|
||||||
healthcheck: # https://github.com/Microsoft/mssql-docker/issues/133#issuecomment-1995615432
|
healthcheck: # https://github.com/Microsoft/mssql-docker/issues/133#issuecomment-1995615432
|
||||||
test: timeout 2 bash -c 'cat < /dev/null > /dev/tcp/127.0.0.1/1433'
|
test: timeout 2 bash -c 'cat < /dev/null > /dev/tcp/127.0.0.1/1433'
|
||||||
interval: 10s
|
interval: 10s
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,7 @@ class PendingScope implements Scope
|
||||||
/**
|
/**
|
||||||
* Apply the scope to a given Eloquent query builder.
|
* Apply the scope to a given Eloquent query builder.
|
||||||
*
|
*
|
||||||
* @param Builder<covariant Model> $builder
|
* @param Builder<Model> $builder
|
||||||
*
|
*
|
||||||
* @return void
|
* @return void
|
||||||
*/
|
*/
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,7 @@ use Illuminate\Database\Eloquent\Scope;
|
||||||
class ParentModelScope implements Scope
|
class ParentModelScope implements Scope
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* @param Builder<covariant Model> $builder
|
* @param Builder<Model> $builder
|
||||||
*/
|
*/
|
||||||
public function apply(Builder $builder, Model $model): void
|
public function apply(Builder $builder, Model $model): void
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,7 @@ use Stancl\Tenancy\Tenancy;
|
||||||
class TenantScope implements Scope
|
class TenantScope implements Scope
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* @param Builder<covariant Model> $builder
|
* @param Builder<Model> $builder
|
||||||
*/
|
*/
|
||||||
public function apply(Builder $builder, Model $model)
|
public function apply(Builder $builder, Model $model)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -22,34 +22,12 @@ class DeleteDatabase implements ShouldQueue
|
||||||
protected TenantWithDatabase&Model $tenant,
|
protected TenantWithDatabase&Model $tenant,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
/** Skip database deletion if the create_database internal attribute is false. */
|
|
||||||
public static bool $skipWhenCreateDatabaseIsFalse = true;
|
|
||||||
|
|
||||||
/** Ignore exceptions thrown during database deletion and continue execution. */
|
|
||||||
public static bool $ignoreFailures = false;
|
|
||||||
|
|
||||||
public function handle(): void
|
public function handle(): void
|
||||||
{
|
{
|
||||||
if (static::$skipWhenCreateDatabaseIsFalse && $this->tenant->getInternal('create_database') === false) {
|
|
||||||
// If database creation was skipped, we presume deletion should also be skipped.
|
|
||||||
// To avoid this skip, either unset the `create_database` attribute (or make it true), or
|
|
||||||
// set the $skipWhenCreateDatabaseIsFalse static property to false.
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
event(new DeletingDatabase($this->tenant));
|
event(new DeletingDatabase($this->tenant));
|
||||||
|
|
||||||
$deleted = false;
|
|
||||||
|
|
||||||
try {
|
|
||||||
$this->tenant->database()->manager()->deleteDatabase($this->tenant);
|
$this->tenant->database()->manager()->deleteDatabase($this->tenant);
|
||||||
$deleted = true;
|
|
||||||
} catch (\Throwable $e) {
|
|
||||||
if (! static::$ignoreFailures) {
|
|
||||||
throw $e;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($deleted) event(new DatabaseDeleted($this->tenant));
|
event(new DatabaseDeleted($this->tenant));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -6,11 +6,9 @@ namespace Stancl\Tenancy;
|
||||||
|
|
||||||
use Closure;
|
use Closure;
|
||||||
use Illuminate\Cache\CacheManager;
|
use Illuminate\Cache\CacheManager;
|
||||||
use Illuminate\Cache\DatabaseStore;
|
|
||||||
use Illuminate\Contracts\Container\Container;
|
use Illuminate\Contracts\Container\Container;
|
||||||
use Illuminate\Database\Console\Migrations\FreshCommand;
|
use Illuminate\Database\Console\Migrations\FreshCommand;
|
||||||
use Illuminate\Routing\Events\RouteMatched;
|
use Illuminate\Routing\Events\RouteMatched;
|
||||||
use Illuminate\Support\Facades\DB;
|
|
||||||
use Illuminate\Support\Facades\Event;
|
use Illuminate\Support\Facades\Event;
|
||||||
use Illuminate\Support\Facades\Route;
|
use Illuminate\Support\Facades\Route;
|
||||||
use Illuminate\Support\ServiceProvider;
|
use Illuminate\Support\ServiceProvider;
|
||||||
|
|
@ -96,15 +94,6 @@ class TenancyServiceProvider extends ServiceProvider
|
||||||
// using the callback below. It is set by DatabaseCacheBootstrapper.
|
// using the callback below. It is set by DatabaseCacheBootstrapper.
|
||||||
$manager = new CacheManager($app);
|
$manager = new CacheManager($app);
|
||||||
|
|
||||||
// Make globalCache use either the configured non-null connection,
|
|
||||||
// or fall back to the central connection.
|
|
||||||
$this->makeDatabaseCacheStoresCentral($manager);
|
|
||||||
|
|
||||||
// If a bootstrapper (like DatabaseCacheBootstrapper) makes the
|
|
||||||
// cache connection tenant explicitly, the makeDatabaseCacheStoresCentral()
|
|
||||||
// call ends up setting the tenant connection rather than the central one,
|
|
||||||
// and the $adjustCacheManagerUsing callback is needed to
|
|
||||||
// make globalCache use the central connection.
|
|
||||||
if (static::$adjustCacheManagerUsing !== null) {
|
if (static::$adjustCacheManagerUsing !== null) {
|
||||||
(static::$adjustCacheManagerUsing)($manager);
|
(static::$adjustCacheManagerUsing)($manager);
|
||||||
}
|
}
|
||||||
|
|
@ -113,34 +102,6 @@ class TenancyServiceProvider extends ServiceProvider
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Ensure globalCache uses the central connection for database cache stores.
|
|
||||||
*
|
|
||||||
* A freshly built CacheManager creates database stores using the current default connection, which
|
|
||||||
* DatabaseTenancyBootstrapper switches to the tenant connection. Since global cache should always be
|
|
||||||
* central, reset those stores back to their configured connection, falling back to the central one.
|
|
||||||
*/
|
|
||||||
protected function makeDatabaseCacheStoresCentral(CacheManager $manager): void
|
|
||||||
{
|
|
||||||
$centralConnection = $this->app['config']['tenancy.database.central_connection'];
|
|
||||||
|
|
||||||
foreach ($this->app['config']['cache.stores'] ?? [] as $name => $store) {
|
|
||||||
$notAValidDatabaseStore = ! is_array($store) || ($store['driver'] ?? null) !== 'database';
|
|
||||||
|
|
||||||
if ($notAValidDatabaseStore) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** @var DatabaseStore $databaseStore */
|
|
||||||
$databaseStore = $manager->store($name)->getStore();
|
|
||||||
|
|
||||||
// If $store['connection'] is null, it defaults to the default DB connection (which may be tenant).
|
|
||||||
// Fall back to the central connection to keep the global cache central.
|
|
||||||
$databaseStore->setConnection(DB::connection($store['connection'] ?? $centralConnection));
|
|
||||||
$databaseStore->setLockConnection(DB::connection($store['lock_connection'] ?? $store['connection'] ?? $centralConnection));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Bootstrap services. */
|
/* Bootstrap services. */
|
||||||
public function boot(): void
|
public function boot(): void
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -165,7 +165,6 @@ test('cache is invalidated when tenant is updated from within the tenant context
|
||||||
['redis', [CacheTenancyBootstrapper::class]],
|
['redis', [CacheTenancyBootstrapper::class]],
|
||||||
['redis', [CacheTagsBootstrapper::class]],
|
['redis', [CacheTagsBootstrapper::class]],
|
||||||
['database', [DatabaseTenancyBootstrapper::class, DatabaseCacheBootstrapper::class]],
|
['database', [DatabaseTenancyBootstrapper::class, DatabaseCacheBootstrapper::class]],
|
||||||
['database', [DatabaseTenancyBootstrapper::class, CacheTenancyBootstrapper::class]],
|
|
||||||
]);
|
]);
|
||||||
|
|
||||||
test('cache is invalidated when the tenant is deleted', function (string $resolver, bool $configureTenantModelColumn) {
|
test('cache is invalidated when the tenant is deleted', function (string $resolver, bool $configureTenantModelColumn) {
|
||||||
|
|
|
||||||
|
|
@ -2,27 +2,17 @@
|
||||||
|
|
||||||
declare(strict_types=1);
|
declare(strict_types=1);
|
||||||
|
|
||||||
use Illuminate\Database\QueryException;
|
|
||||||
use Illuminate\Support\Facades\Event;
|
use Illuminate\Support\Facades\Event;
|
||||||
use Illuminate\Support\Facades\Schema;
|
use Illuminate\Support\Facades\Schema;
|
||||||
use Stancl\JobPipeline\JobPipeline;
|
use Stancl\JobPipeline\JobPipeline;
|
||||||
use Stancl\Tenancy\Events\TenantCreated;
|
use Stancl\Tenancy\Events\TenantCreated;
|
||||||
use Stancl\Tenancy\Events\TenantDeleted;
|
|
||||||
use Stancl\Tenancy\Jobs\CreateDatabase;
|
use Stancl\Tenancy\Jobs\CreateDatabase;
|
||||||
use Stancl\Tenancy\Jobs\DeleteDatabase;
|
|
||||||
use Stancl\Tenancy\Jobs\MigrateDatabase;
|
use Stancl\Tenancy\Jobs\MigrateDatabase;
|
||||||
use Stancl\Tenancy\Jobs\SeedDatabase;
|
use Stancl\Tenancy\Jobs\SeedDatabase;
|
||||||
use Stancl\Tenancy\Tests\Etc\Tenant;
|
use Stancl\Tenancy\Tests\Etc\Tenant;
|
||||||
use Illuminate\Foundation\Auth\User as Authenticable;
|
use Illuminate\Foundation\Auth\User as Authenticable;
|
||||||
use Stancl\Tenancy\Tests\Etc\TestSeeder;
|
use Stancl\Tenancy\Tests\Etc\TestSeeder;
|
||||||
|
|
||||||
beforeEach($cleanup = function () {
|
|
||||||
DeleteDatabase::$ignoreFailures = false;
|
|
||||||
DeleteDatabase::$skipWhenCreateDatabaseIsFalse = true;
|
|
||||||
});
|
|
||||||
|
|
||||||
afterEach($cleanup);
|
|
||||||
|
|
||||||
test('database can be created after tenant creation', function () {
|
test('database can be created after tenant creation', function () {
|
||||||
config(['tenancy.database.template_tenant_connection' => 'mysql']);
|
config(['tenancy.database.template_tenant_connection' => 'mysql']);
|
||||||
|
|
||||||
|
|
@ -92,73 +82,6 @@ test('custom job can be added to the pipeline', function () {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
test('database can be deleted after tenant deletion', function () {
|
|
||||||
Event::listen(TenantCreated::class, JobPipeline::make([CreateDatabase::class])->send(function (TenantCreated $event) {
|
|
||||||
return $event->tenant;
|
|
||||||
})->toListener());
|
|
||||||
|
|
||||||
Event::listen(TenantDeleted::class, JobPipeline::make([DeleteDatabase::class])->send(function (TenantDeleted $event) {
|
|
||||||
return $event->tenant;
|
|
||||||
})->toListener());
|
|
||||||
|
|
||||||
$tenant = Tenant::create();
|
|
||||||
$manager = $tenant->database()->manager();
|
|
||||||
|
|
||||||
expect($manager->databaseExists($tenant->database()->getName()))->toBeTrue();
|
|
||||||
|
|
||||||
$tenant->delete();
|
|
||||||
|
|
||||||
expect($manager->databaseExists($tenant->database()->getName()))->toBeFalse();
|
|
||||||
});
|
|
||||||
|
|
||||||
test('database deletion is skipped when create_database is false', function (bool $skipWhenCreateDatabaseIsFalse) {
|
|
||||||
Event::listen(TenantDeleted::class, JobPipeline::make([DeleteDatabase::class])->send(function (TenantDeleted $event) {
|
|
||||||
return $event->tenant;
|
|
||||||
})->toListener());
|
|
||||||
|
|
||||||
// create_database=false means no DB is created (e.g. tenant uses a pre-existing DB)
|
|
||||||
// On deletion, DeleteDatabase should skip rather than attempting DROP DATABASE on a non-existent DB
|
|
||||||
$tenant = Tenant::create(['tenancy_create_database' => false, 'tenancy_db_name' => 'non_existing_db']);
|
|
||||||
|
|
||||||
$manager = $tenant->database()->manager();
|
|
||||||
expect($manager->databaseExists($tenant->database()->getName()))->toBeFalse();
|
|
||||||
|
|
||||||
DeleteDatabase::$skipWhenCreateDatabaseIsFalse = $skipWhenCreateDatabaseIsFalse;
|
|
||||||
|
|
||||||
if ($skipWhenCreateDatabaseIsFalse) {
|
|
||||||
$tenant->delete(); // no exception
|
|
||||||
} else {
|
|
||||||
expect(fn () => $tenant->delete())->toThrow(QueryException::class, "database doesn't exist");
|
|
||||||
}
|
|
||||||
|
|
||||||
expect($manager->databaseExists($tenant->database()->getName()))->toBeFalse();
|
|
||||||
})->with([true, false]);
|
|
||||||
|
|
||||||
test('database deletion failure is ignored when ignoreFailures is true', function (bool $ignoreFailures) {
|
|
||||||
Event::listen(TenantCreated::class, JobPipeline::make([CreateDatabase::class])->send(function (TenantCreated $event) {
|
|
||||||
return $event->tenant;
|
|
||||||
})->toListener());
|
|
||||||
|
|
||||||
Event::listen(TenantDeleted::class, JobPipeline::make([DeleteDatabase::class])->send(function (TenantDeleted $event) {
|
|
||||||
return $event->tenant;
|
|
||||||
})->toListener());
|
|
||||||
|
|
||||||
DeleteDatabase::$ignoreFailures = $ignoreFailures;
|
|
||||||
|
|
||||||
$tenant = Tenant::create();
|
|
||||||
$manager = $tenant->database()->manager();
|
|
||||||
expect($manager->databaseExists($tenant->database()->getName()))->toBeTrue();
|
|
||||||
|
|
||||||
$manager->deleteDatabase($tenant); // manually delete so the job fails
|
|
||||||
expect($manager->databaseExists($tenant->database()->getName()))->toBeFalse();
|
|
||||||
|
|
||||||
if ($ignoreFailures) {
|
|
||||||
$tenant->delete(); // no exception
|
|
||||||
} else {
|
|
||||||
expect(fn () => $tenant->delete())->toThrow(QueryException::class, "database doesn't exist");
|
|
||||||
}
|
|
||||||
})->with([true, false]);
|
|
||||||
|
|
||||||
class User extends Authenticable
|
class User extends Authenticable
|
||||||
{
|
{
|
||||||
protected $guarded = [];
|
protected $guarded = [];
|
||||||
|
|
|
||||||
|
|
@ -165,7 +165,6 @@ test('global cache is always central', function (string $store, array $bootstrap
|
||||||
['redis', [CacheTagsBootstrapper::class]],
|
['redis', [CacheTagsBootstrapper::class]],
|
||||||
['redis', [CacheTenancyBootstrapper::class]],
|
['redis', [CacheTenancyBootstrapper::class]],
|
||||||
['database', [DatabaseTenancyBootstrapper::class, DatabaseCacheBootstrapper::class]],
|
['database', [DatabaseTenancyBootstrapper::class, DatabaseCacheBootstrapper::class]],
|
||||||
['database', [DatabaseTenancyBootstrapper::class, CacheTenancyBootstrapper::class]],
|
|
||||||
])->with([
|
])->with([
|
||||||
'helper',
|
'helper',
|
||||||
'facade',
|
'facade',
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue