PluginProbe ʕ •ᴥ•ʔ
Matomo Analytics – Powerful, Privacy-First Insights for WordPress / 5.0.3
Matomo Analytics – Powerful, Privacy-First Insights for WordPress v5.0.3
5.13.0 5.12.1 5.12.0 5.11.1 5.11.0 5.10.2 5.10.1 trunk 1.0.2 1.0.3 1.0.4 1.0.5 1.0.6 1.1.0 1.1.1 1.1.2 1.1.3 1.2.0 1.3.0 1.3.1 1.3.2 4.0.0 4.0.1 4.0.2 4.0.3 4.0.4 4.1.0 4.1.1 4.1.2 4.1.3 4.10.0 4.11.0 4.12.0 4.13.0 4.13.2 4.13.3 4.13.4 4.13.5 4.14.0 4.14.1 4.14.2 4.15.0 4.15.1 4.15.2 4.15.3 4.2.0 4.3.0 4.3.1 4.4.1 4.4.2 4.5.0 4.6.0 5.0.1 5.0.2 5.0.3 5.0.4 5.0.5 5.0.6 5.0.7 5.0.8 5.1.0 5.1.1 5.1.2 5.1.3 5.1.4 5.1.5 5.1.6 5.1.7 5.10.0 5.2.0 5.2.1 5.2.2 5.3.0 5.3.1 5.3.2 5.3.3 5.6.0 5.6.1 5.7.0 5.7.1 5.8.0 5.8.1 5.8.2
matomo / app / core / Archive / ArchivePurger.php
matomo / app / core / Archive Last commit date
ArchiveInvalidator 2 years ago ArchiveInvalidator.php 2 years ago ArchivePurger.php 2 years ago ArchiveQuery.php 2 years ago ArchiveQueryFactory.php 2 years ago Chunk.php 2 years ago DataCollection.php 2 years ago DataTableFactory.php 2 years ago Parameters.php 2 years ago
ArchivePurger.php
253 lines
1 <?php
2
3 /**
4 * Matomo - free/libre analytics platform
5 *
6 * @link https://matomo.org
7 * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
8 *
9 */
10 namespace Piwik\Archive;
11
12 use Piwik\ArchiveProcessor\Rules;
13 use Piwik\Config;
14 use Piwik\Container\StaticContainer;
15 use Piwik\DataAccess\ArchiveTableCreator;
16 use Piwik\DataAccess\Model;
17 use Piwik\Date;
18 use Piwik\Piwik;
19 use Piwik\Log\LoggerInterface;
20 /**
21 * Service that purges temporary, error-ed, invalid and custom range archives from archive tables.
22 *
23 * Temporary archives are purged if they were archived before a specific time. The time is dependent
24 * on whether browser triggered archiving is enabled or not.
25 *
26 * Error-ed archives are purged w/o constraint.
27 *
28 * Invalid archives are purged if a new, valid, archive exists w/ the same site, date, period combination.
29 * Archives are marked as invalid via Piwik\Archive\ArchiveInvalidator.
30 */
31 class ArchivePurger
32 {
33 /**
34 * @var Model
35 */
36 private $model;
37 /**
38 * Date threshold for purging custom range archives. Archives that are older than this date
39 * are purged unconditionally from the requested archive table.
40 *
41 * @var Date
42 */
43 private $purgeCustomRangesOlderThan;
44 /**
45 * Date to use for 'yesterday'. Exists so tests can override this value.
46 *
47 * @var Date
48 */
49 private $yesterday;
50 /**
51 * Date to use for 'today'. Exists so tests can override this value.
52 *
53 * @var $today
54 */
55 private $today;
56 /**
57 * Date to use for 'now'. Exists so tests can override this value.
58 *
59 * @var int
60 */
61 private $now;
62 /**
63 * @var LoggerInterface
64 */
65 private $logger;
66 public function __construct(Model $model = null, Date $purgeCustomRangesOlderThan = null, LoggerInterface $logger = null)
67 {
68 $this->model = $model ?: new Model();
69 $this->purgeCustomRangesOlderThan = $purgeCustomRangesOlderThan ?: self::getDefaultCustomRangeToPurgeAgeThreshold();
70 $this->yesterday = Date::factory('yesterday');
71 $this->today = Date::factory('today');
72 $this->now = time();
73 $this->logger = $logger ?: StaticContainer::get(LoggerInterface::class);
74 }
75 /**
76 * Purge all invalidate archives for whom there are newer, valid archives from the archive
77 * table that stores data for `$date`.
78 *
79 * @param Date $date The date identifying the archive table.
80 * @return int The total number of archive rows deleted (from both the blog & numeric tables).
81 */
82 public function purgeInvalidatedArchivesFrom(Date $date)
83 {
84 $numericTable = ArchiveTableCreator::getNumericTable($date);
85 $archiveIds = $this->model->getInvalidatedArchiveIdsSafeToDelete($numericTable);
86 if (empty($archiveIds)) {
87 $this->logger->debug("No invalidated archives found in {table} with newer, valid archives.", array('table' => $numericTable));
88 return 0;
89 }
90 $this->logger->info("Found {countArchiveIds} invalidated archives safe to delete in {table}.", array('table' => $numericTable, 'countArchiveIds' => count($archiveIds)));
91 $deletedRowCount = $this->deleteArchiveIds($date, $archiveIds);
92 $this->logger->debug("Deleted {count} rows in {table} and its associated blob table.", array('table' => $numericTable, 'count' => $deletedRowCount));
93 return $deletedRowCount;
94 }
95 /**
96 * Removes the outdated archives for the given month.
97 * (meaning they are marked with a done flag of ArchiveWriter::DONE_OK_TEMPORARY or ArchiveWriter::DONE_ERROR)
98 *
99 * @param Date $dateStart Only the month will be used
100 * @return int Returns the total number of rows deleted.
101 */
102 public function purgeOutdatedArchives(Date $dateStart)
103 {
104 $purgeArchivesOlderThan = $this->getOldestTemporaryArchiveToKeepThreshold();
105 $deletedRowCount = 0;
106 $idArchivesToDelete = $this->getOutdatedArchiveIds($dateStart, $purgeArchivesOlderThan);
107 if (!empty($idArchivesToDelete)) {
108 $deletedRowCount = $this->deleteArchiveIds($dateStart, $idArchivesToDelete);
109 $this->logger->info("Deleted {count} rows in archive tables (numeric + blob) for {date}.", array('count' => $deletedRowCount, 'date' => $dateStart));
110 } else {
111 $this->logger->debug("No outdated archives found in archive numeric table for {date}.", array('date' => $dateStart));
112 }
113 $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)));
114 return $deletedRowCount;
115 }
116 public function purgeDeletedSiteArchives(Date $dateStart)
117 {
118 $archiveTable = ArchiveTableCreator::getNumericTable($dateStart);
119 $idArchivesToDelete = $this->model->getArchiveIdsForDeletedSites($archiveTable);
120 return $this->purge($idArchivesToDelete, $dateStart, 'deleted sites');
121 }
122 /**
123 * @param Date $dateStart
124 * @param array $deletedSegments List of segments whose archives should be purged
125 * @return int
126 */
127 public function purgeDeletedSegmentArchives(Date $dateStart, array $deletedSegments)
128 {
129 if (count($deletedSegments)) {
130 $idArchivesToDelete = $this->getDeletedSegmentArchiveIds($dateStart, $deletedSegments);
131 return $this->purge($idArchivesToDelete, $dateStart, 'deleted segments');
132 }
133 }
134 /**
135 * Purge all numeric and blob archives with the given IDs from the database.
136 * @param array $idArchivesToDelete
137 * @param Date $dateStart
138 * @param string $reason
139 * @return int
140 */
141 protected function purge(array $idArchivesToDelete, Date $dateStart, $reason)
142 {
143 $deletedRowCount = 0;
144 if (!empty($idArchivesToDelete)) {
145 $deletedRowCount = $this->deleteArchiveIds($dateStart, $idArchivesToDelete);
146 $this->logger->info("Deleted {count} rows in archive tables (numeric + blob) for {reason} for {date}.", array('count' => $deletedRowCount, 'date' => $dateStart, 'reason' => $reason));
147 $this->logger->debug("[Deleted IDs count: {deletedIds}]", array('deletedIds' => count($idArchivesToDelete)));
148 } else {
149 $this->logger->debug("No archives for {reason} found in archive numeric table for {date}.", array('date' => $dateStart, 'reason' => $reason));
150 }
151 return $deletedRowCount;
152 }
153 protected function getDeletedSegmentArchiveIds(Date $date, array $deletedSegments)
154 {
155 $archiveTable = ArchiveTableCreator::getNumericTable($date);
156 return $this->model->getArchiveIdsForSegments($archiveTable, $deletedSegments, $this->getOldestTemporaryArchiveToKeepThreshold());
157 }
158 protected function getOutdatedArchiveIds(Date $date, $purgeArchivesOlderThan)
159 {
160 $archiveTable = ArchiveTableCreator::getNumericTable($date);
161 $result = $this->model->getTemporaryArchivesOlderThan($archiveTable, $purgeArchivesOlderThan);
162 $idArchivesToDelete = array();
163 if (!empty($result)) {
164 foreach ($result as $row) {
165 $idArchivesToDelete[] = $row['idarchive'];
166 }
167 }
168 return $idArchivesToDelete;
169 }
170 /**
171 * Deleting "Custom Date Range" reports after 1 day, since they can be re-processed and would take up un-necessary space.
172 *
173 * @param $date Date
174 * @return int The total number of rows deleted from both the numeric & blob table.
175 */
176 public function purgeArchivesWithPeriodRange(Date $date)
177 {
178 $numericTable = ArchiveTableCreator::getNumericTable($date);
179 $blobTable = ArchiveTableCreator::getBlobTable($date);
180 $deletedCount = $this->model->deleteArchivesWithPeriod($numericTable, $blobTable, Piwik::$idPeriods['range'], $this->purgeCustomRangesOlderThan);
181 $level = $deletedCount == 0 ? 'debug' : 'info';
182 $this->logger->{$level}("Purged {count} range archive rows from {numericTable} & {blobTable}.", array('count' => $deletedCount, 'numericTable' => $numericTable, 'blobTable' => $blobTable));
183 $this->logger->debug(" [ purged archives older than {threshold} ]", array('threshold' => $this->purgeCustomRangesOlderThan));
184 return $deletedCount;
185 }
186 /**
187 * Deletes by batches Archive IDs in the specified month,
188 *
189 * @param Date $date
190 * @param $idArchivesToDelete
191 * @return int Number of rows deleted from both numeric + blob table.
192 */
193 protected function deleteArchiveIds(Date $date, $idArchivesToDelete)
194 {
195 $batches = array_chunk($idArchivesToDelete, 1000);
196 $numericTable = ArchiveTableCreator::getNumericTable($date);
197 $blobTable = ArchiveTableCreator::getBlobTable($date);
198 $deletedCount = 0;
199 foreach ($batches as $idsToDelete) {
200 $deletedCount += $this->model->deleteArchiveIds($numericTable, $blobTable, $idsToDelete);
201 }
202 return $deletedCount;
203 }
204 /**
205 * Returns a timestamp indicating outdated archives older than this timestamp (processed before) can be purged.
206 *
207 * @return int|bool Outdated archives older than this timestamp should be purged
208 */
209 protected function getOldestTemporaryArchiveToKeepThreshold()
210 {
211 $temporaryArchivingTimeout = Rules::getTodayArchiveTimeToLive();
212 if (Rules::isBrowserTriggerEnabled()) {
213 // If Browser Archiving is enabled, it is likely there are many more temporary archives
214 // We delete more often which is safe, since reports are re-processed on demand
215 return Date::factory($this->now - 2 * $temporaryArchivingTimeout)->getDateTime();
216 }
217 // If cron core:archive command is building the reports, we should keep all temporary reports from today
218 return $this->yesterday->getDateTime();
219 }
220 private static function getDefaultCustomRangeToPurgeAgeThreshold()
221 {
222 $daysRangesValid = Config::getInstance()->General['purge_date_range_archives_after_X_days'];
223 return Date::factory('today')->subDay($daysRangesValid)->getDateTime();
224 }
225 /**
226 * For tests.
227 *
228 * @param Date $yesterday
229 */
230 public function setYesterdayDate(Date $yesterday)
231 {
232 $this->yesterday = $yesterday;
233 }
234 /**
235 * For tests.
236 *
237 * @param Date $today
238 */
239 public function setTodayDate(Date $today)
240 {
241 $this->today = $today;
242 }
243 /**
244 * For tests.
245 *
246 * @param int $now
247 */
248 public function setNow($now)
249 {
250 $this->now = $now;
251 }
252 }
253