In my situation, I face a problem with redis, our redis service(RDS) is unstable with unknown problem, but I need to solve consistency service and zero-downtime, so on my first solve problem that I do switch service in my application.

Here is my thinking steps:

  1. Redis service needs to bind container by singlton
  2. Trying to connect Master redis, if it’s failed, rebinding second redis service.
  3. Rebinding facade class to new redis service.
  4. No middleware for now, it should be in service provider.
//database.php
'redis' => [
        'client' => 'predis',
        'default' => [
            'host' => env('REDIS_HOST', '127.0.0.1'),
            'password' => env('REDIS_PASSWORD', null),
            'port' => env('REDIS_PORT', 6379),
            'database' => 0,
        ],
    ],

'redis_backup' => [
    'client' => 'predis',
    'default' => [
        'host' => env('REDIS_BACKUP_HOST', '127.0.0.1'),
        'password' => env('REDIS_BACKUP_PASSWORD', null),
        'port' => env('REDIS_BACKUP_PORT', 6379),
        'database' => 0,
    ],
],

As we know, redis service provider by default is defered, so we need to put trying connection and rebinding in boot method.

<?php

namespace App\Providers;

use Illuminate\Redis\RedisManager;
use Illuminate\Support\Arr;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Redis;
use Illuminate\Support\ServiceProvider;
use Throwable;

class RedisBackupServiceProvider extends ServiceProvider
{
    /**
     * Bootstrap the application services.
     *
     * @return void
     */
    public function boot()
    {
        try {
            // ping redis service.
            $this->app['redis']->ping();
        } catch (Throwable $e) {
            // we don't care what exceptions,
            // just use backup redis keeping the connection and operations.
            $this->rebindRedis();
        }
    }

    private function rebindRedis()
    {
        Log::error('[Rebinding Redis for backup]', [
           'backup_at' => now()->toIso8601String(),
        ]);

        // forget the singleton instance.
        $this->app->forgetInstance('redis');

        // enabled backup redis.
        $this->app->singleton('redis', function ($app) {
            $config = $app->make('config')->get('database.redis_backup');
            return new RedisManager(Arr::pull($config, 'client', 'predis'), $config);
        });

        $this->app->bind('redis.connection', function ($app) {
            return $app['redis']->connection();
        });

        // rebind the singleton instance.
        Redis::clearResolvedInstance('redis');
    }
}

Finally we load the service proivder in app.php, like blow:

/**
 * For our situations, we have to own backup redis server, in the other hand, we don't make a crash when redis connection failed.
 * For first step, we will try to connection or checking redis master, if redis master was failed, switching to redis backup instead of.
 */
\App\Providers\RedisBackupServiceProvider::class,

I thought it’s a attempt solution for me, but as you know, we need to solve service unstable and exceptions, this is a other story.