Skip to content

Conversation

@yhayase
Copy link
Owner

@yhayase yhayase commented Nov 7, 2025

Purpose

This PR serves as a development record for implementing Redis single-endpoint cluster support using a test-first approach. This is NOT intended to be merged into this fork, but rather demonstrates the complete development process before submitting to the upstream Laravel repository.

Background

PR laravel#53940 in laravel/framework only fixed putMany() for explicit cluster connection types (PhpRedisClusterConnection, PredisClusterConnection), but did not address:

  1. Single-endpoint clusters (e.g., AWS Elasticache Valkey Serverless) that use regular connection classes but run in cluster mode
  2. Other methods like many() and flexible() that also perform cross-slot operations
  3. Tagged cache flush operations in cluster environments

Test-First Development Process

1. Test Infrastructure Setup

  • Created comprehensive integration tests for 4 connection types:
    • phpredis single-endpoint
    • phpredis cluster
    • predis single-endpoint
    • predis cluster
  • Built redis-cluster-proxy with INFO command support
  • Configured GitHub Actions workflow for CI testing

2. Write Failing Tests First

Initial test run (before implementation): Tests: 33, Errors: 19, Failures: 1

  • All cross-slot operations failed as expected
  • Demonstrated the problem clearly with concrete test cases

3. Implement to Make Tests Pass

  • Implemented server-side cluster mode detection using INFO command
  • Added WeakMap-based caching for performance
  • Applied cluster fallback to putMany(), many(), and tagged cache operations
  • Handled phpredis vs predis response format differences

4. Verify All Tests Pass

Final test run (after implementation): Tests: 33, Assertions: 108 - All passing ✓

  • All connection types working correctly
  • Tagged cache flush operations working in cluster mode
  • Backward compatibility maintained

Implementation Details

Server-side cluster detection:

protected function isServerInClusterMode($connection)
{
    if (! isset($this->clusterModeCache[$connection])) {
        try {
            $info = $connection->client()->info('cluster');
            
            // Handle different formats from phpredis vs predis
            $clusterEnabled = $info['cluster_enabled']
                ?? $info['Cluster']['cluster_enabled']
                ?? null;
                
            $this->clusterModeCache[$connection] = $clusterEnabled == 1;
        } catch (\Exception $e) {
            $this->clusterModeCache[$connection] = false;
        }
    }
    
    return $this->clusterModeCache[$connection];
}

Files Modified:

  • src/Illuminate/Cache/RedisStore.php - Core cluster detection and fallback
  • src/Illuminate/Cache/RedisTaggedCache.php - Tagged cache flush support

Test Coverage:

  • tests/Integration/Cache/RedisSingleEndpointClusterTest.php - 33 tests covering all scenarios
  • .github/workflows/redis-cluster.yml - CI workflow for cluster testing

CI Results

✅ All tests passing in GitHub Actions:

  • Redis Cluster tests (3 jobs) - All passing
  • Main test suite (48+ jobs across PHP 8.2/8.3/8.4, multiple PHPUnit versions, Linux/Windows) - All passing
  • Backward compatibility confirmed

Next Steps

This implementation is ready to be submitted as a PR to laravel/framework. The test-first approach ensures:

  1. Problem is clearly demonstrated with failing tests before any implementation
  2. Solution is validated with passing tests
  3. No regressions in existing functionality
  4. Comprehensive documentation of the development process

🤖 This PR demonstrates test-first development and is a preparation record for upstream contribution.

hayase_yasuhiro added 3 commits November 7, 2025 18:43
This commit adds integration tests to verify Redis Cache behavior
in single-endpoint cluster environments (e.g., AWS Elasticache
Valkey Serverless) where cluster mode is enabled but accessed
through a single proxy endpoint.

Test Coverage:
- Cross-slot operations (putMany, many, flexible, remember)
- Flexible cache value lifecycle (get, put, forget)
- Tagged cache operations (put, putMany, many, flexible, flush)
- Multiple connection types (phpredis single-endpoint, predis
  single-endpoint, predis cluster)

Infrastructure:
- Docker Compose setup for Redis Cluster (3 nodes)
- redis-cluster-proxy build with INFO command patch
- GitHub Actions workflow for CI testing
- Support for both phpredis and predis drivers

The tests currently fail as expected, demonstrating the need for
cluster mode detection. They will pass once the WeakMap-based
server cluster detection is implemented in RedisStore.

Related to PR laravel#53940 which only addressed putMany() for cluster
connection types, but not for single-endpoint clusters.
This commit implements runtime cluster mode detection using the
Redis INFO command, enabling proper handling of single-endpoint
cluster configurations (e.g., AWS Elasticache Valkey Serverless).

Implementation details:
- Add WeakMap-based cache for cluster mode detection per connection
- Query server cluster mode via INFO cluster command
- Handle different response formats between phpredis and predis:
  * phpredis: ['cluster_enabled' => 1]
  * predis: ['Cluster' => ['cluster_enabled' => '1']]
- Apply cluster fallback to both putMany() and many() methods

The implementation now correctly handles three connection scenarios:
1. Explicit cluster connections (PhpRedisClusterConnection,
   PredisClusterConnection)
2. Single-endpoint clusters (regular connection to cluster-mode server)
3. Non-cluster Redis servers (standard behavior)

For cluster mode, cross-slot operations fall back to individual
commands to avoid CROSSSLOT, MOVED, and driver-specific errors.

Test results: 30/33 tests passing. The 3 failing tests are related
to tagged cache flush() operations, which is a separate issue with
RedisTaggedCache::flushValues() using multi-key DEL commands.

Related to PR laravel#53940 which only handled explicit cluster connection
types but not single-endpoint clusters.
This commit extends cluster mode detection to RedisTaggedCache,
ensuring that tagged cache flush operations work correctly in
cluster environments.

Changes:
- Add needsClusterFallback() method to detect cluster mode
  (handles both explicit cluster connections and single-endpoint
  clusters)
- Modify flush() to use cluster-aware flush for all cluster modes
- Update flushValues() to use individual DEL operations in cluster
  mode instead of multi-key DEL (which causes CROSSSLOT errors)

The implementation properly handles phpredis vs predis INFO command
response format differences:
- phpredis: ['cluster_enabled' => 1]
- predis: ['Cluster' => ['cluster_enabled' => '1']]

All 33 tests now pass, including tagged cache flush operations
across all three connection types (phpredis single-endpoint,
predis single-endpoint, predis cluster).
@yhayase yhayase changed the title Add Redis single-endpoint cluster support with server-side detection [WIP/Test Record] Redis single-endpoint cluster support with TDD approach Nov 7, 2025
@yhayase yhayase changed the title [WIP/Test Record] Redis single-endpoint cluster support with TDD approach [WIP/Test Record] Redis single-endpoint cluster support with test-first approach Nov 7, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants