add redis cluster adapter for v0.9.1 #194
Open
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Add Redis Cluster Support for v0.9.1
This PR adds Redis Cluster support to the legacy v0.9.1 version.
While integrating this library into an older PHP project, I discovered that the original Redis adapter does not work correctly in cluster mode. After analyzing the source code, I identified three key issues and implemented a backward-compatible solution.
🐞 Problem Analysis & Solutions
Problem:
When multiple keys fall into different hash slots, Redis Cluster returns:
CROSSSLOT Keys in request don't hash to the same slot
Solution:
Introduce a setHashTag method to ensure all related keys are placed under the same hash slot:
RedisCluster::setHashTag('TEST_PROMETHEUS');
Problem:
The original adapter relies on EVAL to guarantee atomicity.
However, Redis Cluster does not support cross-slot Lua scripts, resulting in potential write conflicts between multiple application instances.
Solution:
Following Prometheus best practices, assign a unique prefix for each instance to isolate keys:
$instanceID = !empty($_SERVER['SERVER_ADDR']) ? $_SERVER['SERVER_ADDR'] : '127.0.0.1';
RedisCluster::setPrefix('TEST_PROMETHEUS:' . $instanceID);
This ensures each instance only writes to its own metrics keys.
Problem:
Atomic operations can no longer rely on EVAL since Lua scripts may not run on the same node in cluster mode.
Solution:
Use idempotent Redis operations to simulate atomic behavior and reduce inconsistency risks:
// Idempotent operations replacing the original Lua script logic
$pipe->hsetnx($metricKey, '__meta', json_encode($metaData)); // Set only if missing
$pipe->sadd($metricKeysKey, $metricKey); // Idempotent insert
⚖️ Trade-offs
If a Redis connection drops during pipeline execution, some operations may succeed while others fail — strict atomicity cannot be guaranteed.
However, this trade-off is acceptable for Prometheus metric data, where a small amount of inconsistency is tolerable.
💡 Contribution Suggestions
If the maintainers find this approach valuable, I am happy to continue contributing with:
Full Redis Cluster support for the latest version, or
Keeping this PR as a backward-compatible solution, where users only need to copy:
src/Prometheus/Storage/RedisCluster.php