├── .gitignore
├── .travis.yml
├── tests
├── BaseTest.php
└── DriverTest.php
├── phpunit.xml
├── composer.json
├── src
├── Indatus
│ └── LaravelPSRedis
│ │ └── LaravelPSRedisServiceProvider.php
└── lib
│ └── Driver.php
├── LICENSE
└── README.md
/.gitignore:
--------------------------------------------------------------------------------
1 | /vendor
2 | composer.phar
3 | composer.lock
4 | .DS_Store
5 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: php
2 |
3 | php:
4 | - 5.4
5 | - 5.5
6 | - 5.6
7 | - hhvm
8 |
9 | before_script:
10 | - travis_retry composer self-update
11 | - travis_retry composer install --prefer-source --no-interaction --dev
12 |
13 | script: phpunit
14 |
--------------------------------------------------------------------------------
/tests/BaseTest.php:
--------------------------------------------------------------------------------
1 |
11 | */
12 | abstract class BaseTest extends TestCase
13 | {
14 | public function setUp()
15 | {
16 | parent::setUp();
17 | }
18 | /**
19 | * Create the application
20 | *
21 | * @return mixed
22 | */
23 | public function createApplication()
24 | {
25 | $unitTesting = true;
26 | $testEnvironment = 'testing';
27 |
28 | return require __DIR__ . '/../../../../bootstrap/start.php';
29 | }
30 |
31 | public function tearDown()
32 | {
33 | m::close();
34 | }
35 | }
--------------------------------------------------------------------------------
/phpunit.xml:
--------------------------------------------------------------------------------
1 |
2 |
13 |
14 |
15 | ./tests/
16 |
17 |
18 |
19 |
20 |
21 | ./vendor
22 |
23 |
24 | ./src/lib
25 |
26 |
27 |
28 |
--------------------------------------------------------------------------------
/composer.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "indatus/laravel-ps-redis",
3 | "description": "A simple sentinel/redis driver wrapper for laravel.",
4 | "authors": [
5 | {
6 | "name": "Damien Russell",
7 | "email": "drussell@indatus.com"
8 | }
9 | ],
10 | "require": {
11 | "php": ">=5.6.0",
12 | "illuminate/support": "5.*",
13 | "jamescauwelier/psredis": "~1.1"
14 | },
15 | "require-dev": {
16 | "laravel/framework": "5.*",
17 | "phpunit/phpunit": "4.*",
18 | "mockery/mockery": "0.9.*"
19 | },
20 | "autoload": {
21 | "classmap": [
22 | "src/lib"
23 | ],
24 | "psr-0": {
25 | "Indatus\\LaravelPSRedis\\": "src/"
26 | }
27 | },
28 | "psr-4": {
29 | "": "src/lib"
30 | },
31 | "minimum-stability": "stable",
32 | "license": "MIT"
33 | }
34 |
--------------------------------------------------------------------------------
/src/Indatus/LaravelPSRedis/LaravelPSRedisServiceProvider.php:
--------------------------------------------------------------------------------
1 | app->singleton(
24 | 'redis',
25 | function () {
26 | $driver = new Driver();
27 | return new RedisManager('predis', $driver->getConfig());
28 | }
29 | );
30 | }
31 |
32 | /**
33 | * Get the services provided by the provider.
34 | *
35 | * @return array
36 | */
37 | public function provides()
38 | {
39 | return ['redis'];
40 | }
41 |
42 | }
43 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2015 Indatus
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
23 |
--------------------------------------------------------------------------------
/src/lib/Driver.php:
--------------------------------------------------------------------------------
1 |
14 | */
15 | class Driver
16 | {
17 | /** @var MasterDiscovery $masterDiscovery The mechanism for determining the master */
18 | protected $masterDiscovery;
19 |
20 | /** @var HAClient $HAClient is the highly available client which handles the auto-failover. */
21 | protected $HAClient;
22 |
23 |
24 | /**
25 | * Constructor
26 | *
27 | * @return void
28 | */
29 | public function __construct()
30 | {
31 | $this->setUpMasterDiscovery();
32 |
33 | $this->addSentinels();
34 |
35 | $this->HAClient = new HAClient(
36 | $this->masterDiscovery
37 | );
38 | }
39 |
40 |
41 | /**
42 | * Get the config values for the redis database.
43 | *
44 | * @return array
45 | */
46 | public function getConfig()
47 | {
48 | return [
49 | 'cluster' => Config::get('database.redis.cluster'),
50 | 'default' => [
51 | 'host' => $this->HAClient->getIpAddress(),
52 | 'port' => $this->HAClient->getPort(),
53 | 'password' => Config::get('database.redis.password', null),
54 | 'database' => Config::get('database.redis.database', 0),
55 | ]
56 | ];
57 | }
58 |
59 | public function getBackOffStrategy()
60 | {
61 | /** @var array $backOffConfig */
62 | $backOffConfig = Config::get('database.redis.backoff-strategy');
63 |
64 | /** @var Incremental $incrementalBackOff */
65 | $incrementalBackOff = new Incremental(
66 | $backOffConfig['wait-time'],
67 | $backOffConfig['increment']
68 | );
69 |
70 | $incrementalBackOff->setMaxAttempts($backOffConfig['max-attempts']);
71 |
72 | return $incrementalBackOff;
73 | }
74 |
75 | public function setUpMasterDiscovery()
76 | {
77 | $this->masterDiscovery = new MasterDiscovery(Config::get('database.redis.nodeSetName'));
78 |
79 | $this->masterDiscovery->setBackoffStrategy($this->getBackOffStrategy());
80 | }
81 |
82 | public function addSentinels()
83 | {
84 | $clients = Config::get('database.redis.masters');
85 | foreach($clients as $client) {
86 | $sentinel = new PSRedisClient($client['host'], $client['port']);
87 |
88 | $this->masterDiscovery->addSentinel($sentinel);
89 | }
90 | }
91 | }
92 |
--------------------------------------------------------------------------------
/tests/DriverTest.php:
--------------------------------------------------------------------------------
1 |
13 | */
14 | class DriverTest extends BaseTest
15 | {
16 | /** @var \Indatus\LaravelPSRedis\Driver */
17 | protected $driverUnderTest;
18 |
19 | /** @var array */
20 | protected $masters;
21 |
22 | /** @var \Mockery\MockInterface */
23 | protected $masterDiscovery;
24 |
25 | /** @var \Mockery\MockInterface */
26 | protected $HAClient;
27 |
28 | /** @var \Mockery\MockInterface */
29 | protected $PSRedisClient;
30 |
31 | /** @var array */
32 | protected $backOffStrategy;
33 |
34 | /** @var \Mockery\MockInterface */
35 | protected $incremental;
36 |
37 | public function setUp()
38 | {
39 | parent::setUp();
40 |
41 | // some of the classes from the PSRedis package implement the
42 | // `__call()` methods and define a default value for the method arguments
43 | // php doesn't like while using the `E_STRICT` error level
44 | if(defined('E_STRICT')) {
45 | error_reporting('E_ALL ^ E_STRICT');
46 | }
47 |
48 | $this->masters = [
49 | [
50 | 'host' => 'host1.domain.com',
51 | 'port' => '222222'
52 | ],
53 | [
54 | 'host' => 'host2.domain.com',
55 | 'port' => '222222'
56 | ]
57 | ];
58 |
59 | $this->backOffStrategy = [
60 | 'max-attempts' => 10, // the maximum-number of attempt possible to find master
61 | 'wait-time' => 500, // miliseconds to wait for the next attempt
62 | 'increment' => 1.5, // multiplier used to increment the back off time on each try
63 | ];
64 |
65 | $this->masterDiscovery = m::mock('PSRedis\MasterDiscovery')->makePartial();
66 | $this->app->instance('PSRedis\MasterDiscovery', $this->masterDiscovery);
67 |
68 | $this->HAClient = m::mock('PSRedis\HAClient')->makePartial();
69 | $this->app->instance('PSRedis\HAClient', $this->HAClient);
70 |
71 | $this->PSRedisClient = m::mock('PSRedis\Client');
72 | $this->app->instance('PSRedis\Client', $this->PSRedisClient);
73 | }
74 |
75 | /**
76 | * Can we instantiate the class?
77 | *
78 | * @test
79 | */
80 | public function it_instantiates()
81 | {
82 | Config::shouldReceive('get')->once()->with('database.redis.nodeSetName')->andReturn('node-set');
83 | Config::shouldReceive('get')->once()->with('database.redis.masters')->andReturn($this->masters);
84 | Config::shouldReceive('get')->with('database.redis.backoff-strategy')->andReturn($this->backOffStrategy);
85 |
86 | $this->driverUnderTest = new Driver();
87 |
88 | $this->assertInstanceOf('Indatus\LaravelPSRedis\Driver', $this->driverUnderTest);
89 | }
90 |
91 | /**
92 | * Can it get the proper config array?
93 | *
94 | * @test
95 | */
96 | public function it_gets_config_values()
97 | {
98 | $expectedConfig = [
99 | 'cluster' => false,
100 | 'default' => [
101 | 'host' => $this->masters[0]['host'],
102 | 'port' => $this->masters[0]['port']
103 | ]
104 | ];
105 |
106 | Config::shouldReceive('get')->once()->with('database.redis.nodeSetName')->andReturn('node-set');
107 | Config::shouldReceive('get')->once()->with('database.redis.masters')->andReturn($this->masters);
108 | Config::shouldReceive('get')->with('database.redis.backoff-strategy')->andReturn($this->backOffStrategy);
109 | Config::shouldReceive('get')->with('database.redis.cluster')->andReturn(false);
110 |
111 | $this->HAClient->shouldReceive('getIpAddress')->once()->andReturn($this->masters[0]['host']);
112 | $this->HAClient->shouldReceive('getPort')->once()->andReturn($this->masters[0]['port']);
113 |
114 | $this->driverUnderTest = new Driver();
115 |
116 | $configUnderTest = $this->driverUnderTest->getConfig();
117 |
118 | $this->assertEquals($expectedConfig, $configUnderTest);
119 | }
120 | }
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Laravel-PSRedis
2 |
3 | > DEPRECATED, see https://github.com/laravel/framework/pull/18850 for built-in sentinel support
4 |
5 | A simple sentinel/redis driver wrapper for laravel.
6 |
7 | The default laravel redis driver supports redis clusters, however, it does not support high availability with redis, which is where Laravel-PSRedis comes to the rescue.
8 |
9 | With Laravel-PSRedis you'll get all the laravel redis magic that you aleady have such
10 | as `Redis::set()` and `Redis::get()`, and even session, queue, and cache support using redis,
11 | you'll just be able to leverage High Avaliability redis instances instead of a simple cluster.
12 |
13 | We do this by asking your [Redis Sentinels](http://redis.io/topics/sentinel) the location of your master before creating our Redis bindings in the IOC Container. By doing this we ensure anytime your app has a connection to your redis instance, that connection is to master.
14 |
15 | ## README Contents
16 |
17 | * [Installation](#installation)
18 | * [Laravel 5 Installation](#installation-for-Laravel-5)
19 | * [Laravel 4 Installation](#installation-for-Laravel-4)
20 | * [Configuration](#configuration)
21 | * [Service Provider](#the-service-provider)
22 | * [Contributing](#contributing);
23 | * [Testing](#testing)
24 | * [License](#license)
25 |
26 |
27 | ## Installation
28 |
29 |
30 | ### Installation for Laravel 5
31 |
32 | You can install Laravel-PSRedis easily with composer.
33 |
34 | ```
35 | "require": {
36 | "indatus/laravel-ps-redis": "^1.2",
37 | },
38 | ```
39 |
40 |
41 | ### Installation for Laravel 4
42 |
43 | If you're using Laravel 4 then the installation is slightly different. Laravel-PSRedis depends on `sparkcentral/psredis` which requires `'predis/predis': '>=1.0'` in it's stable release. I've taken the liberty of forking `sparkcentral/psredis` and rolling back `predis/predis` to `0.8.7`
44 | which is required by laravel 4. To utilize this fork simply require both `indatus\larave-ps-redis` and `sparkcentral/psredis` in your composer.json. And add a repository to point to the fork. Like so:
45 |
46 | ```
47 | "repositories": [
48 | {
49 | "type": "vcs",
50 | "url": "https://github.com/Olofguard/PSRedis"
51 | }
52 | ],
53 | "require": {
54 | "indatus/laravel-ps-redis": "dev-master",
55 | "sparkcentral/psredis": "dev-master"
56 | },
57 | ```
58 |
59 | This will help composer form an installable set of packages, otherwise composer complains about laravel needing `predis/predis` at version `0.8.7` while `sparkcentral/psredis` is installing `1.0.*`.
60 |
61 |
62 | ## Configuration
63 |
64 | Next, just fill in your sentinel/redis server info in the `app/config/database.php` config files that already exist in your application.
65 |
66 | You may already have some default laravel config values in place in your database config file that looks like this.
67 |
68 | ```
69 | /*
70 | |--------------------------------------------------------------------------
71 | | Redis Databases
72 | |--------------------------------------------------------------------------
73 | |
74 | | Redis is an open source, fast, and advanced key-value store that also
75 | | provides a richer set of commands than a typical key-value systems
76 | | such as APC or Memcached. Laravel makes it easy to dig right in.
77 | |
78 | */
79 | 'redis' => [
80 | 'cluster' => false,
81 | 'default' => [
82 | 'host' => '127.0.0.1',
83 | 'port' => 6379,
84 | 'database' => 0,
85 | ],
86 | ],
87 | ```
88 |
89 | Just overwrite those with the values below and fill in your server info.
90 |
91 | ```
92 | 'redis' => [
93 |
94 | /** the name of the redis node set */
95 | 'nodeSetName' => 'sentinel-node-set',
96 |
97 | 'cluster' => false,
98 |
99 | /** Array of sentinels */
100 | 'masters' => [
101 | [
102 | 'host' => 'sentinel-instance.domain.com',
103 | 'port' => '26379',
104 | ],
105 | [
106 | 'host' => 'sentinel-instance.domain.com',
107 | 'port' => '26379',
108 | ]
109 | ],
110 |
111 | /** how long to wait and try again if we fail to connect to master */
112 | 'backoff-strategy' => [
113 | 'max-attempts' => 10, // the maximum-number of attempt possible to find master
114 | 'wait-time' => 500, // miliseconds to wait for the next attempt
115 | 'increment' => 1.5, // multiplier used to increment the back off time on each try
116 | ]
117 | ];
118 | ```
119 |
120 |
121 | ### The Service Provider
122 |
123 | Finally, you just need to add the service provider to the providers array in `app.php` and comment or remove the
124 | redis service provider.
125 |
126 | ```
127 | /*
128 | |--------------------------------------------------------------------------
129 | | Autoloaded Service Providers
130 | |--------------------------------------------------------------------------
131 | |
132 | */
133 | 'providers' => [
134 | ...
135 | // 'Illuminate\Redis\RedisServiceProvider', # comment this out
136 | 'Indatus\LaravelPSRedis\LaravelPSRedisServiceProvider' # add this
137 | ],
138 | ```
139 |
140 | > Note: you may have to `composer dump-autoload` after adding the service provider
141 |
142 |
143 | ## Contributing
144 |
145 | 1. Fork it
146 | 2. Create your feature branch (git checkout -b my-new-feature)
147 | 3. Commit your changes (git commit -m 'Added some feature')
148 | 4. Push to the branch (git push origin my-new-feature)
149 | 5. Create new Pull Request
150 |
151 |
152 | ## Testing
153 |
154 | Feel free to clone the repo and run the unit tests locally.
155 |
156 | ```
157 | ./vendor/bin/phpunit -c ./phpunit.xml
158 | ```
159 |
160 |
161 | ## License
162 | [The MIT License (MIT)](https://github.com/Indatus/laravel-PSRedis/blob/master/LICENSE)
163 |
--------------------------------------------------------------------------------