isEnabled = (bool)$config->General['enable_segments_subquery_cache']; $this->limitActionIds = $config->General['segments_subquery_cache_limit']; $this->lifetime = $config->General['segments_subquery_cache_ttl']; $this->logger = $logger; $this->cache = $cache; } /** * @param $valueToMatch * @param $sql * @return array|null * @throws \Exception */ public function getIdActionFromSegment($valueToMatch, $sql) { if (!$this->isEnabled) { return array( // mark that the returned value is an sql-expression instead of a literal value 'SQL' => $sql, 'bind' => $valueToMatch, ); } $ids = self::getIdsFromCache($valueToMatch, $sql); if(is_null($ids)) { // Too Big To Cache, issue SQL as subquery instead return array( 'SQL' => $sql, 'bind' => $valueToMatch, ); } if(count($ids) == 0) { return null; } $sql = Common::getSqlStringFieldsArray($ids); $bind = $ids; return array( // mark that the returned value is an sql-expression instead of a literal value 'SQL' => $sql, 'bind' => $bind, ); } /** * @param $valueToMatch * @param $sql * @return array of IDs, or null if the returnset is too big to cache */ private function getIdsFromCache($valueToMatch, $sql) { $cacheKey = $this->getCacheKey($valueToMatch, $sql); if ($this->cache->contains($cacheKey) === true) { // TODO: hits $this->logger->debug("Segment subquery cache HIT (for '$valueToMatch' and SQL '$sql)"); return $this->cache->fetch($cacheKey); } $ids = $this->fetchActionIdsFromDb($valueToMatch, $sql); if($this->isTooBigToCache($ids)) { $this->logger->debug("Segment subquery cache SKIPPED SAVE (too many IDs returned by subquery: %s ids)'", array(count($ids))); $this->cache->save($cacheKey, $ids = null, $this->lifetime); return null; } $this->cache->save($cacheKey, $ids, $this->lifetime); $this->logger->debug("Segment subquery cache SAVE (for '$valueToMatch' and SQL '$sql')'"); return $ids; } /** * @param $valueToMatch * @param $sql * @return string * @throws */ private function getCacheKey($valueToMatch, $sql) { if(is_array($valueToMatch)) { throw new \Exception("value to match is an array: this is not expected"); } $uniqueKey = md5($sql . $valueToMatch); $cacheKey = 'TableLogAction.getIdActionFromSegment.' . $uniqueKey; return $cacheKey; } /** * @param $valueToMatch * @param $sql * @return array|null * @throws \Exception */ private function fetchActionIdsFromDb($valueToMatch, $sql) { $idActions = \Piwik\Db::fetchAll($sql, $valueToMatch); $ids = array(); foreach ($idActions as $idAction) { $ids[] = $idAction['idaction']; } return $ids; } /** * @param $ids * @return bool */ private function isTooBigToCache($ids) { return count($ids) > $this->limitActionIds; } }