@recaptime-dev's working patches + fork for Phorge, a community fork of Phabricator. (Upstream dev and stable branches are at upstream/main and upstream/stable respectively.) hq.recaptime.dev/wiki/Phorge
phorge phabricator
at recaptime-dev/main 135 lines 3.2 kB view raw
1<?php 2 3/** 4 * Stacks multiple caches on top of each other, with readthrough semantics: 5 * 6 * - For reads, we try each cache in order until we find all the keys. 7 * - For writes, we set the keys in each cache. 8 * 9 * @task config Configuring the Stack 10 */ 11final class PhutilKeyValueCacheStack extends PhutilKeyValueCache { 12 13 14 /** 15 * Forward list of caches in the stack (from the nearest cache to the farthest 16 * cache). 17 */ 18 private $cachesForward; 19 20 21 /** 22 * Backward list of caches in the stack (from the farthest cache to the 23 * nearest cache). 24 */ 25 private $cachesBackward; 26 27 28 /** 29 * TTL to use for any writes which are side effects of the next read 30 * operation. 31 */ 32 private $nextTTL; 33 34 35/* -( Configuring the Stack )---------------------------------------------- */ 36 37 38 /** 39 * Set the caches which comprise this stack. 40 * 41 * @param array<PhutilKeyValueCache> $caches Ordered list of key-value 42 * caches. 43 * @return $this 44 * @task config 45 */ 46 public function setCaches(array $caches) { 47 assert_instances_of($caches, parent::class); 48 $this->cachesForward = $caches; 49 $this->cachesBackward = array_reverse($caches); 50 51 return $this; 52 } 53 54 55 /** 56 * Set the readthrough TTL for the next cache operation. The TTL applies to 57 * any keys set by the next call to @{method:getKey} or @{method:getKeys}, 58 * and is reset after the call finishes. 59 * 60 * // If this causes any caches to fill, they'll fill with a 15-second TTL. 61 * $stack->setNextTTL(15)->getKey('porcupine'); 62 * 63 * // TTL does not persist; this will use no TTL. 64 * $stack->getKey('hedgehog'); 65 * 66 * @param int $ttl TTL in seconds. 67 * @return $this 68 * 69 * @task config 70 */ 71 public function setNextTTL($ttl) { 72 $this->nextTTL = $ttl; 73 return $this; 74 } 75 76 77/* -( Key-Value Cache Implementation )------------------------------------- */ 78 79 80 public function getKeys(array $keys) { 81 82 $remaining = array_fuse($keys); 83 $results = array(); 84 $missed = array(); 85 86 try { 87 foreach ($this->cachesForward as $cache) { 88 $result = $cache->getKeys($remaining); 89 $remaining = array_diff_key($remaining, $result); 90 $results += $result; 91 if (!$remaining) { 92 while ($cache = array_pop($missed)) { 93 // TODO: This sets too many results in the closer caches, although 94 // it probably isn't a big deal in most cases; normally we're just 95 // filling the request cache. 96 $cache->setKeys($result, $this->nextTTL); 97 } 98 break; 99 } 100 $missed[] = $cache; 101 } 102 $this->nextTTL = null; 103 } catch (Exception $ex) { 104 $this->nextTTL = null; 105 throw $ex; 106 } 107 108 return $results; 109 } 110 111 112 public function setKeys(array $keys, $ttl = null) { 113 foreach ($this->cachesBackward as $cache) { 114 $cache->setKeys($keys, $ttl); 115 } 116 return $this; 117 } 118 119 120 public function deleteKeys(array $keys) { 121 foreach ($this->cachesBackward as $cache) { 122 $cache->deleteKeys($keys); 123 } 124 return $this; 125 } 126 127 128 public function destroyCache() { 129 foreach ($this->cachesBackward as $cache) { 130 $cache->destroyCache(); 131 } 132 return $this; 133 } 134 135}