From d0d43a7b1c08a8d9a3252bc6cefaed171f2fcfce Mon Sep 17 00:00:00 2001 From: mattab <matthieu.aubry@gmail.com> Date: Mon, 17 Jun 2013 22:42:59 +1200 Subject: [PATCH] Committing two new files --- core/DataAccess/ArchiveTableCreator.php | 29 +++ core/DataAccess/ArchiveWriter.php | 261 ++++++++++++++++++++++++ 2 files changed, 290 insertions(+) create mode 100644 core/DataAccess/ArchiveTableCreator.php create mode 100644 core/DataAccess/ArchiveWriter.php diff --git a/core/DataAccess/ArchiveTableCreator.php b/core/DataAccess/ArchiveTableCreator.php new file mode 100644 index 0000000000..9046f631a7 --- /dev/null +++ b/core/DataAccess/ArchiveTableCreator.php @@ -0,0 +1,29 @@ +<?php +/** + * Piwik - Open source web analytics + * + * @link http://piwik.org + * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later + * + * @category Piwik + * @package Piwik + */ + +class Piwik_DataAccess_ArchiveTableCreator +{ + static public function getNumericTable(Piwik_Date $date) + { + return self::getTable($date, "numeric"); + } + static public function getBlobTable(Piwik_Date $date) + { + return self::getTable($date, "blob"); + } + + static protected function getTable(Piwik_Date $date, $type) + { + Piwik_TablePartitioning_Monthly::createArchiveTablesIfAbsent($date); + + return Piwik_Common::prefixTable("archive_" . $type . "_" . $date->toString('Y_m')); + } +} \ No newline at end of file diff --git a/core/DataAccess/ArchiveWriter.php b/core/DataAccess/ArchiveWriter.php new file mode 100644 index 0000000000..7c42fb5ee5 --- /dev/null +++ b/core/DataAccess/ArchiveWriter.php @@ -0,0 +1,261 @@ +<?php +/** + * Piwik - Open source web analytics + * + * @link http://piwik.org + * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later + * + * @category Piwik + * @package Piwik + */ + +class Piwik_DataAccess_ArchiveWriter +{ + /** + * A row is created to lock an idarchive for the current archive being processed + * @var string + */ + const PREFIX_SQL_LOCK = "locked_"; + + protected $fields = array('idarchive', + 'idsite', + 'date1', + 'date2', + 'period', + 'ts_archived', + 'name', + 'value'); + + public function __construct($idSite, Piwik_Segment $segment, Piwik_Period $period, $requestedPlugin, $isArchiveTemporary) + { + $this->idArchive = false; + $this->idSite = $idSite; + $this->segment = $segment; + $this->period = $period; + $this->doneFlag = Piwik_ArchiveProcessor_Rules::getDoneStringFlagFor($segment, $period->getLabel(), $requestedPlugin); + $this->isArchiveTemporary = $isArchiveTemporary; + + $this->dateStart = $this->period->getDateStart(); + $this->numericTable = Piwik_DataAccess_ArchiveTableCreator::getNumericTable($this->dateStart); + $this->blobTable = Piwik_DataAccess_ArchiveTableCreator::getBlobTable($this->dateStart); + } + + public function getIdArchive() + { + if($this->idArchive === false) { + throw new Exception("Must call allocateNewArchiveId() first"); + } + return $this->idArchive; + } + + public function initNewArchive() + { + $this->acquireLock(); + $this->allocateNewArchiveId(); + $this->logArchiveStatusAsIncomplete(); + } + + protected function acquireLock() + { + $lockName = $this->getArchiveProcessorLockName(); + $result = Piwik_GetDbLock($lockName, $maxRetries = 30); + if (!$result) { + Piwik::log("SELECT GET_LOCK failed to acquire lock. Proceeding anyway."); + } + } + + protected function allocateNewArchiveId() + { + $this->idArchive = $this->insertNewArchiveId(); + return $this->idArchive; + } + + protected function insertNewArchiveId() + { + $table = $this->numericTable; + $idSite = $this->idSite; + + $db = Zend_Registry::get('db'); + $locked = self::PREFIX_SQL_LOCK . Piwik_Common::generateUniqId(); + $date = date("Y-m-d H:i:s"); + $dbLockName = "allocateNewArchiveId.$table"; + + if (Piwik_GetDbLock($dbLockName, $maxRetries = 30) === false) { + throw new Exception("allocateNewArchiveId: Cannot get named lock for table $table."); + } + $insertSql = "INSERT INTO $table " + . " SELECT ifnull(max(idarchive),0)+1, + '" . $locked . "', + " . (int)$idSite . ", + '" . $date . "', + '" . $date . "', + 0, + '" . $date . "', + 0 " + . " FROM $table as tb1"; + $db->exec($insertSql); + Piwik_ReleaseDbLock($dbLockName); + $selectIdSql = "SELECT idarchive FROM $table WHERE name = ? LIMIT 1"; + $id = $db->fetchOne($selectIdSql, $locked); + return $id; + } + + protected function logArchiveStatusAsIncomplete() + { + $statusWhileProcessing = Piwik_ArchiveProcessor::DONE_ERROR; + $this->insertRecord($this->doneFlag, $statusWhileProcessing); + } + + protected function getArchiveProcessorLockName() + { + return self::makeLockName($this->idSite, $this->period, $this->segment); + } + + /** + * @param $idsite + * @param $period + * @param Piwik_Segment $segment + * @return string + */ + protected static function makeLockName($idsite, Piwik_Period $period, Piwik_Segment $segment) + { + $config = Piwik_Config::getInstance(); + + $lockName = 'piwik.' + . $config->database['dbname'] . '.' + . $config->database['tables_prefix'] . '/' + . $idsite . '/' + . (!$segment->isEmpty() ? $segment->getHash() . '/' : '') + . $period->getId() . '/' + . $period->getDateStart()->toString('Y-m-d') . ',' + . $period->getDateEnd()->toString('Y-m-d'); + return $lockName . '/' . md5($lockName . Piwik_Common::getSalt()); + } + + public function finalizeArchive() + { + $this->deletePreviousArchiveStatus(); + $this->logArchiveStatusAsFinal(); + $this->releaseArchiveProcessorLock(); + + if($this->period->getLabel() != 'day') { + $purgeArchivesOlderThan = Piwik_ArchiveProcessor_Rules::doPurgeOutdatedArchives($this->dateStart); + if($purgeArchivesOlderThan) { + Piwik_DataAccess_ArchiveSelector::purgeOutdatedArchives($this->dateStart, $purgeArchivesOlderThan); + } + } + } + + protected function deletePreviousArchiveStatus() + { + Piwik_Query("DELETE FROM " . $this->numericTable . " + WHERE idarchive = ? AND (name = '" . $this->doneFlag . "' OR name LIKE '" . self::PREFIX_SQL_LOCK . "%')", + array($this->getIdArchive()) + ); + } + + protected function logArchiveStatusAsFinal() + { + $status = Piwik_ArchiveProcessor::DONE_OK; + if ($this->isArchiveTemporary) { + $status = Piwik_ArchiveProcessor::DONE_OK_TEMPORARY; + } + $this->insertRecord($this->doneFlag, $status); + } + + protected function releaseArchiveProcessorLock() + { + $lockName = $this->getArchiveProcessorLockName(); + return Piwik_ReleaseDbLock($lockName); + } + + public function insertBulkRecords($records) + { + // Using standard plain INSERT if there is only one record to insert + if ($DEBUG_DO_NOT_USE_BULK_INSERT = false + || count($records) == 1 + ) { + foreach ($records as $record) { + $this->insertRecord($record[0], $record[1]); + } + return true; + } + $bindSql = $this->getInsertRecordBind(); + $values = array(); + + $valueSeen = false; + foreach ($records as $record) { + // don't record zero + if (empty($record[1])) continue; + + $bind = $bindSql; + $bind[] = $record[0]; // name + $bind[] = $record[1]; // value + $values[] = $bind; + + $valueSeen = $record[1]; + } + if (empty($values)) return true; + + $tableName = $this->getTableNameToInsert($valueSeen); + + Piwik::tableInsertBatch($tableName, $this->getInsertFields(), $values); + return true; + } + + /** + * Inserts a record in the right table (either NUMERIC or BLOB) + * + * @param string $name + * @param mixed $value + */ + public function insertRecord($name, $value) + { + if ($this->isRecordZero($value)) { + return false; + } + + $tableName = $this->getTableNameToInsert($value); + + // duplicate idarchives are Ignored, see http://dev.piwik.org/trac/ticket/987 + + $query = "INSERT IGNORE INTO " . $tableName . " + (" . implode(", ", $this->getInsertFields()) . ") + VALUES (?,?,?,?,?,?,?,?)"; + $bindSql = $this->getInsertRecordBind(); + $bindSql[] = $name; + $bindSql[] = $value; + Piwik_Query($query, $bindSql); + return true; + } + + protected function getInsertRecordBind() + { + return array($this->getIdArchive(), + $this->idSite, + $this->dateStart->toString('Y-m-d'), + $this->period->getDateEnd()->toString('Y-m-d'), + $this->period->getId(), + date("Y-m-d H:i:s")); + } + + protected function getTableNameToInsert($value) + { + if (is_numeric($value)) { + return $this->numericTable; + } + return $this->blobTable; + } + + protected function getInsertFields() + { + return $this->fields; + } + + protected function isRecordZero($value) + { + return ($value === '0' || $value === false || $value === 0 || $value === 0.0); + } + + +} \ No newline at end of file -- GitLab