Author: Matthias Mullie (scrapbook@mullie.eu)
Esempio n. 1
0
 /**
  * {@inheritdoc}
  */
 public function getMulti(array $keys, array &$tokens = null)
 {
     // retrieve all that we can from local cache
     $values = $this->local->getMulti($keys);
     $tokens = array();
     // short-circuit reading from real cache if we have an uncommitted flush
     if (!$this->suspend) {
         // figure out which missing key we need to get from real cache
         $keys = array_diff($keys, array_keys($values));
         foreach ($keys as $i => $key) {
             // don't reach out to real cache for keys that are about to be gone
             if ($this->local->expired($key)) {
                 unset($keys[$i]);
             }
         }
         // fetch missing values from real cache
         if ($keys) {
             $missing = $this->cache->getMulti($keys);
             $values += $missing;
         }
     }
     // any tokens we get will be unreliable, so generate some replacements
     // (more elaborate explanation in get())
     foreach ($values as $key => $value) {
         $token = uniqid();
         $tokens[$key] = $token;
         $this->tokens[$token] = serialize($value);
     }
     return $values;
 }
Esempio n. 2
0
 /**
  * {@inheritdoc}
  */
 public function has($key)
 {
     if (!is_string($key)) {
         throw new InvalidArgumentException('Invalid key: ' . serialize($key) . '. Must be string.');
     }
     // KeyValueStore::get returns false for cache misses (which could also
     // be confused for a `false` value), so we'll check existence with getMulti
     $multi = $this->store->getMulti(array($key));
     return isset($multi[$key]);
 }
Esempio n. 3
0
 /**
  * As soon as a key turns up empty (doesn't yet exist in cache), we'll
  * "protect" it for some time. This will be done by writing to a key similar
  * to the original key name. If this key is present (which it will only be
  * for a short amount of time) we'll know it's protected.
  *
  * @param array $keys
  *
  * @return string[] Array of keys that were successfully protected
  */
 protected function protect(array $keys)
 {
     if (empty($keys)) {
         return array();
     }
     $success = array();
     foreach ($keys as $key) {
         /*
          * Key is add()ed because there may be multiple concurrent processes
          * that are both in the process of protecting - first one to add()
          * wins (and those are returned by the function, so those that are
          * failed to protect can be considered protected)
          */
         $success[$key] = $this->cache->add($this->stampedeKey($key), '', $this->sla);
     }
     return array_keys(array_filter($success));
 }
Esempio n. 4
0
 /**
  * Since we can't perform true atomic transactions, we'll fake it.
  * Most of the operations (set, touch, ...) can't fail. We'll do those last.
  * We'll first schedule the operations that can fail (cas, replace, add)
  * to minimize chances of another process overwriting those values in the
  * meantime.
  * But it could still happen, so we should fetch the current values for all
  * unsafe operations. If the transaction fails, we can then restore them.
  *
  * @return array[] Array of 2 [key => value] maps: current & scheduled data
  */
 protected function generateRollback()
 {
     $keys = array();
     $new = array();
     foreach ($this->keys as $key => $data) {
         $operation = $data[0];
         // we only need values for cas & replace - recovering from an 'add'
         // is just deleting the value...
         if (in_array($operation, array('cas', 'replace'))) {
             $keys[] = $key;
             $new[$key] = $data[2]['value'];
         }
     }
     if (empty($keys)) {
         return array(array(), array());
     }
     // fetch the existing data & return the planned new data as well
     $current = $this->cache->getMulti($keys);
     return array($current, $new);
 }
Esempio n. 5
0
 /**
  * Resolve all unresolved keys at once.
  */
 protected function resolve()
 {
     $keys = array_unique(array_values($this->unresolved));
     $values = $this->store->getMulti($keys);
     foreach ($this->unresolved as $unique => $key) {
         if (!array_key_exists($key, $values)) {
             // key doesn't exist in cache
             continue;
         }
         /*
          * In theory, there could've been multiple unresolved requests for
          * the same cache key. In the case of objects, we'll clone them
          * to make sure that when the value for 1 item is manipulated, it
          * doesn't affect the value of the other item (because those objects
          * would be passed by-ref without the cloning)
          */
         $value = $values[$key];
         $value = is_object($value) ? clone $value : $value;
         $this->resolved[$unique] = $value;
     }
     $this->unresolved = array();
 }
Esempio n. 6
0
 /**
  * {@inheritdoc}
  */
 public function commit()
 {
     $deferred = array();
     foreach ($this->deferred as $key => $item) {
         if ($item->isExpired()) {
             // already expired: don't even save it
             continue;
         }
         // setMulti doesn't allow to set expiration times on a per-item basis,
         // so we'll have to group our requests per expiration date
         $expire = $item->getExpiration();
         $deferred[$expire][$item->getKey()] = $item->get();
     }
     // setMulti doesn't allow to set expiration times on a per-item basis,
     // so we'll have to group our requests per expiration date
     $success = true;
     foreach ($deferred as $expire => $items) {
         $status = $this->store->setMulti($items, $expire);
         $success &= !in_array(false, $status);
         unset($deferred[$expire]);
     }
     return (bool) $success;
 }