Skip to content

Redis cluster authentication #58018

@zebamba

Description

@zebamba

Laravel Version

12.40.2

PHP Version

8.4

Database Driver & Version

No response

Description

I posted on issue #58000.

I had an issue with phpredis

The error message was:

phpredis - Couldn't map cluster keyspace using any provided seed Solved

Based on the Laravel documentation to set up a Redis cluster connection the config should look like:

'redis' => [

    'client' => env('REDIS_CLIENT', 'phpredis'),

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

    'clusters' => [
        'default' => [
            [
                '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'),
            ],
        ],
    ],

    // ...
],

I had to setup Redis cluster with phpredis with ACL authentication.

The issue I found is when calling method createRedisClusterInstance on PhpRedisConnector class

return tap(new RedisCluster(...$parameters) ...

The fifth argument auth on variable $parameters is set as null.

To solve the issue I had to change my configuration

'redis' => [

        'client' => env('REDIS_CLIENT', 'phpredis'),

        'options' => [
            'cluster' => env('REDIS_CLUSTER', 'redis'),
            'prefix' => env('REDIS_PREFIX', 'my_prefix:'),
            'password' => env('REDIS_USERNAME', null) ? [env('REDIS_USERNAME', null), env('REDIS_PASSWORD', null)] : env('REDIS_PASSWORD', null),
        ],

        'clusters' => [
            'default' => [
                [
                    'host' => env('REDIS_HOST_1', '127.0.0.1'),
                    'username' => env('REDIS_USERNAME', null),
                    'password' => env('REDIS_PASSWORD', null),
                    'port' => env('REDIS_PORT', '6379'),
                    'database' => env('REDIS_DB', '0'),
                    'persistent' => true,
                ],
                [
                    'host' => env('REDIS_HOST_2', '127.0.0.1'),
                    'username' => env('REDIS_USERNAME', null),
                    'password' => env('REDIS_PASSWORD', null),
                    'port' => env('REDIS_PORT', '6379'),
                    'database' => env('REDIS_DB', '0'),
                    'persistent' => true,
                ],
                [
                    'host' => env('REDIS_HOST_3', '127.0.0.1'),
                    'username' => env('REDIS_USERNAME', null),
                    'password' => env('REDIS_PASSWORD', null),
                    'port' => env('REDIS_PORT', '6379'),
                    'database' => env('REDIS_DB', '0'),
                    'persistent' => true,
                ],
            ],
        ],

    ],

Also as if you noticed 'persistent' => true, should be inside of each cluster connection.

Issue:

The class that generates the mapping for the variable $parameters is not mapping corrected.

OR

Laravel docs should be updated.

Hey @crynobone.

I did upgrade my Laravel framework 12.40.2 and the PHP version 8.4. and the issue still present. I have worked around

What I noticed on this new version is the 5th parameter is not present at all, as the previous version was set to null.

I do believe the method that maps the config into the $parameter is not reading for each cluster node neither from the options username and password.

The solution for me was to add credentials combined as you see below.

'options' => [
        'cluster' => env('REDIS_CLUSTER', 'redis'),
        'prefix' => env('REDIS_PREFIX', Str::slug(env('APP_NAME', 'laravel'), '_').'_database_'),
        'password' => env('REDIS_USERNAME', null) ? [env('REDIS_USERNAME', null), env('REDIS_PASSWORD', null)] : env('REDIS_PASSWORD', null),
    ],

Steps To Reproduce

Requires a Redis cluster with ACL

I added a test endpoint, as you see I had to keep the cache as database and change to redis on runtime to be able to test this endpoint.

Route::get('/cache-test', [TestController::class, 'cacheTest']);

class TestController extends Controller
{
    public function cacheTest(Request $request): JsonResponse
    {
        $validated = $request->validate([
            'key' => ['required', 'string', 'max:20', 'not_in:key'],
            'value' => ['required', 'string', 'max:100'],
            'ttl' => ['required', 'integer', 'min:20', 'max:180'],
        ], [
            'key.not_in' => 'The key name "key" is reserved and cannot be used.',
        ]);

        config(['cache.default' => 'redis']);

        $client = config('database.redis.client');
        $cacheStore = config('cache.default');

        $ttl = $validated['ttl'];
        $key = $validated['key'];
        $value = $validated['value'];

        try {
            $class = get_class(Redis::connection()->client());

            Cache::put($key, $value, $ttl);
            $cachedValue = Cache::get($key);

            $data = [
                'status' => 'success',
                'message' => 'Redis cache is working!',
                'key_set' => $key,
                'value_retrieved' => $cachedValue,
                'ttl_seconds' => $ttl,
                'store' => $cacheStore,
                'client' => $client,
                'class' => $class,
            ];

            if ($cachedValue && $cachedValue === $value) {
                return response()->json($data);
            }

            $data['status'] = 'failure';
            $data['message'] = 'Cache set but retrieved value does not match.';

            return response()->json($data);
        } catch (Exception $e) {
            return response()->json([
                'status' => 'error',
                'message' => 'Redis connection failed or another cache error occurred.',
                'error_detail' => $e->getMessage(),
            ], 500);
        }
    }
}

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions