AggregateCache.class.php

Go to the documentation of this file.
00001 <?php
00002 /****************************************************************************
00003  *   Copyright (C) 2005-2007 by Anton E. Lebedevich, Konstantin V. Arkhipov *
00004  *                                                                          *
00005  *   This program is free software; you can redistribute it and/or modify   *
00006  *   it under the terms of the GNU Lesser General Public License as         *
00007  *   published by the Free Software Foundation; either version 3 of the     *
00008  *   License, or (at your option) any later version.                        *
00009  *                                                                          *
00010  ****************************************************************************/
00011 /* $Id: AggregateCache.class.php 4687 2007-12-09 18:57:18Z voxus $ */
00012 
00019     final class AggregateCache extends SelectivePeer
00020     {
00021         const LEVEL_ULTRAHIGH   = 0xFFFF;
00022         const LEVEL_HIGH        = 0xC000;
00023         const LEVEL_NORMAL      = 0x8000;
00024         const LEVEL_LOW         = 0x4000;
00025         const LEVEL_VERYLOW     = 0x0001;
00026 
00027         private $peers  = array();
00028         private $levels = array();
00029 
00033         public static function create()
00034         {
00035             return new self;
00036         }
00037 
00041         public function addPeer(
00042             $label, CachePeer $peer, $level = self::LEVEL_NORMAL
00043         )
00044         {
00045             if (isset($this->peers[$label]))
00046                 throw new WrongArgumentException(
00047                     'use unique names for your peers'
00048                 );
00049             
00050             if ($peer->isAlive()) {
00051                 $this->peers[$label]['object'] = $peer;
00052                 $this->peers[$label]['level'] = $level;
00053                 $this->peers[$label]['stat'] = array();
00054                 $this->alive = true;
00055             }
00056             
00057             return $this;
00058         }
00059         
00063         public function dropPeer($label)
00064         {
00065             if (!isset($this->peers[$label]))
00066                 throw new MissingElementException(
00067                     "there is no peer with '{$label}' label"
00068                 );
00069 
00070             unset($this->peer[$label]);
00071             
00072             return $this;
00073         }
00074         
00078         public function setClassLevel($class, $level)
00079         {
00080             $this->levels[$class] = $level;
00081             
00082             return $this;
00083         }
00084 
00085         public function checkAlive()
00086         {
00087             $this->alive = false;
00088             
00089             foreach ($this->peers as $label => &$peer)
00090                 if ($peer['object']->isAlive())
00091                     $this->alive = true;
00092                 else
00093                     unset($this->peers[$label]);
00094 
00095             return $this->alive;
00096         }
00097 
00102         public function get($key)
00103         {
00104             $label = $this->guessLabel($key);
00105             
00106             if ($this->peers[$label]['object']->isAlive())
00107                 return $this->peers[$label]['object']->get($key);
00108             else
00109                 $this->checkAlive();
00110             
00111             return null;
00112         }
00113         
00114         public function delete($key)
00115         {
00116             $label = $this->guessLabel($key);
00117             
00118             if (!$this->peers[$label]['object']->isAlive()) {
00119                 $this->checkAlive();
00120                 return false;
00121             }
00122 
00123             return $this->peers[$label]['object']->delete($key);
00124         }
00125 
00129         public function clean()
00130         {
00131             foreach ($this->peers as &$peer)
00132                 $peer['object']->clean();
00133 
00134             $this->checkAlive();
00135 
00136             return parent::clean();
00137         }
00138 
00139         public function getStats()
00140         {
00141             $stats = array();
00142 
00143             foreach ($this->peers as $level => &$peer)
00144                 $stats[$level] = $peer['stat'];
00145 
00146             return $stats;
00147         }
00148 
00149         protected function store(
00150             $action, $key, &$value, $expires = Cache::EXPIRES_MINIMUM
00151         )
00152         {
00153             $label = $this->guessLabel($key);
00154             
00155             if ($this->peers[$label]['object']->isAlive())
00156                 return
00157                     $this->peers[$label]['object']->$action(
00158                         $key,
00159                         $value,
00160                         $expires
00161                     );
00162             else
00163                 $this->checkAlive();
00164             
00165             return false;
00166         }
00167 
00171         private function guessLabel($key)
00172         {
00173             $class = $this->getClassName();
00174 
00175             if (isset($this->levels[$class]))
00176                 $classLevel = $this->levels[$class];
00177             else
00178                 $classLevel = self::LEVEL_NORMAL;
00179 
00180             mt_srand(hexdec(substr(md5($key), 3, 7))); // init by $key
00181 
00182             $zeroDistances = array();
00183             $weights = array();
00184 
00185             foreach ($this->peers as $label => $peer) {
00186                 $distance = abs($classLevel - $peer['level']);
00187 
00188                 if (!$distance)
00189                     $zeroDistances[] = $label;
00190                 else
00191                     $weights[$peer['level']] = 1 / pow($distance, 2); // BOVM
00192             }
00193             
00194             if (count($zeroDistances)) {
00195 
00196                 $selectedLabel =
00197                     $zeroDistances[mt_rand(0, count($zeroDistances) - 1)];
00198 
00199             } else {
00200 
00201                 // weighted random level selection
00202                 $sum = mt_rand() * array_sum($weights) / mt_getrandmax();
00203                 $peerLevel = null;
00204                 foreach ($weights as $level => $weight) {
00205                     if ($sum <= $weight) {
00206                         $peerLevel = $level;
00207                         break;
00208                     } else
00209                         $sum -= $weight;
00210                 }
00211 
00212                 $selectedPeers = array();
00213                 foreach ($this->peers as $label => $peer) {
00214                     if ($peer['level'] == $peerLevel)
00215                         $selectedPeers[] = $label;
00216                 }
00217 
00218                 $selectedLabel = $selectedPeers[mt_rand(0, count($selectedPeers) - 1)];
00219             }
00220 
00221             if (isset($this->peers[$selectedLabel]['stat'][$class]))
00222                 ++$this->peers[$selectedLabel]['stat'][$class];
00223             else
00224                 $this->peers[$selectedLabel]['stat'][$class] = 1;
00225 
00226             return $selectedLabel;
00227         }
00228     }
00229 ?>

Generated on Sun Dec 9 21:56:23 2007 for onPHP by  doxygen 1.5.4