Skip to content

Instantly share code, notes, and snippets.

@marcojetson
Created October 1, 2015 10:45
Show Gist options
  • Save marcojetson/dc0ca815634cebe53cfc to your computer and use it in GitHub Desktop.
Save marcojetson/dc0ca815634cebe53cfc to your computer and use it in GitHub Desktop.
<?php
class PoolManagerException extends Exception {}
class PoolManagerInvalidWeightException extends PoolManagerException {}
class PoolManagerNodeNotFoundException extends PoolManagerException {}
class PoolManagerEmptyException extends PoolManagerException {}
/**
* Generic pool manager with horizontal scaling in mind
*
* @license http://www.xfree86.org/3.3.6/COPYRIGHT2.html#5 Modified BSD license
* @version 1.0
*/
class PoolManager
{
/** Cache de las keys */
private $_cache;
/** Pool keys */
private $_circle;
/** Node tracking */
private $_nodes;
/**
* Constructor generico
*
* @return PoolManager
*/
public function __construct()
{
$this->_cache = false;
$this->_circle = array();
$this->_nodes = array();
return $this;
}
/**
* Agrega un nodo al pool con un peso (opcional)
*
* @param $node string
* @param $weight int
* @throws PoolManagerInvalidWeightException peso no valido
* @return PoolManager
*/
public function add($node, $weight = 100)
{
if ($weight < 1 || $weight > 100) {
// peso invalido
throw new PoolManagerInvalidWeightException;
}
// agrega el nodo al tracker
$this->_nodes[$node] = $weight;
// agrega las keys (tantas como el peso) al circulo
for ($i = 0; $i < $weight; ++$i) {
$index = self::hash($node . '/' . $i);
$this->_circle[$index] = $node;
}
// limpia cache
$this->_cache = false;
return $this;
}
/**
* Quita un nodo del pool
*
* @param $node string
* @throws PoolManagerNodeNotFoundException nodo no encontrado
* @return PoolManager
*/
public function remove($node)
{
if (!isset($this->_nodes[$node])) {
// el nodo no existe
throw new PoolManagerNodeNotFoundException;
}
// obtiene el peso y lo elimina del tracker
$weight = $this->_nodes[$node];
unset($this->_nodes[$node]);
// lo borra del circulo (tantas keys como su peso)
for ($i = 0; $i < $weight; ++$i) {
$index = self::hash($node . '/' . $i);
unset($this->_circle[$index]);
}
// limpia cache
$this->_cache = false;
return $this;
}
/**
* Calcula un nodo para una key especifica
*
* @param $key string
* @throws PoolManagerEmptyException pool vacio
* @return string
*/
public function getNode($key)
{
if (!$this->size()) {
// no hay nodos en el pool
throw new PoolManagerEmptyException;
}
if (!$this->_cache) {
// cache is empty, generate it
$this->_cache = array_keys($this->_circle);
sort($this->_cache);
}
// busca la key mas cercana
$hash = self::hash($key);
$begin = 0;
$end = sizeof($this->_cache) - 1;
if ($this->_cache[0] > $hash) {
$hash += $this->_cache[0];
} else if ($this->_cache[$end] < $hash) {
$hash -= $this->_cache[$end];
}
while ($end - $begin > 1) {
$mid = ($end + $begin) >> 1;
if ($this->_cache[$mid] >= $hash) {
$end = $mid;
} else {
$begin = $mid;
}
}
// obtiene el pool para la key correspondiente
$index = $this->_cache[$end];
$node = $this->_circle[$index];
return $node;
}
/**
* Devuelve el tamano del pool
*
* @return int
*/
public function size()
{
$size = sizeof($this->_nodes);
return $size;
}
/**
* Calcula una representacion decimal (y safe) de un string
*
* @param $string string
* @return string
*/
static public function hash($string)
{
$hash = md5($string);
$hash = substr($hash, 1, 4);
$hash = base_convert($hash, 16, 10);
return $hash;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment