multi-tenant: Driver [redis_tenancy] is not supported.
Hi,
related to Make cache handling better for tenants #309 I get the issue of getting the error Driver [redis_tenancy] is not supported. Currently I setup my own CI/CD process with local docker (base on php:7.1.8-apache), gitlab and production server. This seems to be related to some autogenerated loading of composer or so as it seems nearly to be random when it works and when not.

- hyn/multi-tenant version: hyn/multi-tenant 5.3.1
- laravel version: laravel/framework v5.6.39
- webserver software and version: apache
- php version: PHP 7.1.8 (cli) (built: Aug 4 2017 18:55:44) ( NTS )
Composer:
"extra": {
"laravel": {
"dont-discover": [
"hyn/multi-tenant",
"spatie/laravel-permission"
]
},
config/app
/*
* Laravel Framework Service Providers...
*/
Illuminate\Auth\AuthServiceProvider::class,
...
// Needs to be before Hyn\Tenancy: https://github.com/hyn/multi-tenant/issues/309#issuecomment-401509408
App\Providers\AppServiceProvider::class,
App\Providers\CacheServiceProvider::class,
/*
* Package Service Providers...
*/
Hyn\Tenancy\Providers\TenancyProvider::class,
Hyn\Tenancy\Providers\WebserverProvider::class,
Spatie\Permission\PermissionServiceProvider::class,
...
/*
* Application Service Providers...
*/
App\Providers\HelperServiceProvider::class,
App\Providers\AuthServiceProvider::class,
App\Providers\ComposerServiceProvider::class,
// App\Providers\BroadcastServiceProvider::class,
App\Providers\EventServiceProvider::class,
App\Providers\RouteServiceProvider::class,
...
config/cache.php
return [
'default' => env('CACHE_DRIVER', 'file'), // in env = redis
'stores' => [
...
'redis' => [
'driver' => 'redis_tenancy', //'redis', 'redis_tenancy'
'connection' => 'default',
],
],
'prefix' => env(
'CACHE_PREFIX',
str_slug(env('APP_NAME', 'laravel'), '_').'_cache'
),
];
CacheServiceProvider:
<?php
namespace App\Providers;
use Illuminate\Support\ServiceProvider;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\DB;
use Illuminate\Cache\RedisStore;
class CacheServiceProvider extends ServiceProvider
{
/**
* Bootstrap services.
*
* @return void
*/
public function boot()
{
// https://github.com/hyn/multi-tenant/issues/309
Cache::extend('redis_tenancy', function ($app) {
if (PHP_SAPI === 'cli') {
$uuid = $app['config']['driver'];
} else {
// ok, this is basically a hack to set the redis cache store
// prefix to the UUID of the current website being called
$fqdn = $_SERVER['SERVER_NAME'];
$uuid = DB::table('hostnames')
->select('websites.uuid')
->join('websites', 'hostnames.website_id', '=', 'websites.id')
->where('fqdn', $fqdn)
->value('uuid');
}
return Cache::repository(new RedisStore(
$app['redis'],
$uuid,
$app['config']['cache.stores.redis.connection']
));
});
}
/**
* Register services.
*
* @return void
*/
public function register()
{
}
}
Any idea what is going wrong here? I think the boot of CacheServiceProvider is not loaded early enough, but how to change this?
While writing this, I located this: https://stackoverflow.com/a/53263914/1597218 The order in the config/app is not used as expected. In my case the order is the following:
- Illuminate
- 3rd Party (which may will use Cache as well)
- Rest of app/config including CacheServiceProvider, Hyn & App\
"According to the Application::registerConfiguredProviders it’s hardcoded to have everything that starts with 'Illuminate' to go to the starting chunk, all others to the end, and Composer Packages go in the middle. "
Even so I add all missing 3rd Party provider into the app/config manually, the Application::registerConfiguredProviders list the providers twice. So one option would be that this package add this provider to the default settings, as vendor files are loaded before app providers or alternativly adding a customIlluminate namespace.
About this issue
- Original URL
- State: closed
- Created 6 years ago
- Comments: 24 (2 by maintainers)
Here are my thoughts why the current documentation is not working. At least sometimes. Making it extremely hard to find out the the solution.
I use Laravel 5.8 and
hyn/tenancy5.4.*When any package calls cache for the first time in the app lifecycle, the
Cacheobject is instantiated. And the directory/prefix of the cache is passed to the cache object.E.g.
./vendor/laravel/framework/src/Illuminate/Cache/FileStore.php./vendor/laravel/framework/src/Illuminate/Cache/RedisStore.php./vendor/laravel/framework/src/Illuminate/Cache/DatabaseStore.phpFor all (or at least many) auto-discovered packages
Cacheis instantiated before the application calls ourApp\Providers\CacheServiceProvider::bootto get ourCache::extendrules. SoCache::extendis ignored and we stay at the cache root for all tenants, we are not able to namespace cache dynamically.Anything calling
Cache::remeberinstantiates theCacheobject as it’s configured inconfig/cache.phpand freezes the prefix/path values inside theCacheobject.Here we can try to run our
App\Providers\CacheServiceProvider::bootand\Cache::extend()before anything instantiates aCacheobject. We may try to disable auto-discover for extensions which instantiateCache, manually add all needed service providers intoconfig/app.php.And this may work until you try to run telescope debug tool, which must be run as early as possible to catch all possible activity. But telescope instantiates
Cacheobject before anything else happens, before we change the instantiate rules inApp\Providers\CacheServiceProvider.Fortunately
Illuminate\Cache\RedisStoreclass (but notIlluminate\Cache\FileStoreorIlluminate\Cache\DatabaseStore) hassetPrefixmethods. So we can redefine the prefix any time, aftertelescopehas already instantiated the cache object.So finally to get cache tetant aware we must:
rediscache driver in.envCacheServiceProviderCacheServiceProviderinconfig/app.phpproviderssection beforeAppServiceProvider. Depending on the way you usehyt/tenancyyou have have some code like$env = app(Environment::class);inAppServiceProvider::bootwhich will instantiate theCacheobject. So better placeCacheServiceProviderbefore.After that cache should become tenant aware.
=====
If you are pretty sure you are not going to use anything like
telescopeinstantiatingCacheobject early, you may try to usefileordatabasecache driver.Here the steps are different:
fileordatabasecache driver in.envCACHE_DRIVER=fileorCACHE_DRIVER=databasehyn/tenancyauto-discoveryUpdate
composer.jsonlike thisand run
composer installto apply changes.config/app.phpregister providers likes this:CacheServiceProvider@luceos Please, have look to this post. Maybe it’s worth to update the docs.
What about this, without changing provider just change prefix?
is there any downsides of this approach?
Hi @luceos , I just implemented the tenant cache with the file driver, following the idea of @zomax above. However, it did not work straight off the bat, and I wonder if this is not the case for the redis version also.
The problem was obtaining the uuid for the new prefix and cache file path. Since the CacheServiceProvider is added before the providers for Tenancy, the tenant database is not defined yet (but it is set in the configuration file), so an error is given.
This was solved by changing the line obtaining the uuid to use specifically the system connection (\DB::connection(‘system’)->):
$uuid = \DB::connection(‘system’)->table(‘hostnames’) ->select(‘websites.uuid’) ->join(‘websites’, ‘hostnames.website_id’, ‘=’, ‘websites.id’) ->where(‘fqdn’, $fqdn) ->value(‘uuid’);
A small detail, but maybe it will help others as well.
Cheers!
PS: Btw, the tenant-aware cache is a huge issue, and if not implemented in Tenancy and left to the user to decide, it should be at least mentioned visibly in the installation documentation. Most tenancy websites will have auth (like in my case), and having user permissions messed up between instances is a nasty security breach (and a pain to debug if not knowing what to look for).
@gruz @luceos @kinsaz @36864
Hi,
I did a nice trick to solve this issue. The above solution not working for me as i have many packages that instantiate the
Cacheobject.The problem is with this commit: https://github.com/laravel/framework/pull/19646 Which make any Service Provider with namespace starting with Illuminate load first, then auto-discovered service providers, then any other providers added to config/app.php.
My solution is:
First change CacheServiceProvider namespace to
Illuminate\UniqueAppNameNow just change cache.prefix value in the boot method
composer.jsonautoload files objectRun
composer dump-autoloadFinally add our CacheServiceProvider service provider to
config/app.phpbefore or afterAppServiceProviderIt should work with any
Cachedrive@kinsaz I didn’t meet such an error. You need to debug it at your own. Sorry, cannot help with issue.