-
Notifications
You must be signed in to change notification settings - Fork 11.7k
Fix Cache::flexible() to work with AWS ElastiCache Serverless (Valkey) #57651
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: 12.x
Are you sure you want to change the base?
Conversation
The flexible() method was generating keys that could hash to different
slots in Redis Cluster, causing CROSSSLOT errors. This fix adds Redis
hash tags to ensure all related keys (value, created timestamp, and lock)
are stored in the same slot.
Changes:
- Add a 4-character MD5 hash prefix as a hash tag for slot allocation
- Wrap hash in {} to ensure consistent slot placement in Redis Cluster
- Maintain original key structure for non-cluster Redis instances
- Works with both phpredis and predis drivers
This resolves issues with AWS ElastiCache Serverless (Valkey) and other
Redis Cluster environments while remaining backward compatible with
standalone Redis instances.
|
@yhayase , your PR is backward incompatible, as will change all logic of setting and retreiving values from cache. It's very dangerous. Also you do not need to use md5 hash for key, as redis already calculates slot with more quick crc16 hash (see https://redis.io/docs/latest/operate/oss_and_stack/management/scaling/#redis-cluster-data-sharding). So, all you need are only changes in \Illuminate\Cache\Repository::flexible method. Maybe it was just a mistake not to escape curly braces. This will lead that slot for key 'xxx' and 'illuminate:cache:flexible:created:{xxx}' would be the same (see how redis calculate slots). There are integration tests with Redis Cluster in .github/workflows/queues.yml. You may add run of tests/Integration/Cache/RepositoryTest.php there to show if it fails before fix and not fails after it. |
This adds a new configuration option 'flexible_cluster_mode' that enables
sequential operations in Cache::flexible() for Redis Cluster compatibility.
Implementation:
- When 'flexible_cluster_mode' is enabled, flexible() uses sequential get/put
operations instead of bulk many()/putMany() operations
- This avoids CROSSSLOT errors in Redis Cluster environments where keys hash
to different slots
- Default is false to maintain existing behavior
Configuration (config/cache.php):
```php
'stores' => [
'redis' => [
'driver' => 'redis',
'flexible_cluster_mode' => env('CACHE_FLEXIBLE_CLUSTER_MODE', false),
],
],
```
Trade-offs:
- Sequential mode: 2 network round-trips instead of 1
- Performance impact is minimal since flexible() callback execution dominates
- This approach already exists for putMany() in cluster connections
All tests pass (75 tests, 302 assertions).
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
9fb8741 to
1eadeb1
Compare
Repository::flexible() references the flexible_cluster_mode config, but CacheManager::repository() was using Arr::only($config, ['store']), which prevented flexible_cluster_mode from being passed to Repository. Changes: - Modified Arr::only($config, ['store', 'flexible_cluster_mode']) - This ensures flexible_cluster_mode config is properly passed to Repository Test results: - Local: 75 tests, 302 assertions all passed - Valkey Serverless: 3/3 patterns all succeeded - phpredis + default: SUCCESS - predis + default: SUCCESS - predis + clusters: SUCCESS 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
60baaa4 to
c798cde
Compare
|
@vadimonus Thank you so much for your detailed review and valuable suggestions! I really appreciate you taking the time to point out the backward compatibility issue and propose a simpler solution. You're absolutely right that my original approach (adding hash tags to all keys) was backward incompatible and overly complex. I've been reconsidering the implementation based on your feedback. Issue with the hash tag escape approachI initially considered your suggestion to use escaped braces: "illuminate:cache:flexible:created:{{$key}}"This would produce a string like However, I found a critical issue with this approach. When a user provides a key like Redis Cluster would only use the content between the first
These would hash to different slots, causing the same CROSSSLOT error we're trying to fix. Alternative approach: Configuration-based sequential operationsI've explored a different approach that maintains backward compatibility while fixing the Redis Cluster issue: Add a Configuration (config/cache.php): 'stores' => [
'redis' => [
'driver' => 'redis',
'flexible_cluster_mode' => true,
],
],Implementation:
Trade-offs:
I've tested this approach successfully on AWS ElastiCache Serverless (Valkey) with all patterns passing. I've already updated the branch to implement this new approach. I'll update the PR description once we reach a consensus on the direction. What do you think about this approach? I'm open to any suggestions or alternative solutions you might have. Thanks again for your guidance! |
|
@yhayase You give such a polite and detailed answer that I don't always understand whether I'm communicating with you or with a gpt bot. Cache keys like If you choose use tho puts instead of one putMany, you do not need special config settings. It's enough to check something like |
Description
This PR fixes
Cache::flexible()to work with AWS ElastiCache Serverless (Valkey) and similar environments that havecluster_enabled: 1but provide a single endpoint.Problem
AWS ElastiCache Serverless (Valkey) has a unique configuration:
cluster_enabled: 1(cluster mode enabled)This creates a problem where neither connection mode works:
phpredis driver
Single-node connection mode (default):
The
flexible()method stores two keys:illuminate:cache:{key}illuminate:cache:flexible:created:{key}These keys hash to different slots in Redis Cluster. Since the server has cluster mode enabled, MGET returns a CROSSSLOT error.
Cluster connection mode:
phpredis RedisCluster fails to process responses from Valkey Serverless (Valkey Serverless-specific issue).
predis driver
Single-node connection mode (default):
Same CROSSSLOT error as phpredis.
Cluster connection mode:
predis checks hash tags before executing MGET. Without consistent hash tags, it rejects the operation.
Solution
Add Redis hash tags to ensure all related keys are stored in the same slot:
How it works
{...}to determine the slot{$hashKey}, ensuring they're in the same slot$keyis preserved outside the hash tag for uniquenessWhy this fixes the issue
For phpredis + default (single-node mode):
For predis + default (single-node mode):
For predis + clusters (cluster mode):
{$hashKey}Testing
Verified on AWS ElastiCache Serverless (Valkey) with
cluster_enabled: 1:Complete reproduction case: https://github.com/yhayase/laravel-cache-flexible-valkey-serverless-issue
Compatibility
flexible()cache entries will be missed on first access after upgradeRelated Issue
Fixes CROSSSLOT errors on AWS ElastiCache Serverless (Valkey) and similar environments where the server has cluster mode enabled but provides a single endpoint.