diff --git a/lib/Predis.php b/lib/Predis.php index 61a0e8da..6b807556 100644 --- a/lib/Predis.php +++ b/lib/Predis.php @@ -1267,15 +1267,19 @@ namespace Predis\Utilities; class HashRing { const DEFAULT_REPLICAS = 128; - private $_ring, $_ringKeys, $_replicas; + private $_nodes, $_ring, $_ringKeys, $_replicas; public function __construct($replicas = self::DEFAULT_REPLICAS) { $this->_replicas = $replicas; + $this->_nodes = array(); $this->_ring = array(); $this->_ringKeys = array(); } public function add($node) { + // NOTE: in case of collisions in the hashes of the nodes, the node added + // last wins, thus the order in which nodes are added is significant. + $this->_nodes[] = $node; $nodeHash = (string) $node; $replicas = $this->_replicas; for ($i = 0; $i < $replicas; $i++) { @@ -1287,14 +1291,17 @@ class HashRing { } public function remove($node) { - $nodeHash = (string) $node; - $replicas = $this->_replicas; - for ($i = 0; $i < $replicas; $i++) { - $key = crc32($nodeHash . ':' . $i); - unset($this->_ring[$key]); - $this->_ringKeys = array_filter($this->_ringKeys, function($rk) use($key) { - return $rk !== $key; - }); + // NOTE: a node is removed by recreating the whole ring from scratch, in + // order to reassign possible hashes with collisions to the right node + // according to the order in which they were added in the first place. + $oldNodes = $this->_nodes; + $this->_nodes = array(); + $this->_ring = array(); + $this->_ringKeys = array(); + foreach ($oldNodes as $oldNode) { + if ($oldNode !== $node) { + $this->add($oldNode); + } } }