Reworked HashRing::remove to handle hash collisions in a consistent way.

This commit is contained in:
Daniele Alessandri
2010-03-30 15:41:31 +02:00
parent 2cd056cab6
commit 486244be8d
+16 -9
View File
@@ -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);
}
}
}