@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 153 lines 3.5 kB view raw
1<?php 2 3/** 4 * @task memcache Managing Memcache 5 */ 6final class PhutilMemcacheKeyValueCache extends PhutilKeyValueCache { 7 8 private $servers = array(); 9 private $connections = array(); 10 11 12/* -( Key-Value Cache Implementation )------------------------------------- */ 13 14 15 public function isAvailable() { 16 return function_exists('memcache_pconnect'); 17 } 18 19 public function getKeys(array $keys) { 20 $buckets = $this->bucketKeys($keys); 21 $results = array(); 22 23 foreach ($buckets as $bucket => $bucket_keys) { 24 $conn = $this->getConnection($bucket); 25 $result = $conn->get($bucket_keys); 26 if (!$result) { 27 // If the call fails, treat it as a miss on all keys. 28 $result = array(); 29 } 30 31 $results += $result; 32 } 33 34 return $results; 35 } 36 37 public function setKeys(array $keys, $ttl = null) { 38 $buckets = $this->bucketKeys(array_keys($keys)); 39 40 // Memcache interprets TTLs as: 41 // 42 // - Seconds from now, for values from 1 to 2592000 (30 days). 43 // - Epoch timestamp, for values larger than 2592000. 44 // 45 // We support only relative TTLs, so convert excessively large relative 46 // TTLs into epoch TTLs. 47 if ($ttl > 2592000) { 48 $effective_ttl = time() + $ttl; 49 } else { 50 $effective_ttl = $ttl; 51 } 52 53 foreach ($buckets as $bucket => $bucket_keys) { 54 $conn = $this->getConnection($bucket); 55 56 foreach ($bucket_keys as $key) { 57 $conn->set($key, $keys[$key], 0, $effective_ttl); 58 } 59 } 60 61 return $this; 62 } 63 64 public function deleteKeys(array $keys) { 65 $buckets = $this->bucketKeys($keys); 66 67 foreach ($buckets as $bucket => $bucket_keys) { 68 $conn = $this->getConnection($bucket); 69 foreach ($bucket_keys as $key) { 70 $conn->delete($key); 71 } 72 } 73 74 return $this; 75 } 76 77 public function destroyCache() { 78 foreach ($this->servers as $key => $spec) { 79 $this->getConnection($key)->flush(); 80 } 81 return $this; 82 } 83 84 85/* -( Managing Memcache )-------------------------------------------------- */ 86 87 88 /** 89 * Set available memcache servers. For example: 90 * 91 * $cache->setServers( 92 * array( 93 * array( 94 * 'host' => '10.0.0.20', 95 * 'port' => 11211, 96 * ), 97 * array( 98 * 'host' => '10.0.0.21', 99 * 'port' => 11211, 100 * ), 101 * )); 102 * 103 * @param array $servers List of server specifications. 104 * @return $this 105 * @task memcache 106 */ 107 public function setServers(array $servers) { 108 $this->servers = array_values($servers); 109 return $this; 110 } 111 112 private function bucketKeys(array $keys) { 113 $buckets = array(); 114 $n = count($this->servers); 115 116 if (!$n) { 117 throw new PhutilInvalidStateException('setServers'); 118 } 119 120 foreach ($keys as $key) { 121 $bucket = (int)((crc32($key) & 0x7FFFFFFF) % $n); 122 $buckets[$bucket][] = $key; 123 } 124 125 return $buckets; 126 } 127 128 129 /** 130 * @phutil-external-symbol function memcache_pconnect 131 */ 132 private function getConnection($server) { 133 if (empty($this->connections[$server])) { 134 $spec = $this->servers[$server]; 135 $host = $spec['host']; 136 $port = $spec['port']; 137 138 $conn = memcache_pconnect($host, $spec['port']); 139 140 if (!$conn) { 141 throw new Exception( 142 pht( 143 'Unable to connect to memcache server (%s:%d)!', 144 $host, 145 $port)); 146 } 147 148 $this->connections[$server] = $conn; 149 } 150 return $this->connections[$server]; 151 } 152 153}