1
0
قرینه از https://github.com/matomo-org/matomo.git synced 2025-08-22 15:07:44 +00:00
Files
matomo/tests/PHPUnit/Integration/CronArchiveTest.php
Stefan Giehl c55d07a767 Write empty archive instead of skipping period without visits in archive cron (#23396)
* create empty archive when there are no visits

* don'T skip processing an invalidation if there are no visits

* Improve ArchiveCronTest on 5.x-dev state

* Avoid using local cache instance, so cache get's automatically cleared in tests

* only use website creation date for re archiving segments from beginning of time

* allow skipping recent date invalidation for tests

* fix some tests

* ensure to correctly create empty partial archives

* use correct method

* update phpstan baseline
2025-08-21 14:06:42 +02:00

1659 خطوط
69 KiB
PHP

<?php
/**
* Matomo - free/libre analytics platform
*
* @link https://matomo.org
* @license https://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
*/
namespace Piwik\Tests\Integration;
use Piwik\ArchiveProcessor\Parameters;
use Piwik\ArchiveProcessor\Rules;
use Piwik\Common;
use Piwik\Container\StaticContainer;
use Piwik\CronArchive;
use Piwik\DataAccess\ArchiveTableCreator;
use Piwik\DataAccess\ArchiveWriter;
use Piwik\Date;
use Piwik\Db;
use Piwik\Option;
use Piwik\Period\Factory;
use Piwik\Plugins\CoreAdminHome\tests\Framework\Mock\API;
use Piwik\Plugins\SegmentEditor\Model;
use Piwik\Segment;
use Piwik\Sequence;
use Piwik\Site;
use Piwik\Tests\Framework\Fixture;
use Piwik\Tests\Framework\Mock\FakeLogger;
use Piwik\Tests\Framework\TestCase\IntegrationTestCase;
use Piwik\Plugins\SegmentEditor\API as SegmentAPI;
use Piwik\Version;
/**
* @group Archiver
* @group CronArchive
*/
class CronArchiveTest extends IntegrationTestCase
{
/**
* @dataProvider getTestDataForRepairInvalidationsIfNeeded
*/
public function testRepairInvalidationsIfNeededInsertsProperInvalidations(
$existingInvalidations,
$archive,
$expectedInvalidations
) {
$this->insertInvalidations($existingInvalidations);
$cronArchive = new CronArchive();
$cronArchive->init();
$cronArchive->repairInvalidationsIfNeeded($archive);
$invalidations = $this->getInvalidationsInTable(true);
$this->assertEquals($expectedInvalidations, $invalidations);
}
public function getTestDataForRepairInvalidationsIfNeeded()
{
return [
// day w/ nothing else
[
[
['idarchive' => 1, 'idsite' => 1, 'period' => 1, 'date1' => '2020-03-04', 'date2' => '2020-03-04', 'name' => 'done', 'report' => null, 'ts_invalidated' => '2020-03-04 01:00:00'],
],
['idarchive' => 1, 'idsite' => 1, 'period' => 1, 'date1' => '2020-03-04', 'date2' => '2020-03-04', 'name' => 'done', 'report' => null, 'ts_invalidated' => '2020-03-04 01:00:00'],
[
array (
'idarchive' => '1',
'idsite' => '1',
'period' => '1',
'date1' => '2020-03-04',
'date2' => '2020-03-04',
'name' => 'done',
'report' => null,
'ts_invalidated' => '2020-03-04 01:00:00',
),
array (
'idarchive' => null,
'idsite' => '1',
'period' => '2',
'date1' => '2020-03-02',
'date2' => '2020-03-08',
'name' => 'done',
'report' => null,
'ts_invalidated' => '2020-03-04 01:00:00',
),
array (
'idarchive' => null,
'idsite' => '1',
'period' => '3',
'date1' => '2020-03-01',
'date2' => '2020-03-31',
'name' => 'done',
'report' => null,
'ts_invalidated' => '2020-03-04 01:00:00',
),
array (
'idarchive' => null,
'idsite' => '1',
'period' => '4',
'date1' => '2020-01-01',
'date2' => '2020-12-31',
'name' => 'done',
'report' => null,
'ts_invalidated' => '2020-03-04 01:00:00',
),
],
],
// week with nothing else
[
[
['idarchive' => 1, 'idsite' => 1, 'period' => 2, 'date1' => '2020-03-02', 'date2' => '2020-03-08', 'name' => 'done', 'report' => null, 'ts_invalidated' => '2020-03-04 01:00:00'],
['idarchive' => 1, 'idsite' => 1, 'period' => 2, 'date1' => '2020-03-02', 'date2' => '2020-03-08', 'name' => 'done', 'report' => null, 'ts_invalidated' => '2020-03-04 01:00:00', 'status' => 1], // duplicate w/ status = 1
],
['idarchive' => 1, 'idsite' => 1, 'period' => 2, 'date1' => '2020-03-02', 'date2' => '2020-03-08', 'name' => 'done', 'report' => null, 'ts_invalidated' => '2020-03-04 01:00:00'],
[
array (
'idarchive' => '1',
'idsite' => '1',
'period' => '2',
'date1' => '2020-03-02',
'date2' => '2020-03-08',
'name' => 'done',
'report' => null,
'ts_invalidated' => '2020-03-04 01:00:00',
),
array ( // status = 1 version
'idarchive' => '1',
'idsite' => '1',
'period' => '2',
'date1' => '2020-03-02',
'date2' => '2020-03-08',
'name' => 'done',
'report' => null,
'ts_invalidated' => '2020-03-04 01:00:00',
),
array (
'idarchive' => null,
'idsite' => '1',
'period' => '3',
'date1' => '2020-03-01',
'date2' => '2020-03-31',
'name' => 'done',
'report' => null,
'ts_invalidated' => '2020-03-04 01:00:00',
),
array (
'idarchive' => null,
'idsite' => '1',
'period' => '4',
'date1' => '2020-01-01',
'date2' => '2020-12-31',
'name' => 'done',
'report' => null,
'ts_invalidated' => '2020-03-04 01:00:00',
),
],
],
// week w/ month
[
[
['idarchive' => 1, 'idsite' => 1, 'period' => 3, 'date1' => '2020-03-01', 'date2' => '2020-03-31', 'name' => 'done', 'report' => null, 'ts_invalidated' => '2020-03-04 01:00:00'],
['idarchive' => 1, 'idsite' => 1, 'period' => 2, 'date1' => '2020-03-02', 'date2' => '2020-03-08', 'name' => 'done', 'report' => null, 'ts_invalidated' => '2020-03-04 01:00:00'],
],
['idarchive' => 1, 'idsite' => 1, 'period' => 2, 'date1' => '2020-03-02', 'date2' => '2020-03-08', 'name' => 'done', 'report' => null, 'ts_invalidated' => '2020-03-04 01:00:00'],
[
array (
'idarchive' => '1',
'idsite' => '1',
'period' => '3',
'date1' => '2020-03-01',
'date2' => '2020-03-31',
'name' => 'done',
'report' => null,
'ts_invalidated' => '2020-03-04 01:00:00',
),
array (
'idarchive' => '1',
'idsite' => '1',
'period' => '2',
'date1' => '2020-03-02',
'date2' => '2020-03-08',
'name' => 'done',
'report' => null,
'ts_invalidated' => '2020-03-04 01:00:00',
),
array (
'idarchive' => null,
'idsite' => '1',
'period' => '4',
'date1' => '2020-01-01',
'date2' => '2020-12-31',
'name' => 'done',
'report' => null,
'ts_invalidated' => '2020-03-04 01:00:00',
),
],
],
// week on edge of month w/ nothing else
[
[
['idarchive' => 1, 'idsite' => 1, 'period' => 2, 'date1' => '2020-02-24', 'date2' => '2020-03-01', 'name' => 'done', 'report' => null, 'ts_invalidated' => '2020-03-04 01:00:00'],
],
['idarchive' => 1, 'idsite' => 1, 'period' => 2, 'date1' => '2020-02-24', 'date2' => '2020-03-01', 'name' => 'done', 'report' => null, 'ts_invalidated' => '2020-03-04 01:00:00'],
[
array (
'idarchive' => '1',
'idsite' => '1',
'period' => '2',
'date1' => '2020-02-24',
'date2' => '2020-03-01',
'name' => 'done',
'report' => null,
'ts_invalidated' => '2020-03-04 01:00:00',
),
array (
'idarchive' => null,
'idsite' => '1',
'period' => '4',
'date1' => '2020-01-01',
'date2' => '2020-12-31',
'name' => 'done',
'report' => null,
'ts_invalidated' => '2020-03-04 01:00:00',
),
],
],
// week for report w/ some other similar archives
[
[
['idarchive' => 1, 'idsite' => 1, 'period' => 2, 'date1' => '2020-03-02', 'date2' => '2020-03-08', 'name' => 'done.MyPlugin', 'report' => 'myReport', 'ts_invalidated' => '2020-03-04 03:04:04'],
['idarchive' => 1, 'idsite' => 1, 'period' => 2, 'date1' => '2020-03-02', 'date2' => '2020-03-08', 'name' => 'done.MyPlugin', 'report' => 'myOtherReport', 'ts_invalidated' => '2020-03-04 03:04:04'],
['idarchive' => 1, 'idsite' => 1, 'period' => 2, 'date1' => '2020-03-02', 'date2' => '2020-03-08', 'name' => 'done.MyOtherPlugin', 'report' => 'myReport', 'ts_invalidated' => '2020-03-04 03:04:04'],
['idarchive' => 1, 'idsite' => 1, 'period' => 2, 'date1' => '2020-03-02', 'date2' => '2020-03-08', 'name' => 'done', 'report' => null, 'ts_invalidated' => '2020-03-04 03:04:04'],
],
['idarchive' => 1, 'idsite' => 1, 'period' => 2, 'date1' => '2020-03-02', 'date2' => '2020-03-08', 'name' => 'done.MyPlugin', 'report' => 'myReport', 'ts_invalidated' => '2020-03-04 03:04:04'],
[
array (
'idarchive' => '1',
'idsite' => '1',
'period' => '2',
'date1' => '2020-03-02',
'date2' => '2020-03-08',
'name' => 'done.MyPlugin',
'report' => 'myReport',
'ts_invalidated' => '2020-03-04 03:04:04',
),
array (
'idarchive' => '1',
'idsite' => '1',
'period' => '2',
'date1' => '2020-03-02',
'date2' => '2020-03-08',
'name' => 'done.MyPlugin',
'report' => 'myOtherReport',
'ts_invalidated' => '2020-03-04 03:04:04',
),
array (
'idarchive' => '1',
'idsite' => '1',
'period' => '2',
'date1' => '2020-03-02',
'date2' => '2020-03-08',
'name' => 'done.MyOtherPlugin',
'report' => 'myReport',
'ts_invalidated' => '2020-03-04 03:04:04',
),
array (
'idarchive' => '1',
'idsite' => '1',
'period' => '2',
'date1' => '2020-03-02',
'date2' => '2020-03-08',
'name' => 'done',
'report' => null,
'ts_invalidated' => '2020-03-04 03:04:04',
),
array (
'idarchive' => null,
'idsite' => '1',
'period' => '3',
'date1' => '2020-03-01',
'date2' => '2020-03-31',
'name' => 'done.MyPlugin',
'report' => 'myReport',
'ts_invalidated' => '2020-03-04 03:04:04',
),
array (
'idarchive' => null,
'idsite' => '1',
'period' => '4',
'date1' => '2020-01-01',
'date2' => '2020-12-31',
'name' => 'done.MyPlugin',
'report' => 'myReport',
'ts_invalidated' => '2020-03-04 03:04:04',
),
],
],
// week split across two years - make sure the year invalidation isn't changed to the week start year
[
[
['idarchive' => 1, 'idsite' => 1, 'period' => 2, 'date1' => '2021-12-27', 'date2' => '2022-01-02', 'name' => 'done', 'report' => null, 'ts_invalidated' => '2022-03-04 01:00:00'],
['idarchive' => 1, 'idsite' => 1, 'period' => 4, 'date1' => '2022-01-01', 'date2' => '2022-12-31', 'name' => 'done', 'report' => null, 'ts_invalidated' => '2022-03-04 01:00:00'],
],
['idarchive' => 1, 'idsite' => 1, 'period' => 2, 'date1' => '2021-12-27', 'date2' => '2022-01-02', 'name' => 'done', 'report' => null, 'ts_invalidated' => '2022-03-04 01:00:00'],
[
array (
'idarchive' => '1',
'idsite' => '1',
'period' => '2',
'date1' => '2021-12-27',
'date2' => '2022-01-02',
'name' => 'done',
'report' => null,
'ts_invalidated' => '2022-03-04 01:00:00',
),
array (
'idarchive' => 1,
'idsite' => '1',
'period' => '4',
'date1' => '2022-01-01',
'date2' => '2022-12-31',
'name' => 'done',
'report' => null,
'ts_invalidated' => '2022-03-04 01:00:00',
),
],
],
];
}
/**
* @dataProvider getTestDataForInvalidateRecentDate
*/
public function testInvalidateRecentDateInvalidatesCorrectPeriodsAndSegments(
$dateStr,
$segments,
$expectedInvalidationCalls
) {
$idSite = Fixture::createWebsite('2019-04-04 03:45:45', 0, false, false, 1, null, null, 'Australia/Sydney');
Rules::setBrowserTriggerArchiving(false);
foreach ($segments as $idx => $segment) {
SegmentAPI::getInstance()->add('segment #' . $idx, $segment, $idx % 2 === 0 ? $idSite : false, true, true);
}
Rules::setBrowserTriggerArchiving(true);
$t = Fixture::getTracker($idSite, Date::yesterday()->addHour(2)->getDatetime());
$t->setUrl('http://someurl.com/abc');
Fixture::checkResponse($t->doTrackPageView('some page'));
$t = Fixture::getTracker($idSite, Date::today()->addHour(2)->getDatetime());
$t->setUrl('http://someurl.com/def');
Fixture::checkResponse($t->doTrackPageView('some page 2'));
$mockInvalidateApi = $this->getMockInvalidateApi();
$archiver = new CronArchive();
$archiver->init();
$archiver->setApiToInvalidateArchivedReport($mockInvalidateApi);
$archiver->invalidateRecentDate($dateStr, $idSite);
$actualInvalidationCalls = $mockInvalidateApi->getInvalidations();
$this->assertEquals($expectedInvalidationCalls, $actualInvalidationCalls);
}
public function getTestDataForInvalidateRecentDate()
{
$segments = [
'browserCode==IE',
'visitCount>5',
];
return [
[
'today',
$segments,
[
array (
1,
'2020-02-03',
'day',
false,
false,
false,
),
array (
1,
'2020-02-03',
'day',
'visitCount>5',
false,
false,
),
array (
1,
'2020-02-03',
'day',
'browserCode==IE',
false,
false,
),
],
],
[
'yesterday',
$segments,
[
array (
1,
'2020-02-02',
'day',
false,
false,
false,
),
array (
1,
'2020-02-02',
'day',
'visitCount>5',
false,
false,
),
array (
1,
'2020-02-02',
'day',
'browserCode==IE',
false,
false,
),
],
],
];
}
private function getMockInvalidateApi()
{
$mock = new class {
private $calls = [];
public function invalidateArchivedReports()
{
$this->calls[] = func_get_args();
}
public function getInvalidations()
{
return $this->calls;
}
};
return $mock;
}
/**
* @dataProvider getInvalidateYesterdayTestData
*/
public function testInvalidateRecentDateForYesterdayIsSkippedWhenAlreadyInProgress($segmentsToCreate, $timezone, $nowTs, $existingInvalidations, $expectedInvalidationCalls)
{
$idSite = Fixture::createWebsite('2019-04-04 03:45:45', 0, false, false, 1, null, null, $timezone);
Rules::setBrowserTriggerArchiving(false);
foreach ($segmentsToCreate as $segment) {
SegmentAPI::getInstance()->add($segment, $segment, 1, true, true);
}
Rules::setBrowserTriggerArchiving(true);
$offset = Date::getUtcOffset($timezone);
Date::$now = $nowTs;
$this->insertInvalidations($existingInvalidations);
$t = Fixture::getTracker($idSite, Date::yesterday()->addHour(2)->getDatetime());
$t->setUrl('http://someurl.com/abc');
Fixture::checkResponse($t->doTrackPageView('some page'));
$t = Fixture::getTracker($idSite, Date::today()->addHour(2)->getDatetime());
$t->setUrl('http://someurl.com/def');
Fixture::checkResponse($t->doTrackPageView('some page 2'));
$mockInvalidateApi = $this->getMockInvalidateApi();
$archiver = new CronArchive();
$archiver->init();
$archiver->setApiToInvalidateArchivedReport($mockInvalidateApi);
$archiver->invalidateRecentDate('yesterday', $idSite);
$actualInvalidationCalls = $mockInvalidateApi->getInvalidations();
$this->assertEquals($expectedInvalidationCalls, $actualInvalidationCalls);
}
public function getInvalidateYesterdayTestData()
{
$timezones = [
'UTC-12',
'America/Caracas', // UTC-4
'UTC-0.5',
'UTC',
'Asia/Kathmandu', // UTC+5:45
'Australia/Brisbane', // UTC+10
'UTC+14',
];
foreach ($timezones as $timezone) {
$offset = Date::getUtcOffset($timezone);
yield "invalidating yesterday all visits should be skipped if archiving was started after midnight in sites timezone ($timezone)" => [
[],
$timezone,
Date::factory('2020-02-03 04:05:06')->subSeconds($offset)->getTimestamp(),
[
[
'idarchive' => 1,
'idsite' => 1,
'period' => 1,
'date1' => '2020-02-02',
'date2' => '2020-02-02',
'name' => 'done',
'report' => null,
'ts_invalidated' => '2020-02-02 21:00:00',
'status' => 1,
'ts_started' => Date::factory('2020-02-03 00:12:33')->subSeconds($offset)->getDatetime()
],
],
[],
];
yield "invalidating yesterday all visits should not be skipped if archiving for a segment was started after midnight in sites timezone ($timezone)" => [
['actions>=1'],
$timezone,
Date::factory('2020-02-03 04:05:06')->subSeconds($offset)->getTimestamp(),
[
[
'idarchive' => 1,
'idsite' => 1,
'period' => 1,
'date1' => '2020-02-02',
'date2' => '2020-02-02',
'name' => 'done' . md5('actions>=1'),
'report' => null,
'ts_invalidated' => '2020-02-02 21:00:00',
'status' => 1,
'ts_started' => Date::factory('2020-02-03 00:12:33')->subSeconds($offset)->getDatetime()
],
],
[
[
1,
'2020-02-02',
'day',
false,
false,
false,
],
],
];
yield "invalidating yesterdays segments should not be skipped even if archiving all visits was started after midnight in sites timezone ($timezone)" => [
['actions>=1', 'actions>=2',],
$timezone,
Date::factory('2020-02-03 04:05:06')->subSeconds($offset)->getTimestamp(),
[
[
'idarchive' => 1,
'idsite' => 1,
'period' => 1,
'date1' => '2020-02-02',
'date2' => '2020-02-02',
'name' => 'done',
'report' => null,
'ts_invalidated' => '2020-02-02 21:00:00',
'status' => 1,
'ts_started' => Date::factory('2020-02-03 00:12:33')->subSeconds($offset)->getDatetime()
],
],
[
[
1,
'2020-02-02',
'day',
'actions>=1',
false,
false,
],
[
1,
'2020-02-02',
'day',
'actions>=2',
false,
false,
],
],
];
yield "invalidating yesterdays segments should be skipped if an archiving for it was started after midnight in sites timezone ($timezone)" => [
['actions>=1', 'actions>=2',],
$timezone,
Date::factory('2020-02-03 04:05:06')->subSeconds($offset)->getTimestamp(),
[
[
'idarchive' => 1,
'idsite' => 1,
'period' => 1,
'date1' => '2020-02-02',
'date2' => '2020-02-02',
'name' => 'done',
'report' => null,
'ts_invalidated' => '2020-02-02 21:00:00',
'status' => 1,
'ts_started' => Date::factory('2020-02-03 00:12:33')->subSeconds($offset)->getDatetime()
],
[
'idarchive' => 1,
'idsite' => 1,
'period' => 1,
'date1' => '2020-02-02',
'date2' => '2020-02-02',
'name' => 'done' . md5('actions>=2'),
'report' => null,
'ts_invalidated' => '2020-02-02 21:00:00',
'status' => 1,
'ts_started' => Date::factory('2020-02-03 00:12:33')->subSeconds($offset)->getDatetime()
],
],
[
[
1,
'2020-02-02',
'day',
'actions>=1',
false,
false,
],
],
];
yield "invalidating yesterday all visits should not be skipped if archiving was started before midnight in sites timezone ($timezone)" => [
[],
$timezone,
Date::factory('2020-02-03 04:05:06')->subSeconds($offset)->getTimestamp(),
[
[
'idarchive' => 1,
'idsite' => 1,
'period' => 1,
'date1' => '2020-02-02',
'date2' => '2020-02-02',
'name' => 'done',
'report' => null,
'ts_invalidated' => '2020-02-02 21:00:00',
'status' => 1,
'ts_started' => Date::factory('2020-02-02 23:48:33')->subSeconds($offset)->getDatetime()
],
],
[
[
1,
'2020-02-02',
'day',
false,
false,
false,
],
],
];
yield "invalidating yesterdays segment should not be skipped if archiving was started before midnight in sites timezone ($timezone)" => [
['actions>=1'],
$timezone,
Date::factory('2020-02-03 04:05:06')->subSeconds($offset)->getTimestamp(),
[
[
'idarchive' => 1,
'idsite' => 1,
'period' => 1,
'date1' => '2020-02-02',
'date2' => '2020-02-02',
'name' => 'done' . md5('actions>=1'),
'report' => null,
'ts_invalidated' => '2020-02-02 21:00:00',
'status' => 1,
'ts_started' => Date::factory('2020-02-02 23:48:33')->subSeconds($offset)->getDatetime()
],
],
[
[
1,
'2020-02-02',
'day',
false,
false,
false,
],
[
1,
'2020-02-02',
'day',
'actions>=1',
false,
false,
],
],
];
yield "invalidating yesterdays data should be skipped correctly for multiple segments with various in progress invalidations in sites timezone ($timezone)" => [
['actions>=1', 'actions>=2', 'actions>=3'],
$timezone,
Date::factory('2020-02-03 04:05:06')->subSeconds($offset)->getTimestamp(),
[
[
'idarchive' => 3,
'idsite' => 1,
'period' => 3,
'date1' => '2020-02-01',
'date2' => '2020-02-29',
'name' => 'done' . md5('actions>=1'),
'report' => null,
'ts_invalidated' => '2020-02-02 21:00:00',
'status' => 1,
'ts_started' => Date::factory('2020-02-02 23:48:33')->subSeconds($offset)->getDatetime()
], // different period, so should be ignored
[
'idarchive' => 1,
'idsite' => 1,
'period' => 1,
'date1' => '2020-02-02',
'date2' => '2020-02-02',
'name' => 'done' . md5('actions>=1'),
'report' => null,
'ts_invalidated' => '2020-02-02 21:00:00',
'status' => 1,
'ts_started' => Date::factory('2020-02-02 23:48:33')->subSeconds($offset)->getDatetime()
], // started too early, so should be ignored
[
'idarchive' => 1,
'idsite' => 1,
'period' => 1,
'date1' => '2020-02-02',
'date2' => '2020-02-02',
'name' => 'done' . md5('actions>=2') . '.MyPlugin',
'report' => null,
'ts_invalidated' => '2020-02-02 21:00:00',
'status' => 1,
'ts_started' => Date::factory('2020-02-03 00:10:33')->subSeconds($offset)->getDatetime()
], // partial archive, so should be ignored
[
'idarchive' => 2,
'idsite' => 2,
'period' => 1,
'date1' => '2020-02-02',
'date2' => '2020-02-02',
'name' => 'done' . md5('actions>=2'),
'report' => null,
'ts_invalidated' => '2020-02-02 21:00:00',
'status' => 1,
'ts_started' => Date::factory('2020-02-03 00:10:33')->subSeconds($offset)->getDatetime()
], // different site, so should be ignored
[
'idarchive' => 2,
'idsite' => 1,
'period' => 1,
'date1' => '2020-02-02',
'date2' => '2020-02-02',
'name' => 'done' . md5('actions>=3'),
'report' => null,
'ts_invalidated' => '2020-02-02 21:00:00',
'status' => 1,
'ts_started' => Date::factory('2020-02-03 00:10:33')->subSeconds($offset)->getDatetime()
], // should be considered and invalidation skipped
],
[
[
1,
'2020-02-02',
'day',
false,
false,
false,
],
[
1,
'2020-02-02',
'day',
'actions>=1',
false,
false,
],
[
1,
'2020-02-02',
'day',
'actions>=2',
false,
false,
],
],
];
}
}
/**
* @dataProvider getArchivingTestData
*/
public function testCanWeSkipInvalidatingBecauseThereIsAUsablePeriodReturnsExpectedValue(
string $timezone,
string $nowDateTime,
string $dayToArchive,
string $periodToArchive,
string $tsArchived,
int $archiveStatus,
bool $expected
) {
Rules::setBrowserTriggerArchiving(false);
Fixture::createWebsite('2019-04-04 03:45:45', 0, false, false, 1, 0, 0, $timezone);
Date::$now = strtotime($nowDateTime);
if ($dayToArchive === 'yesterday' || $dayToArchive === 'today') {
$dateToArchive = Date::factoryInTimezone($dayToArchive, $timezone)->toString();
} else {
$dateToArchive = $dayToArchive;
}
$period = Factory::build($periodToArchive, $dateToArchive);
$archiver = new CronArchive();
$params = new Parameters(new Site(1), $period, new Segment('', [1]));
$archiveTable = ArchiveTableCreator::getNumericTable($period->getDateStart());
Db::query("INSERT INTO $archiveTable (idarchive, idsite, period, date1, date2, name, value, ts_archived) VALUES (?, ?, ?, ?, ?, ?, ?, ?)", [
1, 1, $period::PERIOD_ID, $period->getDateStart()->toString(), $period->getDateEnd()->toString(), 'done', $archiveStatus, $tsArchived
]);
// $skipWhenRunningOrNewEnoughArchiveExists is set to true when running invalidateRecentDate('yesterday');
$class = new \ReflectionClass(CronArchive::class);
$method = $class->getMethod('canWeSkipInvalidatingBecauseThereIsAUsablePeriod');
$method->setAccessible(true);
$actual = $method->invoke($archiver, $params, $dayToArchive === 'yesterday');
$this->assertSame($expected, $actual);
}
public function getArchivingTestData(): iterable
{
$timezones = [
'UTC-12',
'America/Caracas', // UTC-4
'UTC-0.5',
'UTC',
'Asia/Kathmandu', // UTC+5:45
'Australia/Brisbane', // UTC+10
'UTC+14',
];
foreach ($timezones as $timezone) {
$offset = Date::getUtcOffset($timezone);
yield "Invalidating yesterday should not be skipped if an archive for yesterday was built before midnight in site's timezone ($timezone)" => [
$timezone,
Date::factory('2020-04-05 00:22:00')->subSeconds($offset)->getDatetime(),
'yesterday',
'day',
Date::factory('2020-04-04 23:45:40')->subSeconds($offset)->getDatetime(),
ArchiveWriter::DONE_OK,
false
];
yield "Invalidating yesterday should not be skipped if an archive for yesterday was built some time before midnight in site's timezone ($timezone)" => [
$timezone,
Date::factory('2020-04-05 06:22:00')->subSeconds($offset)->getDatetime(),
'yesterday',
'day',
Date::factory('2020-04-04 18:25:35')->subSeconds($offset)->getDatetime(),
ArchiveWriter::DONE_OK,
false
];
yield "Invalidating yesterday should not be skipped if an archive for yesterday was built long before midnight in site's timezone ($timezone)" => [
$timezone,
Date::factory('2020-04-05 19:22:00')->subSeconds($offset)->getDatetime(),
'yesterday',
'day',
Date::factory('2020-04-04 09:25:35')->subSeconds($offset)->getDatetime(),
ArchiveWriter::DONE_OK,
false
];
yield "Invalidating yesterday should be skipped if an archive for yesterday was built after midnight in site's timezone ($timezone)" => [
$timezone,
Date::factory('2020-04-05 00:22:00')->subSeconds($offset)->getDatetime(),
'yesterday',
'day',
Date::factory('2020-04-05 00:05:40')->subSeconds($offset)->getDatetime(),
ArchiveWriter::DONE_OK,
true
];
yield "Invalidating yesterday should be skipped if an archive for yesterday was built some time after midnight in site's timezone ($timezone)" => [
$timezone,
Date::factory('2020-04-05 16:22:00')->subSeconds($offset)->getDatetime(),
'yesterday',
'day',
Date::factory('2020-04-05 09:05:40')->subSeconds($offset)->getDatetime(),
ArchiveWriter::DONE_OK,
true
];
yield "Invalidating yesterday should be skipped if an archive for yesterday was built long after midnight in site's timezone ($timezone)" => [
$timezone,
Date::factory('2020-04-05 22:22:00')->subSeconds($offset)->getDatetime(),
'yesterday',
'day',
Date::factory('2020-04-05 19:05:40')->subSeconds($offset)->getDatetime(),
ArchiveWriter::DONE_OK,
true
];
yield "Invalidation should be skipped when checking an older date that was archived within ttl, as invalidation will be processed later ($timezone)" => [
$timezone,
Date::factory('2020-04-05 00:00:00')->subSeconds($offset)->getDatetime(),
'2020-03-05',
'day',
Date::factory('2020-04-04 23:49:44')->subSeconds($offset)->getDatetime(),
ArchiveWriter::DONE_OK,
true
];
yield "Invalidation should not be skipped when checking an older period that was not archived within ttl ($timezone)" => [
$timezone,
Date::factory('2020-04-05 04:00:00')->subSeconds($offset)->getDatetime(),
'2020-03-05',
'week',
Date::factory('2020-04-03 23:49:44')->subSeconds($offset)->getDatetime(),
ArchiveWriter::DONE_OK,
false
];
yield "Invalidation should be skipped when checking an older period that was archived within ttl, as invalidation will be processed later ($timezone)" => [
$timezone,
Date::factory('2020-04-05 04:00:00')->subSeconds($offset)->getDatetime(),
'2020-03-05',
'week',
Date::factory('2020-04-05 03:55:44')->subSeconds($offset)->getDatetime(),
ArchiveWriter::DONE_OK,
true
];
// ttl is defined by time_before_today_archive_considered_outdated (default = 900)
yield "Invalidating today should be skipped when checking today archive, which is newer than ttl, as invalidation will be processed later ($timezone)" => [
$timezone,
Date::factory('2020-04-05 19:15:00')->subSeconds($offset)->getDatetime(),
'today',
'day',
Date::factory('2020-04-05 19:05:00')->subSeconds($offset)->getDatetime(),
ArchiveWriter::DONE_OK,
true
];
// ttl is defined by time_before_today_archive_considered_outdated (default = 900)
yield "Invalidating today should not be skipped when checking today archive, which is older than ttl ($timezone)" => [
$timezone,
Date::factory('2020-04-05 19:15:00')->subSeconds($offset)->getDatetime(),
'today',
'day',
Date::factory('2020-04-05 16:05:00')->subSeconds($offset)->getDatetime(),
ArchiveWriter::DONE_OK,
false
];
yield "Invalidating current week should be skipped if a recently built archive is valid ($timezone)" => [
$timezone,
Date::factory('2020-04-05 19:15:00')->subSeconds($offset)->getDatetime(),
'today',
'week',
Date::factory('2020-04-05 19:13:40')->subSeconds($offset)->getDatetime(),
ArchiveWriter::DONE_OK,
true
];
yield "Invalidating current week should also be skipped if a recently built archive is already invalidated ($timezone)" => [
$timezone,
Date::factory('2020-04-05 19:15:00')->subSeconds($offset)->getDatetime(),
'today',
'week',
Date::factory('2020-04-05 19:13:40')->subSeconds($offset)->getDatetime(),
ArchiveWriter::DONE_INVALIDATED,
true
];
yield "Invalidating current week should not be skipped if a recently built archive is older than ttl ($timezone)" => [
$timezone,
Date::factory('2020-04-05 19:15:00')->subSeconds($offset)->getDatetime(),
'today',
'week',
Date::factory('2020-04-05 18:28:40')->subSeconds($offset)->getDatetime(),
ArchiveWriter::DONE_OK,
false
];
}
}
public function testGetColumnNamesFromTable()
{
Fixture::createWebsite('2014-12-12 00:01:02');
Fixture::createWebsite('2014-12-12 00:01:02');
$ar = StaticContainer::get('Piwik\Archive\ArchiveInvalidator');
$ar->rememberToInvalidateArchivedReportsLater(1, Date::factory('2014-04-05'));
$ar->rememberToInvalidateArchivedReportsLater(2, Date::factory('2014-04-05'));
$ar->rememberToInvalidateArchivedReportsLater(2, Date::factory('2014-04-06'));
$api = API::getInstance();
$cronarchive = new TestCronArchive();
$cronarchive->skipInvalidatingRecentDates = true;
$cronarchive->init();
$cronarchive->setApiToInvalidateArchivedReport($api);
$cronarchive->invalidateArchivedReportsForSitesThatNeedToBeArchivedAgain(1);
$cronarchive->invalidateArchivedReportsForSitesThatNeedToBeArchivedAgain(2);
/**
* should look like this but the result is random
* array(
array(array(1,2), '2014-04-05'),
array(array(2), '2014-04-06')
)
*/
$invalidatedReports = $api->getInvalidatedReports();
$this->assertCount(3, $invalidatedReports);
usort($invalidatedReports, function ($a, $b) {
return strcmp($a[1], $b[1]);
});
$this->assertSame(1, $invalidatedReports[0][0]);
$this->assertSame('2014-04-05', $invalidatedReports[0][1]);
$this->assertSame(2, $invalidatedReports[1][0]);
$this->assertSame('2014-04-05', $invalidatedReports[1][1]);
$this->assertSame(2, $invalidatedReports[2][0]);
$this->assertSame('2014-04-06', $invalidatedReports[2][1]);
}
public function testWasSegmentCreatedRecently()
{
Fixture::createWebsite('2014-12-12 00:01:02');
Rules::setBrowserTriggerArchiving(false);
SegmentAPI::getInstance()->add('foo', 'actions>=1', 1, true, true);
$id = SegmentAPI::getInstance()->add('barb', 'actions>=2', 1, true, true);
Rules::setBrowserTriggerArchiving(true);
$segments = new Model();
$segments->updateSegment($id, array('ts_created' => Date::now()->subHour(30)->getDatetime()));
$allSegments = $segments->getSegmentsToAutoArchive(1);
$cronarchive = new TestCronArchive();
$this->assertTrue($cronarchive->wasSegmentChangedRecently('actions>=1', $allSegments));
// created 30 hours ago...
$this->assertFalse($cronarchive->wasSegmentChangedRecently('actions>=2', $allSegments));
// not configured segment
$this->assertFalse($cronarchive->wasSegmentChangedRecently('actions>=999', $allSegments));
}
public function testSkipSegmentsTodayDoesNotRequestAnySegmentInvalidationsForToday()
{
Date::$now = strtotime('2020-09-09 09:00:00');
Fixture::createWebsite('2014-12-12 00:01:02');
// track a visit for today, so todays data will get invalidated
$tracker = Fixture::getTracker(1, '2020-09-09 07:00:00');
$tracker->setUrl('http://someurl.com');
Fixture::checkResponse($tracker->doTrackPageView('abcdefg'));
// remove invalidation options created through tracking
Option::deleteLike('%report_to_invalidate_%');
Rules::setBrowserTriggerArchiving(false);
$id1 = SegmentAPI::getInstance()->add('foo', 'actions>=1', 1, true, true);
$id2 = SegmentAPI::getInstance()->add('barb', 'actions>=2', 1, true, true);
Rules::setBrowserTriggerArchiving(true);
$segments = new Model();
$segments->updateSegment($id1, array('ts_created' => Date::now()->subHour(300)->getDatetime()));
$segments->updateSegment($id2, array('ts_created' => Date::now()->subHour(30)->getDatetime()));
$logger = new FakeLogger();
$mockInvalidateApi = $this->getMockInvalidateApi();
$archiver = new CronArchive($logger);
$archiver->init();
$archiver->setApiToInvalidateArchivedReport($mockInvalidateApi);
$archiveFilter = new CronArchive\ArchiveFilter();
$archiveFilter->setSkipSegmentsForToday(true);
$archiver->setArchiveFilter($archiveFilter);
$archiver->shouldArchiveAllSites = true;
$archiver->init();
$archiver->run();
// check that no segment invalidations were requested
$requestedInvalidations = $mockInvalidateApi->getInvalidations();
$expectedInvalidations = [
[
1,
Date::now()->toString(),
'day',
false,
false,
false,
],
[
1,
Date::yesterday()->toString(),
'day',
false,
false,
false,
],
[
1,
Date::yesterday()->toString(),
'day',
'actions>=1',
false,
false,
],
[
1,
Date::yesterday()->toString(),
'day',
'actions>=2',
false,
false,
],
];
self::assertEquals($expectedInvalidations, $requestedInvalidations);
self::assertStringContainsString('Will skip segments archiving for today unless they were created recently', $logger->output);
}
public function testSkipSegmentsTodayDoesStillRequestSegmentInvalidationsForRecentlyCreatedSegments()
{
Date::$now = strtotime('2020-09-09 09:00:00');
Fixture::createWebsite('2014-12-12 00:01:02');
// track a visit for today, so todays data will get invalidated
$tracker = Fixture::getTracker(1, '2020-09-09 07:00:00');
$tracker->setUrl('http://someurl.com');
Fixture::checkResponse($tracker->doTrackPageView('abcdefg'));
// remove invalidation options created through tracking
Option::deleteLike('%report_to_invalidate_%');
Rules::setBrowserTriggerArchiving(false);
$id1 = SegmentAPI::getInstance()->add('foo', 'actions>=1', 1, true, true);
$id2 = SegmentAPI::getInstance()->add('barb', 'actions>=2', 1, true, true);
Rules::setBrowserTriggerArchiving(true);
$segments = new Model();
$segments->updateSegment($id1, array('ts_created' => Date::now()->subHour(300)->getDatetime()));
$segments->updateSegment($id2, array('ts_created' => Date::now()->subHour(12)->getDatetime()));
$logger = new FakeLogger();
$mockInvalidateApi = $this->getMockInvalidateApi();
$archiver = new CronArchive($logger);
$archiver->init();
$archiver->setApiToInvalidateArchivedReport($mockInvalidateApi);
$archiveFilter = new CronArchive\ArchiveFilter();
$archiveFilter->setSkipSegmentsForToday(true);
$archiver->setArchiveFilter($archiveFilter);
$archiver->shouldArchiveAllSites = true;
$archiver->init();
$archiver->run();
// check that no segment invalidations were requested
$requestedInvalidations = $mockInvalidateApi->getInvalidations();
$expectedInvalidations = [
[
1,
Date::now()->toString(),
'day',
false,
false,
false,
],
[
1,
Date::now()->toString(),
'day',
'actions>=2',
false,
false,
],
[
1,
Date::yesterday()->toString(),
'day',
false,
false,
false,
],
[
1,
Date::yesterday()->toString(),
'day',
'actions>=1',
false,
false,
],
[
1,
Date::yesterday()->toString(),
'day',
'actions>=2',
false,
false,
],
];
self::assertEquals($expectedInvalidations, $requestedInvalidations);
self::assertStringContainsString('Will skip segments archiving for today unless they were created recently', $logger->output);
}
public function testInvalidatingYesterdayWillStillRequestSegmentInvalidationsWithSkipSegmentsToday()
{
Date::$now = strtotime('2020-08-05 09:00:00');
Fixture::createWebsite('2014-12-12 00:01:02');
// track a visit for yesterday, so yesterdays data will get invalidated
$tracker = Fixture::getTracker(1, '2020-08-04 16:00:00');
$tracker->setUrl('http://someurl.com');
Fixture::checkResponse($tracker->doTrackPageView('abcdefg'));
// track a visit for today, so todays data will get invalidated
$tracker = Fixture::getTracker(1, '2020-08-05 07:00:00');
$tracker->setUrl('http://someurl.com');
Fixture::checkResponse($tracker->doTrackPageView('abcdefg'));
// remove invalidation options created through tracking
Option::deleteLike('%report_to_invalidate_%');
Rules::setBrowserTriggerArchiving(false);
$id1 = SegmentAPI::getInstance()->add('foo', 'actions>=1', 1, true, true);
$id2 = SegmentAPI::getInstance()->add('barb', 'actions>=2', 1, true, true);
Rules::setBrowserTriggerArchiving(true);
$segments = new Model();
$segments->updateSegment($id1, array('ts_created' => Date::now()->subHour(120)->getDatetime()));
$segments->updateSegment($id2, array('ts_created' => Date::now()->subHour(120)->getDatetime()));
$logger = new FakeLogger();
$mockInvalidateApi = $this->getMockInvalidateApi();
$archiver = new CronArchive($logger);
$archiver->init();
$archiver->setApiToInvalidateArchivedReport($mockInvalidateApi);
$archiveFilter = new CronArchive\ArchiveFilter();
$archiveFilter->setSkipSegmentsForToday(true);
$archiver->setArchiveFilter($archiveFilter);
$archiver->shouldArchiveAllSites = true;
$archiver->init();
$archiver->run();
// check that no segment invalidations were requested
$requestedInvalidations = $mockInvalidateApi->getInvalidations();
$expectedInvalidations = [
[
1,
'2020-08-05',
'day',
false,
false,
false,
],
[
1,
'2020-08-04',
'day',
false,
false,
false,
],
[
1,
'2020-08-04',
'day',
'actions>=1',
false,
false,
],
[
1,
'2020-08-04',
'day',
'actions>=2',
false,
false,
],
];
self::assertEquals($expectedInvalidations, $requestedInvalidations);
self::assertStringContainsString('Will skip segments archiving for today unless they were created recently', $logger->output);
}
public function testOutput()
{
\Piwik\Tests\Framework\Mock\FakeCliMulti::$specifiedResults = array(
'/method=API.get/' => json_encode(array(array('nb_visits' => 1)))
);
Fixture::createWebsite('2014-12-12 00:01:02');
Rules::setBrowserTriggerArchiving(false);
SegmentAPI::getInstance()->add('foo', 'actions>=2', 1, true, true);
SegmentAPI::getInstance()->add('burr', 'actions>=4', 1, true, true);
Rules::setBrowserTriggerArchiving(true);
// remove invalidations that were created by adding the segments
Option::delete(CronArchive\ReArchiveList::OPTION_NAME);
$tracker = Fixture::getTracker(1, '2019-12-12 02:03:00');
$tracker->setUrl('http://someurl.com');
Fixture::checkResponse($tracker->doTrackPageView('abcdefg'));
$tracker->setForceVisitDateTime('2019-12-11 03:04:05');
$tracker->setUrl('http://someurl.com/2');
Fixture::checkResponse($tracker->doTrackPageView('abcdefg2'));
$tracker->setForceVisitDateTime('2019-12-10 03:04:05');
$tracker->setUrl('http://someurl.com/3');
Fixture::checkResponse($tracker->doTrackPageView('abcdefg3'));
$tracker->setForceVisitDateTime('2019-12-02 03:04:05');
$tracker->setUrl('http://someurl.com/4');
Fixture::checkResponse($tracker->doTrackPageView('abcdefg4'));
$logger = new FakeLogger();
$archiver = new CronArchive($logger);
$archiver->skipInvalidatingRecentDates = true;
$archiveFilter = new CronArchive\ArchiveFilter();
$archiveFilter->setSegmentsToForce(['actions>=2;browserCode=FF', 'actions>=2']);
$archiver->setArchiveFilter($archiveFilter);
$archiver->init();
$archiver->run();
$version = Version::VERSION;
$expected = <<<LOG
---------------------------
INIT
Running Matomo $version as Super User
---------------------------
NOTES
- If you execute this script at least once per hour (or more often) in a crontab, you may disable 'Browser trigger archiving' in Matomo UI > Settings > General Settings.
See the doc at: https://matomo.org/docs/setup-auto-archiving/
- Async process archiving supported, using CliMulti.
- Reports for today will be processed at most every 900 seconds. You can change this value in Matomo UI > Settings > General Settings.
- Limiting segment archiving to following segments:
* actions>=2;browserCode=FF
* actions>=2
---------------------------
START
Starting Matomo reports archiving...
Applying queued rearchiving...
Start processing archives for site 1.
Checking for queued invalidations...
Will invalidate archived reports for 2019-12-12 for following websites ids: 1
Will invalidate archived reports for 2019-12-11 for following websites ids: 1
Will invalidate archived reports for 2019-12-10 for following websites ids: 1
Will invalidate archived reports for 2019-12-02 for following websites ids: 1
Done invalidating
Processing invalidation: [idinvalidation = %d, idsite = 1, period = day(2019-12-12 - 2019-12-12), name = donee0512c03f7c20af6ef96a8d792c6bb9f, segment = actions>=2].
Processing invalidation: [idinvalidation = %d, idsite = 1, period = day(2019-12-11 - 2019-12-11), name = donee0512c03f7c20af6ef96a8d792c6bb9f, segment = actions>=2].
Processing invalidation: [idinvalidation = %d, idsite = 1, period = day(2019-12-10 - 2019-12-10), name = donee0512c03f7c20af6ef96a8d792c6bb9f, segment = actions>=2].
Starting archiving for ?module=API&method=CoreAdminHome.archiveReports&idSite=1&period=day&date=2019-12-12&format=json&segment=actions%3E%3D2&trigger=archivephp
Starting archiving for ?module=API&method=CoreAdminHome.archiveReports&idSite=1&period=day&date=2019-12-11&format=json&segment=actions%3E%3D2&trigger=archivephp
Starting archiving for ?module=API&method=CoreAdminHome.archiveReports&idSite=1&period=day&date=2019-12-10&format=json&segment=actions%3E%3D2&trigger=archivephp
Archived website id 1, period = day, date = 2019-12-12, segment = 'actions>=2', 0 visits found. Time elapsed: %fs
Archived website id 1, period = day, date = 2019-12-11, segment = 'actions>=2', 0 visits found. Time elapsed: %fs
Archived website id 1, period = day, date = 2019-12-10, segment = 'actions>=2', 0 visits found. Time elapsed: %fs
Processing invalidation: [idinvalidation = %d, idsite = 1, period = week(2019-12-09 - 2019-12-15), name = donee0512c03f7c20af6ef96a8d792c6bb9f, segment = actions>=2].
Processing invalidation: [idinvalidation = %d, idsite = 1, period = day(2019-12-02 - 2019-12-02), name = donee0512c03f7c20af6ef96a8d792c6bb9f, segment = actions>=2].
No next invalidated archive.
Starting archiving for ?module=API&method=CoreAdminHome.archiveReports&idSite=1&period=week&date=2019-12-09&format=json&segment=actions%3E%3D2&trigger=archivephp
Starting archiving for ?module=API&method=CoreAdminHome.archiveReports&idSite=1&period=day&date=2019-12-02&format=json&segment=actions%3E%3D2&trigger=archivephp
Archived website id 1, period = week, date = 2019-12-09, segment = 'actions>=2', 0 visits found. Time elapsed: %fs
Archived website id 1, period = day, date = 2019-12-02, segment = 'actions>=2', 0 visits found. Time elapsed: %fs
Processing invalidation: [idinvalidation = %d, idsite = 1, period = week(2019-12-02 - 2019-12-08), name = donee0512c03f7c20af6ef96a8d792c6bb9f, segment = actions>=2].
No next invalidated archive.
Starting archiving for ?module=API&method=CoreAdminHome.archiveReports&idSite=1&period=week&date=2019-12-02&format=json&segment=actions%3E%3D2&trigger=archivephp
Archived website id 1, period = week, date = 2019-12-02, segment = 'actions>=2', 0 visits found. Time elapsed: %fs
Processing invalidation: [idinvalidation = %d, idsite = 1, period = month(2019-12-01 - 2019-12-31), name = donee0512c03f7c20af6ef96a8d792c6bb9f, segment = actions>=2].
No next invalidated archive.
Starting archiving for ?module=API&method=CoreAdminHome.archiveReports&idSite=1&period=month&date=2019-12-01&format=json&segment=actions%3E%3D2&trigger=archivephp
Archived website id 1, period = month, date = 2019-12-01, segment = 'actions>=2', 0 visits found. Time elapsed: %fs
Processing invalidation: [idinvalidation = %d, idsite = 1, period = year(2019-01-01 - 2019-12-31), name = donee0512c03f7c20af6ef96a8d792c6bb9f, segment = actions>=2].
No next invalidated archive.
Starting archiving for ?module=API&method=CoreAdminHome.archiveReports&idSite=1&period=year&date=2019-01-01&format=json&segment=actions%3E%3D2&trigger=archivephp
Archived website id 1, period = year, date = 2019-01-01, segment = 'actions>=2', 0 visits found. Time elapsed: %fs
No next invalidated archive.
Finished archiving for site 1, 8 API requests, Time elapsed: %fs [1 / 1 done]
No more sites left to archive, stopping.
Done archiving!
---------------------------
SUMMARY
Processed 8 archives.
Total API requests: 8
done: 8 req, %d ms, no error
Time elapsed: %fs
LOG;
// remove a bunch of debug lines since we can't have a sprintf format that long
$output = $this->cleanOutput($logger->output);
$this->assertStringMatchesFormat($expected, $output);
}
public function testOutputWithSkipIdSites()
{
\Piwik\Tests\Framework\Mock\FakeCliMulti::$specifiedResults = array(
'/method=API.get/' => json_encode(array(array('nb_visits' => 1)))
);
Fixture::createWebsite('2014-12-12 00:01:02');
Fixture::createWebsite('2014-12-12 00:01:02');
Fixture::createWebsite('2014-12-12 00:01:02');
$tracker = Fixture::getTracker(1, '2019-12-12 02:03:00');
$tracker->enableBulkTracking();
foreach ([1,2,3] as $idSite) {
$tracker->setIdSite($idSite);
$tracker->setUrl('http://someurl.com');
Fixture::assertTrue($tracker->doTrackPageView('abcdefg'));
$tracker->setForceVisitDateTime('2019-12-11 03:04:05');
$tracker->setUrl('http://someurl.com/2');
Fixture::assertTrue($tracker->doTrackPageView('abcdefg2'));
$tracker->setForceVisitDateTime('2019-12-10 03:04:05');
$tracker->setUrl('http://someurl.com/3');
Fixture::assertTrue($tracker->doTrackPageView('abcdefg3'));
}
$tracker->doBulkTrack();
$logger = new FakeLogger();
// prevent race condition in sequence creation during test
$sequence = new Sequence(ArchiveTableCreator::getNumericTable(Date::factory('2019-12-10')));
$sequence->create();
$archiver = new CronArchive($logger);
$archiver->skipInvalidatingRecentDates = true;
$archiveFilter = new CronArchive\ArchiveFilter();
$archiver->setArchiveFilter($archiveFilter);
$archiver->shouldSkipSpecifiedSites = [1,3];
$archiver->init();
$archiver->run();
$version = Version::VERSION;
$expected = <<<LOG
---------------------------
INIT
Running Matomo $version as Super User
---------------------------
NOTES
- If you execute this script at least once per hour (or more often) in a crontab, you may disable 'Browser trigger archiving' in Matomo UI > Settings > General Settings.
See the doc at: https://matomo.org/docs/setup-auto-archiving/
- Async process archiving supported, using CliMulti.
- Reports for today will be processed at most every 900 seconds. You can change this value in Matomo UI > Settings > General Settings.
---------------------------
START
Starting Matomo reports archiving...
Applying queued rearchiving...
Start processing archives for site 2.
Checking for queued invalidations...
Will invalidate archived reports for 2019-12-11 for following websites ids: 2
Will invalidate archived reports for 2019-12-10 for following websites ids: 2
Done invalidating
Processing invalidation: [idinvalidation = %d, idsite = 2, period = day(2019-12-11 - 2019-12-11), name = done, segment = ].
Processing invalidation: [idinvalidation = %d, idsite = 2, period = day(2019-12-10 - 2019-12-10), name = done, segment = ].
No next invalidated archive.
Starting archiving for ?module=API&method=CoreAdminHome.archiveReports&idSite=2&period=day&date=2019-12-11&format=json&trigger=archivephp
Starting archiving for ?module=API&method=CoreAdminHome.archiveReports&idSite=2&period=day&date=2019-12-10&format=json&trigger=archivephp
Archived website id 2, period = day, date = 2019-12-11, segment = '', 1 visits found. Time elapsed: %fs
Archived website id 2, period = day, date = 2019-12-10, segment = '', 1 visits found. Time elapsed: %fs
Processing invalidation: [idinvalidation = %d, idsite = 2, period = week(2019-12-09 - 2019-12-15), name = done, segment = ].
No next invalidated archive.
Starting archiving for ?module=API&method=CoreAdminHome.archiveReports&idSite=2&period=week&date=2019-12-09&format=json&trigger=archivephp
Archived website id 2, period = week, date = 2019-12-09, segment = '', 2 visits found. Time elapsed: %fs
Processing invalidation: [idinvalidation = %d, idsite = 2, period = month(2019-12-01 - 2019-12-31), name = done, segment = ].
No next invalidated archive.
Starting archiving for ?module=API&method=CoreAdminHome.archiveReports&idSite=2&period=month&date=2019-12-01&format=json&trigger=archivephp
Archived website id 2, period = month, date = 2019-12-01, segment = '', 2 visits found. Time elapsed: %fs
Processing invalidation: [idinvalidation = %d, idsite = 2, period = year(2019-01-01 - 2019-12-31), name = done, segment = ].
No next invalidated archive.
Starting archiving for ?module=API&method=CoreAdminHome.archiveReports&idSite=2&period=year&date=2019-01-01&format=json&trigger=archivephp
Archived website id 2, period = year, date = 2019-01-01, segment = '', 2 visits found. Time elapsed: %fs
No next invalidated archive.
Finished archiving for site 2, 5 API requests, Time elapsed: %fs [1 / 1 done]
No more sites left to archive, stopping.
Done archiving!
---------------------------
SUMMARY
Processed 5 archives.
Total API requests: 5
done: 5 req, %d ms, no error
Time elapsed: %fs
LOG;
// remove a bunch of debug lines since we can't have a sprintf format that long
$output = $this->cleanOutput($logger->output);
$this->assertStringMatchesFormat($expected, $output);
}
private function cleanOutput($output)
{
$output = explode("\n", $output);
$output = array_filter($output, function ($l) {
return strpos($l, 'Skipping invalidated archive') === false;
});
$output = array_filter($output, function ($l) {
return strpos($l, 'Found archive with intersecting period') === false;
});
$output = array_filter($output, function ($l) {
return strpos($l, 'Found duplicate invalidated archive') === false;
});
$output = array_filter($output, function ($l) {
return strpos($l, 'No usable archive exists') === false;
});
$output = array_filter($output, function ($l) {
return strpos($l, 'Found invalidated archive we can skip (no visits)') === false;
});
$output = implode("\n", $output);
return $output;
}
public function testShouldNotStopProcessingWhenOneSiteIsInvalid()
{
\Piwik\Tests\Framework\Mock\FakeCliMulti::$specifiedResults = array(
'/method=API.get/' => json_encode(array(array('nb_visits' => 1)))
);
Fixture::createWebsite('2014-12-12 00:01:02');
$logger = new FakeLogger();
$archiver = new CronArchive($logger);
$archiver->shouldArchiveSpecifiedSites = array(99999, 1);
$archiver->init();
$archiver->run();
$expected = <<<LOG
- Will process 2 websites (--force-idsites)
- Will process specified sites: 1
---------------------------
START
Starting Matomo reports archiving...
Applying queued rearchiving...
Start processing archives for site 1.
Checking for queued invalidations...
Will invalidate archived reports for today in site ID = 1's timezone (2020-02-03 00:00:00).
Will invalidate archived reports for yesterday in site ID = 1's timezone (2020-02-02 00:00:00).
Done invalidating
LOG;
self::assertStringContainsString($expected, $logger->output);
}
public function provideContainerConfig()
{
Date::$now = strtotime('2020-02-03 04:05:06');
return array(
'Piwik\CliMulti' => \Piwik\DI::create('Piwik\Tests\Framework\Mock\FakeCliMulti')
);
}
protected static function configureFixture($fixture)
{
parent::configureFixture($fixture);
$fixture->createSuperUser = true;
}
private function insertInvalidations(array $invalidations)
{
$now = Date::now()->getDatetime();
$table = Common::prefixTable('archive_invalidations');
foreach ($invalidations as $inv) {
$bind = [
$inv['idarchive'],
$inv['name'],
$inv['idsite'],
$inv['date1'],
$inv['date2'],
$inv['period'],
isset($inv['ts_invalidated']) ? $inv['ts_invalidated'] : $now,
$inv['report'],
isset($inv['status']) ? $inv['status'] : 0,
isset($inv['ts_started']) ? $inv['ts_started'] : null,
];
Db::query("INSERT INTO `$table` (idarchive, name, idsite, date1, date2, period, ts_invalidated, report, status, ts_started)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", $bind);
}
}
private function getInvalidationsInTable($includeInvalidated = false)
{
$table = Common::prefixTable('archive_invalidations');
$suffix = $includeInvalidated ? ', ts_invalidated' : '';
$sql = "SELECT idarchive, idsite, period, date1, date2, name, report$suffix FROM `$table`";
return Db::fetchAll($sql);
}
}
class TestCronArchive extends CronArchive
{
protected function checkPiwikUrlIsValid()
{
}
protected function initPiwikHost($piwikUrl = false)
{
}
public function wasSegmentChangedRecently($definition, $allSegments)
{
return parent::wasSegmentChangedRecently($definition, $allSegments);
}
}