@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 upstream/main 173 lines 4.3 kB view raw
1<?php 2 3final class PhabricatorKeyValueDatabaseCache 4 extends PhutilKeyValueCache { 5 6 const CACHE_FORMAT_RAW = 'raw'; 7 const CACHE_FORMAT_DEFLATE = 'deflate'; 8 9 public function setKeys(array $keys, $ttl = null) { 10 if (PhabricatorEnv::isReadOnly()) { 11 return; 12 } 13 14 if ($keys) { 15 $map = $this->digestKeys(array_keys($keys)); 16 $conn_w = $this->establishConnection('w'); 17 18 $sql = array(); 19 foreach ($map as $key => $hash) { 20 $value = $keys[$key]; 21 22 list($format, $storage_value) = $this->willWriteValue($key, $value); 23 24 $sql[] = qsprintf( 25 $conn_w, 26 '(%s, %s, %s, %B, %d, %nd)', 27 $hash, 28 $key, 29 $format, 30 $storage_value, 31 time(), 32 $ttl ? (time() + $ttl) : null); 33 } 34 35 $guard = AphrontWriteGuard::beginScopedUnguardedWrites(); 36 foreach (PhabricatorLiskDAO::chunkSQL($sql) as $chunk) { 37 queryfx( 38 $conn_w, 39 'INSERT INTO %T 40 (cacheKeyHash, cacheKey, cacheFormat, cacheData, 41 cacheCreated, cacheExpires) VALUES %LQ 42 ON DUPLICATE KEY UPDATE 43 cacheKey = VALUES(cacheKey), 44 cacheFormat = VALUES(cacheFormat), 45 cacheData = VALUES(cacheData), 46 cacheCreated = VALUES(cacheCreated), 47 cacheExpires = VALUES(cacheExpires)', 48 $this->getTableName(), 49 $chunk); 50 } 51 unset($guard); 52 } 53 54 return $this; 55 } 56 57 public function getKeys(array $keys) { 58 $results = array(); 59 if ($keys) { 60 $map = $this->digestKeys($keys); 61 62 $rows = queryfx_all( 63 $this->establishConnection('r'), 64 'SELECT * FROM %T WHERE cacheKeyHash IN (%Ls)', 65 $this->getTableName(), 66 $map); 67 $rows = ipull($rows, null, 'cacheKey'); 68 69 foreach ($keys as $key) { 70 if (empty($rows[$key])) { 71 continue; 72 } 73 74 $row = $rows[$key]; 75 76 if ($row['cacheExpires'] && ($row['cacheExpires'] < time())) { 77 continue; 78 } 79 80 try { 81 $results[$key] = $this->didReadValue( 82 $row['cacheFormat'], 83 $row['cacheData']); 84 } catch (Exception $ex) { 85 // Treat this as a cache miss. 86 phlog($ex); 87 } 88 } 89 } 90 91 return $results; 92 } 93 94 public function deleteKeys(array $keys) { 95 if ($keys) { 96 $map = $this->digestKeys($keys); 97 queryfx( 98 $this->establishConnection('w'), 99 'DELETE FROM %T WHERE cacheKeyHash IN (%Ls)', 100 $this->getTableName(), 101 $map); 102 } 103 104 return $this; 105 } 106 107 public function destroyCache() { 108 queryfx( 109 $this->establishConnection('w'), 110 'DELETE FROM %T', 111 $this->getTableName()); 112 return $this; 113 } 114 115 116/* -( Raw Cache Access )--------------------------------------------------- */ 117 118 119 public function establishConnection($mode) { 120 // TODO: This is the only concrete table we have on the database right 121 // now. 122 return id(new PhabricatorMarkupCache())->establishConnection($mode); 123 } 124 125 public function getTableName() { 126 return 'cache_general'; 127 } 128 129 130/* -( Implementation )----------------------------------------------------- */ 131 132 133 private function digestKeys(array $keys) { 134 $map = array(); 135 foreach ($keys as $key) { 136 $map[$key] = PhabricatorHash::digestForIndex($key); 137 } 138 return $map; 139 } 140 141 private function willWriteValue($key, $value) { 142 if (!is_string($value)) { 143 throw new Exception(pht('Only strings may be written to the DB cache!')); 144 } 145 146 static $can_deflate; 147 if ($can_deflate === null) { 148 $can_deflate = function_exists('gzdeflate'); 149 } 150 151 if ($can_deflate) { 152 $deflated = PhabricatorCaches::maybeDeflateData($value); 153 if ($deflated !== null) { 154 return array(self::CACHE_FORMAT_DEFLATE, $deflated); 155 } 156 } 157 158 return array(self::CACHE_FORMAT_RAW, $value); 159 } 160 161 private function didReadValue($format, $value) { 162 switch ($format) { 163 case self::CACHE_FORMAT_RAW: 164 return $value; 165 case self::CACHE_FORMAT_DEFLATE: 166 return PhabricatorCaches::inflateData($value); 167 default: 168 throw new Exception(pht('Unknown cache format.')); 169 } 170 } 171 172 173}