PluginProbe ʕ •ᴥ•ʔ
Matomo Analytics – Powerful, Privacy-First Insights for WordPress / 1.3.1
Matomo Analytics – Powerful, Privacy-First Insights for WordPress v1.3.1
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 6 years ago ArchiveInvalidator.php 6 years ago ArchivePurger.php 6 years ago ArchiveQuery.php 6 years ago ArchiveQueryFactory.php 6 years ago Chunk.php 6 years ago DataCollection.php 6 years ago DataTableFactory.php 6 years ago Parameters.php 6 years ago
ArchivePurger.php
330 lines
1 <?php
2 /**
3 * Piwik - free/libre analytics platform
4 *
5 * @link https://matomo.org
6 * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
7 *
8 */
9 namespace Piwik\Archive;
10
11 use Piwik\ArchiveProcessor\Rules;
12 use Piwik\Common;
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\Db;
19 use Piwik\Piwik;
20 use Psr\Log\LoggerInterface;
21 use Psr\Log\LogLevel;
22
23 /**
24 * Service that purges temporary, error-ed, invalid and custom range archives from archive tables.
25 *
26 * Temporary archives are purged if they were archived before a specific time. The time is dependent
27 * on whether browser triggered archiving is enabled or not.
28 *
29 * Error-ed archives are purged w/o constraint.
30 *
31 * Invalid archives are purged if a new, valid, archive exists w/ the same site, date, period combination.
32 * Archives are marked as invalid via Piwik\Archive\ArchiveInvalidator.
33 */
34 class ArchivePurger
35 {
36 /**
37 * @var Model
38 */
39 private $model;
40
41 /**
42 * Date threshold for purging custom range archives. Archives that are older than this date
43 * are purged unconditionally from the requested archive table.
44 *
45 * @var Date
46 */
47 private $purgeCustomRangesOlderThan;
48
49 /**
50 * Date to use for 'yesterday'. Exists so tests can override this value.
51 *
52 * @var Date
53 */
54 private $yesterday;
55
56 /**
57 * Date to use for 'today'. Exists so tests can override this value.
58 *
59 * @var $today
60 */
61 private $today;
62
63 /**
64 * Date to use for 'now'. Exists so tests can override this value.
65 *
66 * @var int
67 */
68 private $now;
69
70 /**
71 * @var LoggerInterface
72 */
73 private $logger;
74
75 public function __construct(Model $model = null, Date $purgeCustomRangesOlderThan = null, LoggerInterface $logger = null)
76 {
77 $this->model = $model ?: new Model();
78
79 $this->purgeCustomRangesOlderThan = $purgeCustomRangesOlderThan ?: self::getDefaultCustomRangeToPurgeAgeThreshold();
80
81 $this->yesterday = Date::factory('yesterday');
82 $this->today = Date::factory('today');
83 $this->now = time();
84 $this->logger = $logger ?: StaticContainer::get('Psr\Log\LoggerInterface');
85 }
86
87 /**
88 * Purge all invalidate archives for whom there are newer, valid archives from the archive
89 * table that stores data for `$date`.
90 *
91 * @param Date $date The date identifying the archive table.
92 * @return int The total number of archive rows deleted (from both the blog & numeric tables).
93 */
94 public function purgeInvalidatedArchivesFrom(Date $date)
95 {
96 $numericTable = ArchiveTableCreator::getNumericTable($date);
97
98 $archiveIds = $this->model->getInvalidatedArchiveIdsSafeToDelete($numericTable);
99 if (empty($archiveIds)) {
100 $this->logger->debug("No invalidated archives found in {table} with newer, valid archives.", array('table' => $numericTable));
101 return 0;
102 }
103
104 $this->logger->info("Found {countArchiveIds} invalidated archives safe to delete in {table}.", array(
105 'table' => $numericTable, 'countArchiveIds' => count($archiveIds)
106 ));
107
108 $deletedRowCount = $this->deleteArchiveIds($date, $archiveIds);
109
110 $this->logger->debug("Deleted {count} rows in {table} and its associated blob table.", array(
111 'table' => $numericTable, 'count' => $deletedRowCount
112 ));
113
114 return $deletedRowCount;
115 }
116
117 /**
118 * Removes the outdated archives for the given month.
119 * (meaning they are marked with a done flag of ArchiveWriter::DONE_OK_TEMPORARY or ArchiveWriter::DONE_ERROR)
120 *
121 * @param Date $dateStart Only the month will be used
122 * @return int Returns the total number of rows deleted.
123 */
124 public function purgeOutdatedArchives(Date $dateStart)
125 {
126 $purgeArchivesOlderThan = $this->getOldestTemporaryArchiveToKeepThreshold();
127 $deletedRowCount = 0;
128
129 $idArchivesToDelete = $this->getOutdatedArchiveIds($dateStart, $purgeArchivesOlderThan);
130 if (!empty($idArchivesToDelete)) {
131 $deletedRowCount = $this->deleteArchiveIds($dateStart, $idArchivesToDelete);
132
133 $this->logger->info("Deleted {count} rows in archive tables (numeric + blob) for {date}.", array(
134 'count' => $deletedRowCount,
135 'date' => $dateStart
136 ));
137 } else {
138 $this->logger->debug("No outdated archives found in archive numeric table for {date}.", array('date' => $dateStart));
139 }
140
141 $this->logger->debug("Purging temporary archives: done [ purged archives older than {date} in {yearMonth} ] [Deleted IDs: {deletedIds}]", array(
142 'date' => $purgeArchivesOlderThan,
143 'yearMonth' => $dateStart->toString('Y-m'),
144 'deletedIds' => implode(',', $idArchivesToDelete)
145 ));
146
147 return $deletedRowCount;
148 }
149
150 public function purgeDeletedSiteArchives(Date $dateStart)
151 {
152 $archiveTable = ArchiveTableCreator::getNumericTable($dateStart);
153 $idArchivesToDelete = $this->model->getArchiveIdsForDeletedSites($archiveTable);
154
155 return $this->purge($idArchivesToDelete, $dateStart, 'deleted sites');
156 }
157
158 /**
159 * @param Date $dateStart
160 * @param array $deletedSegments List of segments whose archives should be purged
161 * @return int
162 */
163 public function purgeDeletedSegmentArchives(Date $dateStart, array $deletedSegments)
164 {
165 if (count($deletedSegments)) {
166 $idArchivesToDelete = $this->getDeletedSegmentArchiveIds($dateStart, $deletedSegments);
167 return $this->purge($idArchivesToDelete, $dateStart, 'deleted segments');
168 }
169 }
170
171 /**
172 * Purge all numeric and blob archives with the given IDs from the database.
173 * @param array $idArchivesToDelete
174 * @param Date $dateStart
175 * @param string $reason
176 * @return int
177 */
178 protected function purge(array $idArchivesToDelete, Date $dateStart, $reason)
179 {
180 $deletedRowCount = 0;
181 if (!empty($idArchivesToDelete)) {
182 $deletedRowCount = $this->deleteArchiveIds($dateStart, $idArchivesToDelete);
183
184 $this->logger->info(
185 "Deleted {count} rows in archive tables (numeric + blob) for {reason} for {date}.",
186 array(
187 'count' => $deletedRowCount,
188 'date' => $dateStart,
189 'reason' => $reason
190 )
191 );
192
193 $this->logger->debug("[Deleted IDs: {deletedIds}]", array(
194 'deletedIds' => implode(',', $idArchivesToDelete)
195 ));
196 } else {
197 $this->logger->debug(
198 "No archives for {reason} found in archive numeric table for {date}.",
199 array('date' => $dateStart, 'reason' => $reason)
200 );
201 }
202
203 return $deletedRowCount;
204 }
205
206 protected function getDeletedSegmentArchiveIds(Date $date, array $deletedSegments)
207 {
208 $archiveTable = ArchiveTableCreator::getNumericTable($date);
209 return $this->model->getArchiveIdsForSegments(
210 $archiveTable, $deletedSegments, $this->getOldestTemporaryArchiveToKeepThreshold()
211 );
212 }
213
214 protected function getOutdatedArchiveIds(Date $date, $purgeArchivesOlderThan)
215 {
216 $archiveTable = ArchiveTableCreator::getNumericTable($date);
217
218 $result = $this->model->getTemporaryArchivesOlderThan($archiveTable, $purgeArchivesOlderThan);
219
220 $idArchivesToDelete = array();
221 if (!empty($result)) {
222 foreach ($result as $row) {
223 $idArchivesToDelete[] = $row['idarchive'];
224 }
225 }
226
227 return $idArchivesToDelete;
228 }
229
230 /**
231 * Deleting "Custom Date Range" reports after 1 day, since they can be re-processed and would take up un-necessary space.
232 *
233 * @param $date Date
234 * @return int The total number of rows deleted from both the numeric & blob table.
235 */
236 public function purgeArchivesWithPeriodRange(Date $date)
237 {
238 $numericTable = ArchiveTableCreator::getNumericTable($date);
239 $blobTable = ArchiveTableCreator::getBlobTable($date);
240
241 $deletedCount = $this->model->deleteArchivesWithPeriod(
242 $numericTable, $blobTable, Piwik::$idPeriods['range'], $this->purgeCustomRangesOlderThan);
243
244 $level = $deletedCount == 0 ? LogLevel::DEBUG : LogLevel::INFO;
245 $this->logger->log($level, "Purged {count} range archive rows from {numericTable} & {blobTable}.", array(
246 'count' => $deletedCount,
247 'numericTable' => $numericTable,
248 'blobTable' => $blobTable
249 ));
250
251 $this->logger->debug(" [ purged archives older than {threshold} ]", array('threshold' => $this->purgeCustomRangesOlderThan));
252
253 return $deletedCount;
254 }
255
256 /**
257 * Deletes by batches Archive IDs in the specified month,
258 *
259 * @param Date $date
260 * @param $idArchivesToDelete
261 * @return int Number of rows deleted from both numeric + blob table.
262 */
263 protected function deleteArchiveIds(Date $date, $idArchivesToDelete)
264 {
265 $batches = array_chunk($idArchivesToDelete, 1000);
266 $numericTable = ArchiveTableCreator::getNumericTable($date);
267 $blobTable = ArchiveTableCreator::getBlobTable($date);
268
269 $deletedCount = 0;
270 foreach ($batches as $idsToDelete) {
271 $deletedCount += $this->model->deleteArchiveIds($numericTable, $blobTable, $idsToDelete);
272 }
273 return $deletedCount;
274 }
275
276 /**
277 * Returns a timestamp indicating outdated archives older than this timestamp (processed before) can be purged.
278 *
279 * @return int|bool Outdated archives older than this timestamp should be purged
280 */
281 protected function getOldestTemporaryArchiveToKeepThreshold()
282 {
283 $temporaryArchivingTimeout = Rules::getTodayArchiveTimeToLive();
284 if (Rules::isBrowserTriggerEnabled()) {
285 // If Browser Archiving is enabled, it is likely there are many more temporary archives
286 // We delete more often which is safe, since reports are re-processed on demand
287 return Date::factory($this->now - 2 * $temporaryArchivingTimeout)->getDateTime();
288 }
289
290 // If cron core:archive command is building the reports, we should keep all temporary reports from today
291 return $this->yesterday->getDateTime();
292 }
293
294 private static function getDefaultCustomRangeToPurgeAgeThreshold()
295 {
296 $daysRangesValid = Config::getInstance()->General['purge_date_range_archives_after_X_days'];
297 return Date::factory('today')->subDay($daysRangesValid)->getDateTime();
298 }
299
300 /**
301 * For tests.
302 *
303 * @param Date $yesterday
304 */
305 public function setYesterdayDate(Date $yesterday)
306 {
307 $this->yesterday = $yesterday;
308 }
309
310 /**
311 * For tests.
312 *
313 * @param Date $today
314 */
315 public function setTodayDate(Date $today)
316 {
317 $this->today = $today;
318 }
319
320 /**
321 * For tests.
322 *
323 * @param int $now
324 */
325 public function setNow($now)
326 {
327 $this->now = $now;
328 }
329 }
330