1
0
Fork 0
mirror of https://github.com/archtechx/tenancy.git synced 2025-12-12 17:04:04 +00:00

Add Predis support (#59)

* wip

* Add Predis support

* Enable phpredis extension only for some builds

* Add note about phpredis

* Fix if command

* Revert travis changes & add a version check to predis tests
This commit is contained in:
Samuel Štancl 2019-07-03 14:48:55 +02:00 committed by GitHub
parent e7b5a77a6b
commit 1b6759084f
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
9 changed files with 75 additions and 18 deletions

View file

@ -1,6 +1,6 @@
env: env:
- LARAVEL_VERSION="5.7.*" TESTBENCH_VERSION="~3.7" - LARAVEL_VERSION="5.7.*" TESTBENCH_VERSION="~3.7" REDIS_DRIVER=phpredis
- LARAVEL_VERSION="5.8.*" TESTBENCH_VERSION="~3.8" - LARAVEL_VERSION="5.8.*" TESTBENCH_VERSION="~3.8" REDIS_DRIVER=phpredis
language: php language: php
php: php:
@ -14,7 +14,7 @@ before_install:
- echo "extension = redis.so" >> ~/.phpenv/versions/$(phpenv version-name)/etc/php.ini - echo "extension = redis.so" >> ~/.phpenv/versions/$(phpenv version-name)/etc/php.ini
install: install:
- composer require "laravel/framework:$LARAVEL_VERSION" "orchestra/testbench:$TESTBENCH_VERSION" - travis_retry composer require "laravel/framework:$LARAVEL_VERSION" "orchestra/testbench:$TESTBENCH_VERSION"
- travis_retry composer install --no-interaction - travis_retry composer install --no-interaction
before_script: before_script:

View file

@ -22,7 +22,6 @@ You won't have to change a thing in your application's code.\*
### Requirements ### Requirements
- Laravel 5.7 or 5.8 - Laravel 5.7 or 5.8
- phpredis (predis is not supported)
### Installing the package ### Installing the package
@ -99,6 +98,8 @@ config('tenancy.redis.prefix_base') . $uuid
These changes will only apply for connections listed in `prefixed_connections`. These changes will only apply for connections listed in `prefixed_connections`.
> **Note: *If you want Redis to be multi-tenant, you <u>must</u> use phpredis. Predis does not support prefixes.***
#### `cache` #### `cache`
Cache keys will be tagged with a tag: Cache keys will be tagged with a tag:
@ -328,7 +329,7 @@ The entire application will use a new database connection. The connection will b
Connections listed in the `tenancy.redis.prefixed_connections` config array use a prefix based on the `tenancy.redis.prefix_base` and the tenant UUID. Connections listed in the `tenancy.redis.prefixed_connections` config array use a prefix based on the `tenancy.redis.prefix_base` and the tenant UUID.
**Note: You *must* use phpredis. Predis doesn't support prefixes.** **Note: You *must* use phpredis if you want mutli-tenant Redis. Predis doesn't support prefixes.**
## Cache ## Cache

View file

@ -11,7 +11,8 @@
], ],
"require": { "require": {
"illuminate/support": "5.7.*||5.8.*", "illuminate/support": "5.7.*||5.8.*",
"webpatser/laravel-uuid": "^3.0" "webpatser/laravel-uuid": "^3.0",
"predis/predis": "^1.1"
}, },
"require-dev": { "require-dev": {
"vlucas/phpdotenv": "^2.2||^3.3", "vlucas/phpdotenv": "^2.2||^3.3",

View file

@ -0,0 +1,8 @@
<?php
namespace Stancl\Tenancy\Exceptions;
class PhpRedisNotInstalledException extends \Exception
{
protected $message = 'PhpRedis is not installed. PhpRedis is required for Redis multi-tenancy because Predis does not support prefixes.';
}

View file

@ -71,16 +71,21 @@ class RedisStorageDriver implements StorageDriver
return "tenants:{$hash}"; return "tenants:{$hash}";
}, $uuids); }, $uuids);
// Apparently, the PREFIX is applied to all functions except scan() if (! $hashes) {
// Apparently, the PREFIX is applied to all functions except scan().
// Therefore, if the `tenancy` Redis connection has a prefix set
// (and PhpRedis is used), prepend the prefix to the search.
$redis_prefix = '';
if (config('database.redis.client') === 'phpredis') {
$redis_prefix = $this->redis->getOption($this->redis->client()::OPT_PREFIX); $redis_prefix = $this->redis->getOption($this->redis->client()::OPT_PREFIX);
$hashes = $hashes ?: $this->redis->scan(null, $redis_prefix.'tenants:*'); }
$hashes = array_map(function ($hash) use ($redis_prefix) {
return array_map(function ($tenant) use ($redis_prefix) { // Left strip $redis_prefix from $hash
// Left strip $redis_prefix from $tenant return substr($hash, strlen($redis_prefix));
if (substr($tenant, 0, strlen($redis_prefix)) == $redis_prefix) { }, $this->redis->scan(null, $redis_prefix.'tenants:*'));
$tenant = substr($tenant, strlen($redis_prefix));
} }
return array_map(function ($tenant) {
return $this->redis->hgetall($tenant); return $this->redis->hgetall($tenant);
}, $hashes); }, $hashes);
} }

View file

@ -5,6 +5,8 @@ namespace Stancl\Tenancy\Traits;
use Stancl\Tenancy\CacheManager; use Stancl\Tenancy\CacheManager;
use Illuminate\Support\Facades\Redis; use Illuminate\Support\Facades\Redis;
use Illuminate\Support\Facades\Storage; use Illuminate\Support\Facades\Storage;
use Symfony\Component\Debug\Exception\FatalThrowableError;
use Stancl\Tenancy\Exceptions\PhpRedisNotInstalledException;
trait BootstrapsTenancy trait BootstrapsTenancy
{ {
@ -13,7 +15,9 @@ trait BootstrapsTenancy
public function bootstrap() public function bootstrap()
{ {
$this->switchDatabaseConnection(); $this->switchDatabaseConnection();
if ($this->app['config']['tenancy.redis.tenancy']) {
$this->setPhpRedisPrefix($this->app['config']['tenancy.redis.prefixed_connections']); $this->setPhpRedisPrefix($this->app['config']['tenancy.redis.prefixed_connections']);
}
$this->tagCache(); $this->tagCache();
$this->suffixFilesystemRootPaths(); $this->suffixFilesystemRootPaths();
} }
@ -28,7 +32,11 @@ trait BootstrapsTenancy
foreach ($connections as $connection) { foreach ($connections as $connection) {
$prefix = $this->app['config']['tenancy.redis.prefix_base'] . $this->tenant['uuid']; $prefix = $this->app['config']['tenancy.redis.prefix_base'] . $this->tenant['uuid'];
$client = Redis::connection($connection)->client(); $client = Redis::connection($connection)->client();
try {
$client->setOption($client::OPT_PREFIX, $prefix); $client->setOption($client::OPT_PREFIX, $prefix);
} catch (\Throwable $t) {
throw new PhpRedisNotInstalledException();
}
} }
} }

View file

@ -12,6 +12,7 @@ return [
'suffix' => '', 'suffix' => '',
], ],
'redis' => [ 'redis' => [
'tenancy' => false,
'prefix_base' => 'tenant', 'prefix_base' => 'tenant',
'prefixed_connections' => [ 'prefixed_connections' => [
'default', 'default',

View file

@ -3,6 +3,8 @@
namespace Stancl\Tenancy\Tests; namespace Stancl\Tenancy\Tests;
use Illuminate\Support\Facades\Redis; use Illuminate\Support\Facades\Redis;
use Illuminate\Support\Facades\Config;
use Stancl\Tenancy\Exceptions\PhpRedisNotInstalledException;
class BootstrapsTenancyTest extends TestCase class BootstrapsTenancyTest extends TestCase
{ {
@ -30,6 +32,36 @@ class BootstrapsTenancyTest extends TestCase
} }
} }
/** @test */
public function predis_is_supported()
{
if (app()->version() < 'v5.8.27') {
$this->markTestSkipped();
}
Config::set('database.redis.client', 'predis');
Redis::setDriver('predis');
Config::set('tenancy.redis.tenancy', false);
// assert no exception is thrown from initializing tenancy
$this->assertNotNull($this->initTenancy());
}
/** @test */
public function predis_is_not_supported_without_disabling_redis_multitenancy()
{
if (app()->version() < 'v5.8.27') {
$this->markTestSkipped();
}
Config::set('database.redis.client', 'predis');
Redis::setDriver('predis');
Config::set('tenancy.redis.tenancy', true);
$this->expectException(PhpRedisNotInstalledException::class);
$this->initTenancy();
}
/** @test */ /** @test */
public function filesystem_is_suffixed() public function filesystem_is_suffixed()
{ {

View file

@ -36,7 +36,7 @@ abstract class TestCase extends \Orchestra\Testbench\TestCase
public function initTenancy($domain = 'localhost') public function initTenancy($domain = 'localhost')
{ {
tenancy()->init($domain); return tenancy()->init($domain);
} }
/** /**
@ -72,6 +72,7 @@ abstract class TestCase extends \Orchestra\Testbench\TestCase
'public', 'public',
's3', 's3',
], ],
'tenancy.redis.tenancy' => true,
'tenancy.migrations_directory' => database_path('../migrations'), 'tenancy.migrations_directory' => database_path('../migrations'),
]); ]);
} }