From c186252bf9a30a54c15330f0f4c88c1a033e7f25 Mon Sep 17 00:00:00 2001
From: diosmosis <benaka@piwik.pro>
Date: Wed, 7 Oct 2015 17:32:08 -0700
Subject: [PATCH] Fix regression, make sure if period is not supplied to
 ArchiveInvalidator then all periods are invalidated efficiently.

---
 core/Archive/ArchiveInvalidator.php           | 51 ++++++++++++----
 core/DataAccess/Model.php                     | 19 +++---
 .../DataAccess/ArchiveInvalidatorTest.php     | 60 +++++++++++++++----
 3 files changed, 101 insertions(+), 29 deletions(-)

diff --git a/core/Archive/ArchiveInvalidator.php b/core/Archive/ArchiveInvalidator.php
index daa847e520..c4fff2bef4 100644
--- a/core/Archive/ArchiveInvalidator.php
+++ b/core/Archive/ArchiveInvalidator.php
@@ -137,11 +137,16 @@ class ArchiveInvalidator
 
         $datesToInvalidate = $this->removeDatesThatHaveBeenPurged($dates, $invalidationInfo);
 
-        $periods = $this->getPeriodsToInvalidate($datesToInvalidate, $period, $cascadeDown);
-        $periods = $this->getPeriodsByYearMonthAndType($periods);
-        $this->markArchivesInvalidated($idSites, $periods, $segment);
+        if (empty($period)) {
+            // if the period is empty, we don't need to cascade in any way, since we'll remove all periods
+            $periodDates = $this->getDatesByYearMonthAndPeriodType($dates);
+        } else {
+            $periods = $this->getPeriodsToInvalidate($datesToInvalidate, $period, $cascadeDown);
+            $periodDates = $this->getPeriodDatesByYearMonthAndPeriodType($periods);
+        }
+        $this->markArchivesInvalidated($idSites, $periodDates, $segment);
 
-        $yearMonths = array_keys($periods);
+        $yearMonths = array_keys($periodDates);
         $this->markInvalidatedArchivesForReprocessAndPurge($idSites, $yearMonths);
 
         foreach ($idSites as $idSite) {
@@ -197,35 +202,57 @@ class ArchiveInvalidator
 
     /**
      * @param Period[] $periods
-     * @return Period[][][]
+     * @return string[][][]
      */
-    private function getPeriodsByYearMonthAndType($periods)
+    private function getPeriodDatesByYearMonthAndPeriodType($periods)
     {
         $result = array();
         foreach ($periods as $period) {
-            $yearMonth = ArchiveTableCreator::getTableMonthFromDate($period->getDateStart());
+            $date = $period->getDateStart();
             $periodType = $period->getId();
 
-            $result[$yearMonth][$periodType][] = $period;
+            $yearMonth = ArchiveTableCreator::getTableMonthFromDate($date);
+            $result[$yearMonth][$periodType][] = $date->toString();
+        }
+        return $result;
+    }
+
+    /**
+     * Called when deleting all periods.
+     *
+     * @param Date[] $dates
+     * @return string[][][]
+     */
+    private function getDatesByYearMonthAndPeriodType($dates)
+    {
+        $result = array();
+        foreach ($dates as $date) {
+            $yearMonth = ArchiveTableCreator::getTableMonthFromDate($date);
+            $result[$yearMonth][null][] = $date->toString();
+
+            // since we're removing all periods, we must make sure to remove year periods as well.
+            // this means we have to make sure the january table is processed.
+            $janYearMonth = $date->toString('Y') . '_01';
+            $result[$janYearMonth][null][] = $date->toString();
         }
         return $result;
     }
 
     /**
      * @param int[] $idSites
-     * @param Period[][][] $periods
+     * @param string[][][] $dates
      * @throws \Exception
      */
-    private function markArchivesInvalidated($idSites, $periods, Segment $segment = null)
+    private function markArchivesInvalidated($idSites, $dates, Segment $segment = null)
     {
         $archiveNumericTables = ArchiveTableCreator::getTablesArchivesInstalled($type = ArchiveTableCreator::NUMERIC_TABLE);
         foreach ($archiveNumericTables as $table) {
             $tableDate = ArchiveTableCreator::getDateFromTableName($table);
-            if (empty($periods[$tableDate])) {
+            if (empty($dates[$tableDate])) {
                 continue;
             }
 
-            $this->model->updateArchiveAsInvalidated($table, $idSites, $periods[$tableDate], $segment);
+            $this->model->updateArchiveAsInvalidated($table, $idSites, $dates[$tableDate], $segment);
         }
     }
 
diff --git a/core/DataAccess/Model.php b/core/DataAccess/Model.php
index f70a934d13..e8ec27c7de 100644
--- a/core/DataAccess/Model.php
+++ b/core/DataAccess/Model.php
@@ -100,28 +100,33 @@ class Model
     /**
      * @param string $archiveTable Prefixed table name
      * @param int[] $idSites
-     * @param Period[][] $periodsByType
+     * @param string[][] $datesByPeriodType
      * @param Segment $segment
      * @return \Zend_Db_Statement
      * @throws Exception
      */
-    public function updateArchiveAsInvalidated($archiveTable, $idSites, $periodsByType, Segment $segment = null)
+    public function updateArchiveAsInvalidated($archiveTable, $idSites, $datesByPeriodType, Segment $segment = null)
     {
         $idSites = array_map('intval', $idSites);
 
         $bind = array();
 
         $periodConditions = array();
-        foreach ($periodsByType as $periodType => $periods) {
+        foreach ($datesByPeriodType as $periodType => $dates) {
             $dateConditions = array();
 
-            foreach ($periods as $period) {
+            foreach ($dates as $date) {
                 $dateConditions[] = "(date1 <= ? AND ? <= date2)";
-                $bind[] = $period->getDateStart()->toString();
-                $bind[] = $period->getDateStart()->toString();
+                $bind[] = $date;
+                $bind[] = $date;
             }
 
-            $periodConditions[] = "(period = " . (int)$periodType . " AND (" . implode(" OR ", $dateConditions) . "))";
+            $dateConditionsSql = implode(" OR ", $dateConditions);
+            if (empty($periodType)) { // remove all periods
+                $periodConditions[] = "($dateConditionsSql)";
+            } else {
+                $periodConditions[] = "(period = " . (int)$periodType . " AND ($dateConditionsSql))";
+            }
         }
 
         if ($segment) {
diff --git a/tests/PHPUnit/Integration/DataAccess/ArchiveInvalidatorTest.php b/tests/PHPUnit/Integration/DataAccess/ArchiveInvalidatorTest.php
index 76433c0eb4..199187d46a 100644
--- a/tests/PHPUnit/Integration/DataAccess/ArchiveInvalidatorTest.php
+++ b/tests/PHPUnit/Integration/DataAccess/ArchiveInvalidatorTest.php
@@ -243,11 +243,14 @@ class ArchiveInvalidatorTest extends IntegrationTestCase
         $expectedWarningDates = array($dateBeforeThreshold->toString());
         $this->assertEquals($expectedWarningDates, $result->warningDates);
 
-        $expectedIdArchives = array(
-            '2015_03' => array(),
-            '2015_04' => array(1),
-        );
-        $this->assertEquals($expectedIdArchives, $this->getInvalidatedIdArchives());
+        $invalidatedArchives = $this->getInvalidatedIdArchives();
+
+        $countInvalidatedArchives = 0;
+        foreach ($invalidatedArchives as $idarchives) {
+            $countInvalidatedArchives += count($idarchives);
+        }
+
+        $this->assertEquals(1, $countInvalidatedArchives);
     }
 
     public function test_markArchivesAsInvalidated_CorrectlyModifiesDistributedLists()
@@ -337,6 +340,8 @@ class ArchiveInvalidatorTest extends IntegrationTestCase
                         '2.2015-01-01.2015-01-01.1.done.VisitsSummary',
                         '1.2015-01-01.2015-01-31.3.done3736b708e4d20cfc10610e816a1b2341',
                         '2.2015-01-01.2015-01-31.3.done.VisitsSummary',
+                        '1.2015-01-01.2015-12-31.4.done5447835b0a861475918e79e932abdfd8',
+                        '2.2015-01-01.2015-12-31.4.done',
                     ),
                     '2015_02' => array(
                         '1.2015-02-05.2015-02-05.1.done3736b708e4d20cfc10610e816a1b2341.UserCountry',
@@ -347,9 +352,10 @@ class ArchiveInvalidatorTest extends IntegrationTestCase
                         '2.2015-02-01.2015-02-28.3.done3736b708e4d20cfc10610e816a1b2341.UserCountry',
                     ),
                     '2015_05' => array(
-                        '1.2015-05-01.2015-05-31.3.done3736b708e4d20cfc10610e816a1b2341.UserCountry',
-                        '2.2015-05-01.2015-05-31.3.done5447835b0a861475918e79e932abdfd8',
+                        '1.2015-05-01.2015-05-31.3.done3736b708e4d20cfc10610e816a1b2341',
+                        '2.2015-05-01.2015-05-31.3.done.VisitsSummary',
                     ),
+                    '2015_06' => array(),
                 ),
             ),
 
@@ -365,11 +371,13 @@ class ArchiveInvalidatorTest extends IntegrationTestCase
                     '2014_12' => array(),
                     '2015_01' => array(
                         '1.2015-01-01.2015-01-31.3.done3736b708e4d20cfc10610e816a1b2341',
+                        '1.2015-01-01.2015-12-31.4.done5447835b0a861475918e79e932abdfd8',
                     ),
                     '2015_02' => array(),
                     '2015_03' => array(),
                     '2015_04' => array(),
-                    '2015_05' => array()
+                    '2015_05' => array(),
+                    '2015_06' => array(),
                 ),
             ),
 
@@ -422,11 +430,13 @@ class ArchiveInvalidatorTest extends IntegrationTestCase
                         '1.2015-01-19.2015-01-25.2.done',
                         '1.2015-01-26.2015-02-01.2.done3736b708e4d20cfc10610e816a1b2341.UserCountry',
                         '1.2015-01-01.2015-01-31.3.done3736b708e4d20cfc10610e816a1b2341',
+                        '1.2015-01-01.2015-12-31.4.done5447835b0a861475918e79e932abdfd8',
                     ),
                     '2015_02' => array(),
                     '2015_03' => array(),
                     '2015_04' => array(),
                     '2015_05' => array(),
+                    '2015_06' => array(),
                 ),
             ),
 
@@ -461,6 +471,7 @@ class ArchiveInvalidatorTest extends IntegrationTestCase
                         '1.2015-01-31.2015-01-31.1.done3736b708e4d20cfc10610e816a1b2341',
                         '1.2015-01-26.2015-02-01.2.done3736b708e4d20cfc10610e816a1b2341.UserCountry',
                         '1.2015-01-01.2015-01-31.3.done3736b708e4d20cfc10610e816a1b2341',
+                        '1.2015-01-01.2015-12-31.4.done5447835b0a861475918e79e932abdfd8',
                     ),
                     '2015_02' => array(
                         '1.2015-02-01.2015-02-01.1.done3736b708e4d20cfc10610e816a1b2341',
@@ -469,6 +480,7 @@ class ArchiveInvalidatorTest extends IntegrationTestCase
                     '2015_03' => array(),
                     '2015_04' => array(),
                     '2015_05' => array(),
+                    '2015_06' => array(),
                 ),
             ),
 
@@ -483,7 +495,7 @@ class ArchiveInvalidatorTest extends IntegrationTestCase
                     '2014_01' => array(),
                     '2014_12' => array(),
                     '2015_01' => array(
-                        '1.2015-01-01.2015-01-10.5.done5447835b0a861475918e79e932abdfd8',
+                        '1.2015-01-01.2015-01-10.5.done.VisitsSummary',
                     ),
                     '2015_02' => array(),
                     '2015_03' => array(
@@ -492,6 +504,7 @@ class ArchiveInvalidatorTest extends IntegrationTestCase
                     ),
                     '2015_04' => array(),
                     '2015_05' => array(),
+                    '2015_06' => array(),
                 ),
             ),
 
@@ -528,6 +541,32 @@ class ArchiveInvalidatorTest extends IntegrationTestCase
                     '2015_03' => array(),
                     '2015_04' => array(),
                     '2015_05' => array(),
+                    '2015_06' => array(),
+                ),
+            ),
+
+            // removing all periods
+            array(
+                array(1),
+                array('2015-05-05'),
+                '',
+                null,
+                false,
+                array(
+                    '2014_01' => array(),
+                    '2014_12' => array(),
+                    '2015_01' => array(
+                        '1.2015-01-01.2015-12-31.4.done5447835b0a861475918e79e932abdfd8',
+                    ),
+                    '2015_02' => array(),
+                    '2015_03' => array(),
+                    '2015_04' => array(),
+                    '2015_05' => array(
+                        '1.2015-05-05.2015-05-05.1.done3736b708e4d20cfc10610e816a1b2341.UserCountry',
+                        '1.2015-05-04.2015-05-10.2.done5447835b0a861475918e79e932abdfd8',
+                        '1.2015-05-01.2015-05-31.3.done3736b708e4d20cfc10610e816a1b2341',
+                    ),
+                    '2015_06' => array(),
                 ),
             ),
         );
@@ -572,7 +611,8 @@ class ArchiveInvalidatorTest extends IntegrationTestCase
         $endDate = Date::factory('2015-05-31');
 
         foreach ($periods as $periodLabel) {
-            for ($date = $startDate; $date->isEarlier($endDate); $date = $date->addPeriod(1, $periodLabel)) {
+            $nextEndDate = $endDate->addPeriod(1, $periodLabel);
+            for ($date = $startDate; $date->isEarlier($nextEndDate); $date = $date->addPeriod(1, $periodLabel)) {
                 foreach ($sites as $idSite) {
                     $this->insertArchiveRow($idSite, $date->toString(), $periodLabel);
                 }
-- 
GitLab