# AggregateCache.class.php

Go to the documentation of this file.

    00001 <?php
    00002 /****************************************************************************
    00003  *   Copyright (C) 2005-2008 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
    00018     class AggregateCache extends SelectivePeer
    00019     {
    00020         const LEVEL_ULTRAHIGH   = 0xFFFF;
    00021         const LEVEL_HIGH        = 0xC000;
    00022         const LEVEL_NORMAL      = 0x8000;
    00023         const LEVEL_LOW         = 0x4000;
    00024         const LEVEL_VERYLOW     = 0x0001;
    00025
    00026         protected $peers    = array();
    00027         private $levels     = array();
    00028
    00032         public static function create()
    00033         {
    00034             return new self;
    00035         }
    00036
    00040         public function addPeer(
    00041             $label, CachePeer $peer, $level = self::LEVEL_NORMAL
    00042         )
    00043         {
    00044             if (isset($this->peers[$label]))
    00045                 throw new WrongArgumentException(
    00046                     'use unique names for your peers'
    00047                 );
    00048
    00049             if ($peer->isAlive()) {
    00050                 $this->peers[$label]['object'] = $peer;
    00051                 $this->peers[$label]['level'] = $level;
    00052                 $this->peers[$label]['stat'] = array();
    00053                 $this->alive = true;
    00054             }
    00055
    00056             return $this;
    00057         }
    00058
    00062         public function dropPeer($label)
    00063         {
    00064             if (!isset($this->peers[$label]))
    00065                 throw new MissingElementException(
    00066                     "there is no peer with '{$label}' label"
    00067                 );
    00068
    00069             unset($this->peer[$label]);
    00070
    00071             return $this;
    00072         }
    00073
    00077         public function setClassLevel($class, $level)
    00078         {
    00079             $this->levels[$class] = $level;
    00080
    00081             return $this;
    00082         }
    00083
    00084         public function checkAlive()
    00085         {
    00086             $this->alive = false;
    00087
    00088             foreach ($this->peers as $label => $peer)
    00089                 if ($peer['object']->isAlive())
    00090                     $this->alive = true;
    00091                 else
    00092                     unset($this->peers[$label]);
    00093
    00094             return $this->alive;
    00095         }
    00096
    00101         public function increment($key, $value)
    00102         {
    00103             $label = $this->guessLabel($key);
    00104
    00105             if ($this->peers[$label]['object']->isAlive())
    00106                 return $this->peers[$label]['object']->increment($key, $value);
    00107             else
    00108                 $this->checkAlive();
    00109
    00110             return null;
    00111         }
    00112
    00113         public function decrement($key, $value)
    00114         {
    00115             $label = $this->guessLabel($key);
    00116
    00117             if ($this->peers[$label]['object']->isAlive())
    00118                 return $this->peers[$label]['object']->decrement($key, $value);
    00119             else
    00120                 $this->checkAlive();
    00121
    00122             return null;
    00123         }
    00124
    00125         public function get($key)
    00126         {
    00127             $label = $this->guessLabel($key);
    00128
    00129             if ($this->peers[$label]['object']->isAlive())
    00130                 return $this->peers[$label]['object']->get($key);
    00131             else
    00132                 $this->checkAlive();
    00133
    00134             return null;
    00135         }
    00136
    00137         public function getList($indexes)
    00138         {
    00139             $labels = array();
    00140             $out = array();
    00141
    00142             foreach ($indexes as $index)
    00143                 $labels[$this->guessLabel($index)][] = $index;
    00144
    00145             foreach ($labels as $label => $indexList)
    00146                 if ($this->peers[$label]['object']->isAlive()) {
    00147                     if ($list = $this->peers[$label]['object']->getList($indexList))
    00148                         $out = array_merge($out, $list);
    00149                 } else
    00150                     $this->checkAlive();
    00151
    00152             return $out;
    00153         }
    00154
    00155         public function delete($key)
    00156         {
    00157             $label = $this->guessLabel($key);
    00158
    00159             if (!$this->peers[$label]['object']->isAlive()) {
    00160                 $this->checkAlive();
    00161                 return false;
    00162             }
    00163
    00164             return $this->peers[$label]['object']->delete($key);
    00165         }
    00166
    00170         public function clean()
    00171         {
    00172             foreach ($this->peers as $peer)
    00173                 $peer['object']->clean();
    00174
    00175             $this->checkAlive();
    00176
    00177             return parent::clean();
    00178         }
    00179
    00180         public function getStats()
    00181         {
    00182             $stats = array();
    00183
    00184             foreach ($this->peers as $level => $peer)
    00185                 $stats[$level] = $peer['stat'];
    00186
    00187             return $stats;
    00188         }
    00189
    00190         public function append($key, $data)
    00191         {
    00192             $label = $this->guessLabel($key);
    00193
    00194             if ($this->peers[$label]['object']->isAlive())
    00195                 return $this->peers[$label]['object']->append($key, $data);
    00196             else
    00197                 $this->checkAlive();
    00198
    00199             return false;
    00200         }
    00201
    00202         protected function store(
    00203             $action, $key, $value, $expires = Cache::EXPIRES_MINIMUM
    00204         )
    00205         {
    00206             $label = $this->guessLabel($key);
    00207
    00208             if ($this->peers[$label]['object']->isAlive())
    00209                 return
    00210                     $this->peers[$label]['object']->$action(
    00211                         $key,
    00212                         $value,
    00213                         $expires
    00214                     );
    00215             else
    00216                 $this->checkAlive();
    00217
    00218             return false;
    00219         }
    00220
    00224         protected function guessLabel($key)
    00225         {
    00226             $class = $this->getClassName();
    00227
    00228             if (isset($this->levels[$class]))
    00229                 $classLevel = $this->levels[$class];
    00230             else
    00231                 $classLevel = self::LEVEL_NORMAL;
    00232
    00233             // init by $key, randomness will be restored later
    00234             mt_srand(hexdec(substr(md5($key), 3, 7)));
    00235
    00236             $zeroDistances = array();
    00237             $weights = array();
    00238
    00239             foreach ($this->peers as $label => $peer) {
    00240                 $distance = abs($classLevel - $peer['level']);
    00241
    00242                 if (!$distance)
    00243                     $zeroDistances[] = $label;
    00244                 else
    00245                     $weights[$peer['level']] = 1 / pow($distance, 2); // BOVM
    00246             }
    00247
    00248             if (count($zeroDistances)) {
    00249
    00250                 $selectedLabel =
    00251                     $zeroDistances[mt_rand(0, count($zeroDistances) - 1)];
    00252
    00253             } else {
    00254
    00255                 // weighted random level selection
    00256                 $sum = mt_rand() * array_sum($weights) / mt_getrandmax();
    00257                 $peerLevel = null;
    00258                 foreach ($weights as $level => $weight) {
    00259                     if ($sum <= $weight) {
    00260                         $peerLevel = $level;
    00261                         break;
    00262                     } else
    00263                         $sum -= $weight;
    00264                 }
    00265
    00266                 $selectedPeers = array();
    00267                 foreach ($this->peers as $label => $peer) {
    00268                     if ($peer['level'] == $peerLevel)
    00269                         $selectedPeers[] = $label;
    00270                 }
    00271
    00272                 $selectedLabel = $selectedPeers[mt_rand(0, count($selectedPeers) - 1)];
    00273             }
    00274
    00275             if (isset($this->peers[$selectedLabel]['stat'][$class]))
    00276                 ++$this->peers[$selectedLabel]['stat'][$class];
    00277             else
    00278                 $this->peers[$selectedLabel]['stat'][$class] = 1;
    00279
    00280             // restore randomness
    00281             mt_srand(
    00282                 (int) (
    00283                     (int) (microtime(true) << 2)
    00284                     * (rand(time() / 2, time()) >> 2)
    00285                 )
    00286             );
    00287
    00288             return $selectedLabel;
    00289         }
    00290     }
    00291 ?>