/** * Retrieve or build a graph cache bucket from the cache. * * Normally, this operates as a readthrough cache call. It can also be used * to force a cache update by passing the existing data to `$rebuild_data`. * * @param int Bucket key, from @{method:getBucketKey}. * @param mixed Current data, to force a cache rebuild of this bucket. * @return array Data from the cache. * @task cache */ private function getBucketData($bucket_key, $rebuild_data = null) { $cache_key = $this->getBucketCacheKey($bucket_key); // TODO: This cache stuff could be handled more gracefully, but the // database cache currently requires values to be strings and needs // some tweaking to support this as part of a stack. Our cache semantics // here are also unusual (not purely readthrough) because this cache is // appendable. $cache_level1 = PhabricatorCaches::getRepositoryGraphL1Cache(); $cache_level2 = PhabricatorCaches::getRepositoryGraphL2Cache(); if ($rebuild_data === null) { $bucket_data = $cache_level1->getKey($cache_key); if ($bucket_data) { return $bucket_data; } $bucket_data = $cache_level2->getKey($cache_key); if ($bucket_data) { $unserialized = @unserialize($bucket_data); if ($unserialized) { // Fill APC if we got a database hit but missed in APC. $cache_level1->setKey($cache_key, $unserialized); return $unserialized; } } } if (!is_array($rebuild_data)) { $rebuild_data = array(); } $bucket_data = $this->rebuildBucket($bucket_key, $rebuild_data); // Don't bother writing the data if we didn't update anything. if ($bucket_data !== $rebuild_data) { $cache_level2->setKey($cache_key, serialize($bucket_data)); $cache_level1->setKey($cache_key, $bucket_data); } return $bucket_data; }