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