archtechx / tenancy

Automatic multi-tenancy for Laravel. No code changes needed.
https://tenancyforlaravel.com
MIT License
3.67k stars 432 forks source link

Issue with Invalidating Tenant Cache when Redis Data Separation is Enabled #1230

Closed waqleh closed 4 months ago

waqleh commented 5 months ago

Bug description

According to the documentation, updating and saving a Tenant Model's attributes should invalidate the cache entry for this model when DomainTenantResolver::$shouldCache is set to true. This expected behavior does not occur when Redis Data Separation is enabled. However, the cache invalidation works correctly if Redis Data Separation is disabled.

Temporary Workaround: As a temporary workaround, I created a TenantObserver class that invalidates the cache by using the Redis FLUSHDB command. This approach ensures the cache is cleared; however, it has a significant drawback as it invalidates the cache for all tenants, which is not ideal

class TenantObserver
{
    public function updated(Tenant $model)
    {
         Illuminate\Support\Facades\Redis::connection('cache')->command('FLUSHDB');
    }
}
Tenant::observe(TenantObserver::class);

Steps to reproduce

  1. Enable Redis Data Separation (cache Redis DB connection): tenancy config file:
    ...
    'redis' => [
        'prefix_base' => 'tenant',
        'prefixed_connection' => [
            //'default',
            'cache', // enabled it here
            //'gracenote',
        ],
    ],
    ...

    database config file:

...
    'redis' => [
        'client' => env('REDIS_CLIENT', 'phpredis'),
        'options' => [
            'cluster' => env('REDIS_CLUSTER', 'redis'),
            'prefix' => env('REDIS_PREFIX', Str::slug(env('APP_NAME', 'laravel'), '_').'_database_'),
        ],

        'default' => [
            'scheme' => 'tcp',
//            'url' => env('REDIS_URL'),
            'host' => env('REDIS_HOST', '127.0.0.1'),
//            'username' => env('REDIS_USERNAME'),
            'password' => env('REDIS_PASSWORD'),
            'port' => env('REDIS_PORT', '6379'),
            'database' => env('REDIS_DB', '0'),
        ],

        'cache' => [
//            'url' => env('REDIS_URL'),
            'host' => env('REDIS_HOST', '127.0.0.1'),
//            'username' => env('REDIS_USERNAME'),
            'password' => env('REDIS_PASSWORD'),
            'port' => env('REDIS_PORT', '6379'),
            'database' => '10',
        ],

    ],
...
  1. Set DomainTenantResolver::$shouldCache to true. in \App\Tenancy\TenancyServiceProvider::boot:

    // TenancyServiceProvider::boot()
    
    use Stancl\Tenancy\Resolvers;
    
    DomainTenantResolver::$shouldCache = true;
    DomainTenantResolver::$cacheStore = 'redis';
  2. Update and save a Tenant Model's attributes.
  3. Observe that the cache entry for the Tenant Model is not invalidated. they cache that fails to get invalidated is the one for DomainTenantResolver, I noticed that it does not start with the tenant id prefix, so instead of they cache key starting with tenantXapp_cache:.. it starts with app_database_app_cache:...
  4. Disable Redis Data Separation by commenting out in the config tenancy.php redis.prefixed_connection.cache
  5. Repeat steps 3-4 and observe that the cache entry is correctly invalidated.

Expected behavior

When Redis Data Separation is enabled, updating and saving a Tenant Model's attributes should invalidate the cache entry for the model, similar to when Redis Data Separation is disabled.

Laravel version

9.5.2.16

stancl/tenancy version

3.8.4

stancl commented 4 months ago

The resolver cache is what's meant to be invalidated. Is there a bug in that?

The "tenant's cache" as in data stored in the cache from within the tenant app isn't meant to be getting invalidated, that wouldn't make sense.