| <?php |
|
|
| |
| |
| |
| |
| |
| |
|
|
| namespace Piwik\Archive; |
|
|
| use Piwik\ArchiveProcessor\Rules; |
| use Piwik\Config; |
| use Piwik\Container\StaticContainer; |
| use Piwik\DataAccess\ArchiveTableCreator; |
| use Piwik\DataAccess\Model; |
| use Piwik\Date; |
| use Piwik\Piwik; |
| use Piwik\Period\Month; |
| use Piwik\Log\LoggerInterface; |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| class ArchivePurger |
| { |
| |
| |
| |
| private $model; |
|
|
| |
| |
| |
| |
| |
| |
| private $purgeCustomRangesOlderThan; |
|
|
| |
| |
| |
| |
| |
| private $yesterday; |
|
|
| |
| |
| |
| |
| |
| private $today; |
|
|
| |
| |
| |
| |
| |
| private $now; |
|
|
| |
| |
| |
| private $logger; |
|
|
| public function __construct(?Model $model = null, ?Date $purgeCustomRangesOlderThan = null, ?LoggerInterface $logger = null) |
| { |
| $this->model = $model ?: new Model(); |
|
|
| $this->purgeCustomRangesOlderThan = $purgeCustomRangesOlderThan ?: self::getDefaultCustomRangeToPurgeAgeThreshold(); |
|
|
| $this->yesterday = Date::factory('yesterday'); |
| $this->today = Date::factory('today'); |
| $this->now = time(); |
| $this->logger = $logger ?: StaticContainer::get(LoggerInterface::class); |
| } |
|
|
| |
| |
| |
| |
| |
| |
| |
| public function purgeInvalidatedArchivesFrom(Date $date) |
| { |
| $numericTable = ArchiveTableCreator::getNumericTable($date, false); |
| if (empty($numericTable)) { |
| return 0; |
| } |
|
|
| $archiveIds = $this->model->getInvalidatedArchiveIdsSafeToDelete($numericTable); |
| if (empty($archiveIds)) { |
| $this->logger->debug("No invalidated archives found in {table} with newer, valid archives.", array('table' => $numericTable)); |
| return 0; |
| } |
|
|
| $this->logger->info("Found {countArchiveIds} invalidated archives safe to delete in {table}.", array( |
| 'table' => $numericTable, 'countArchiveIds' => count($archiveIds), |
| )); |
|
|
| $deletedRowCount = $this->deleteArchiveIds($date, $archiveIds); |
|
|
| $this->logger->debug("Deleted {count} rows in {table} and its associated blob table.", array( |
| 'table' => $numericTable, 'count' => $deletedRowCount, |
| )); |
|
|
| return $deletedRowCount; |
| } |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| public function purgeOutdatedArchives(Date $dateStart) |
| { |
| $purgeArchivesOlderThan = $this->getOldestTemporaryArchiveToKeepThreshold(); |
| $deletedRowCount = 0; |
|
|
| $idArchivesToDelete = $this->getOutdatedArchiveIds($dateStart, $purgeArchivesOlderThan); |
| if (!empty($idArchivesToDelete)) { |
| $deletedRowCount = $this->deleteArchiveIds($dateStart, $idArchivesToDelete); |
|
|
| $this->logger->info("Deleted {count} rows in archive tables (numeric + blob) for {date}.", array( |
| 'count' => $deletedRowCount, |
| 'date' => $dateStart, |
| )); |
| } else { |
| $this->logger->debug("No outdated archives found in archive numeric table for {date}.", array('date' => $dateStart)); |
| } |
|
|
| $this->logger->debug("Purging temporary archives: done [ purged archives older than {date} in {yearMonth} ] [Deleted IDs count: {deletedIds}]", array( |
| 'date' => $purgeArchivesOlderThan, |
| 'yearMonth' => $dateStart->toString('Y-m'), |
| 'deletedIds' => count($idArchivesToDelete), |
| )); |
|
|
| return $deletedRowCount; |
| } |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| public function purgeBrokenArchives(Month $startMonth, ?Month $endMonth = null): int |
| { |
| if (!isset($endMonth) || $startMonth->getDateStart()->isLater($endMonth->getDateEnd())) { |
| $endMonth = $startMonth; |
| } |
|
|
| $currentMonth = $startMonth; |
| $currentMonthStart = $currentMonth->getDateStart(); |
| $numRowsDeleted = 0; |
|
|
| |
| do { |
| $idArchivesToDelete = $this->getBrokenArchiveIds($currentMonth); |
| $deletedRowCount = 0; |
| if (!empty($idArchivesToDelete)) { |
| $deletedRowCount = $this->deleteArchiveIds($currentMonthStart, $idArchivesToDelete); |
|
|
| $this->logger->info("Deleted {count} rows in archive tables (numeric + blob) for month starting {monthStart}.", [ |
| 'count' => $deletedRowCount, |
| 'monthStart' => $currentMonthStart->toString('Y-m'), |
| ]); |
| } else { |
| $this->logger->debug("No broken archives found in archive numeric table for month starting {monthStart}.", ['monthStart' => $currentMonthStart->toString('Y-m')]); |
| } |
|
|
| $numRowsDeleted += $deletedRowCount; |
| $this->logger->debug("Purging broken archives: done [ purged archives in {yearMonth} ] [Deleted IDs count: {deletedIds}]", [ |
| 'yearMonth' => $currentMonthStart->toString('Y-m'), |
| 'deletedIds' => $deletedRowCount, |
| ]); |
| $currentMonth = new Month($currentMonthStart->addMonth(1)); |
| $currentMonthStart = $currentMonth->getDateStart(); |
| } while ($endMonth->getDateEnd()->isLater($currentMonthStart)); |
|
|
| return $numRowsDeleted; |
| } |
|
|
| public function purgeDeletedSiteArchives(Date $dateStart) |
| { |
| $archiveTable = ArchiveTableCreator::getNumericTable($dateStart, false); |
| if (empty($archiveTable)) { |
| return 0; |
| } |
| $idArchivesToDelete = $this->model->getArchiveIdsForDeletedSites($archiveTable); |
|
|
| return $this->purge($idArchivesToDelete, $dateStart, 'deleted sites'); |
| } |
|
|
| |
| |
| |
| public function purgeDeletedSegmentArchives(Date $dateStart, array $deletedSegments): int |
| { |
| if (count($deletedSegments)) { |
| $idArchivesToDelete = $this->getDeletedSegmentArchiveIds($dateStart, $deletedSegments); |
| return $this->purge($idArchivesToDelete, $dateStart, 'deleted segments'); |
| } |
|
|
| return 0; |
| } |
|
|
| |
| |
| |
| |
| |
| protected function purge(array $idArchivesToDelete, Date $dateStart, $reason): int |
| { |
| $deletedRowCount = 0; |
| if (!empty($idArchivesToDelete)) { |
| $deletedRowCount = $this->deleteArchiveIds($dateStart, $idArchivesToDelete); |
|
|
| $this->logger->info( |
| "Deleted {count} rows in archive tables (numeric + blob) for {reason} for {date}.", |
| array( |
| 'count' => $deletedRowCount, |
| 'date' => $dateStart, |
| 'reason' => $reason, |
| ) |
| ); |
|
|
| $this->logger->debug("[Deleted IDs count: {deletedIds}]", array( |
| 'deletedIds' => count($idArchivesToDelete), |
| )); |
| } else { |
| $this->logger->debug( |
| "No archives for {reason} found in archive numeric table for {date}.", |
| array('date' => $dateStart, 'reason' => $reason) |
| ); |
| } |
|
|
| return $deletedRowCount; |
| } |
|
|
| protected function getDeletedSegmentArchiveIds(Date $date, array $deletedSegments) |
| { |
| $archiveTable = ArchiveTableCreator::getNumericTable($date, false); |
| if (empty($archiveTable)) { |
| return []; |
| } |
|
|
| return $this->model->getArchiveIdsForSegments( |
| $archiveTable, |
| $deletedSegments, |
| $this->getOldestTemporaryArchiveToKeepThreshold() |
| ); |
| } |
|
|
| protected function getOutdatedArchiveIds(Date $date, $purgeArchivesOlderThan) |
| { |
| $archiveTable = ArchiveTableCreator::getNumericTable($date, false); |
| if (empty($archiveTable)) { |
| return []; |
| } |
|
|
| $result = $this->model->getTemporaryArchivesOlderThan($archiveTable, $purgeArchivesOlderThan); |
|
|
| $idArchivesToDelete = array(); |
| if (!empty($result)) { |
| foreach ($result as $row) { |
| $idArchivesToDelete[] = $row['idarchive']; |
| } |
| } |
|
|
| return $idArchivesToDelete; |
| } |
|
|
| |
| |
| |
| |
| |
| |
| |
| private function getBrokenArchiveIds(Month $month): array |
| { |
| $archiveTable = ArchiveTableCreator::getNumericTable($month->getDateStart(), false); |
| if (empty($archiveTable)) { |
| return []; |
| } |
|
|
| $result = $this->model->getArchivesMissingDoneFlag($archiveTable); |
|
|
| if (!empty($result)) { |
| return array_column($result, 'idarchive'); |
| } |
|
|
| return []; |
| } |
|
|
| |
| |
| |
| |
| |
| public function purgeArchivesWithPeriodRange(Date $date) |
| { |
| $numericTable = ArchiveTableCreator::getNumericTable($date, false); |
| if (empty($numericTable)) { |
| return 0; |
| } |
|
|
| $blobTable = ArchiveTableCreator::getBlobTable($date, false); |
|
|
| $deletedCount = $this->model->deleteArchivesWithPeriod( |
| $numericTable, |
| $blobTable, |
| Piwik::$idPeriods['range'], |
| $this->purgeCustomRangesOlderThan |
| ); |
|
|
| $level = $deletedCount == 0 ? 'debug' : 'info'; |
| $this->logger->$level("Purged {count} range archive rows from {numericTable} & {blobTable}.", array( |
| 'count' => $deletedCount, |
| 'numericTable' => $numericTable, |
| 'blobTable' => $blobTable, |
| )); |
|
|
| $this->logger->debug(" [ purged archives older than {threshold} ]", array('threshold' => $this->purgeCustomRangesOlderThan)); |
|
|
| return $deletedCount; |
| } |
|
|
| |
| |
| |
| |
| |
| |
| protected function deleteArchiveIds(Date $date, $idArchivesToDelete): int |
| { |
| $batches = array_chunk($idArchivesToDelete, 1000); |
| $numericTable = ArchiveTableCreator::getNumericTable($date, false); |
| if (empty($numericTable)) { |
| return 0; |
| } |
|
|
| $blobTable = ArchiveTableCreator::getBlobTable($date, false); |
|
|
| $deletedCount = 0; |
| foreach ($batches as $idsToDelete) { |
| $deletedCount += $this->model->deleteArchiveIds($numericTable, $blobTable, $idsToDelete); |
| } |
| return $deletedCount; |
| } |
|
|
| |
| |
| |
| |
| |
| protected function getOldestTemporaryArchiveToKeepThreshold() |
| { |
| $temporaryArchivingTimeout = Rules::getTodayArchiveTimeToLive(); |
| if (Rules::isBrowserTriggerEnabled()) { |
| |
| |
| return Date::factory($this->now - 2 * $temporaryArchivingTimeout)->getDateTime(); |
| } |
|
|
| |
| return $this->yesterday->getDateTime(); |
| } |
|
|
| private static function getDefaultCustomRangeToPurgeAgeThreshold() |
| { |
| $daysRangesValid = Config::getInstance()->General['purge_date_range_archives_after_X_days']; |
| return Date::factory('today')->subDay($daysRangesValid)->getDateTime(); |
| } |
|
|
| |
| |
| |
| public function setYesterdayDate(Date $yesterday) |
| { |
| $this->yesterday = $yesterday; |
| } |
|
|
| |
| |
| |
| public function setTodayDate(Date $today) |
| { |
| $this->today = $today; |
| } |
|
|
| |
| |
| |
| |
| |
| public function setNow($now) |
| { |
| $this->now = $now; |
| } |
| } |
|
|