Ticket #1077: issue1077.diff

File issue1077.diff, 14.1 KB (added by capedfuzz, 3 years ago)

Patch for issue #1077. Optimizes the IndexedBySite archive type. Speeds up All Websites page by 12 seconds when Piwik tracks 800 websites.

  • core/ArchiveProcessing.php

     
    200200        protected $startTimestampUTC; 
    201201        protected $endTimestampUTC; 
    202202         
    203         protected $segmentsToProcess = null; 
    204          
    205203        /** 
    206204         * Constructor 
    207205         */ 
     
    315313                $dateStartLocalTimezone = $this->period->getDateStart(); 
    316314                $dateEndLocalTimezone = $this->period->getDateEnd(); 
    317315                 
    318                 $this->tableArchiveNumeric = new Piwik_TablePartitioning_Monthly('archive_numeric'); 
    319                 $this->tableArchiveNumeric->setIdSite($this->idsite); 
    320                 $this->tableArchiveNumeric->setTimestamp($dateStartLocalTimezone->getTimestamp()); 
    321                 $this->tableArchiveBlob = new Piwik_TablePartitioning_Monthly('archive_blob'); 
    322                 $this->tableArchiveBlob->setIdSite($this->idsite);       
    323                 $this->tableArchiveBlob->setTimestamp($dateStartLocalTimezone->getTimestamp()); 
     316                $this->tableArchiveNumeric = self::makeNumericArchiveTable($this->period); 
     317                $this->tableArchiveBlob = self::makeBlobArchiveTable($this->period); 
    324318 
    325319                $dateStartUTC = $dateStartLocalTimezone->setTimezone($this->site->getTimezone()); 
    326320                $dateEndUTC = $dateEndLocalTimezone->setTimezone($this->site->getTimezone()); 
     
    333327                $db = Zend_Registry::get('db'); 
    334328                $this->compressBlob = $db->hasBlobDataType(); 
    335329        } 
     330         
     331        /** 
     332         * Utility function which creates a TablePartitioning instance for the numeric 
     333         * archive data of a given period. 
     334         *  
     335         * @param $period The time period of the archive data. 
     336         * @return Piwik_TablePartitioning_Monthly 
     337         */ 
     338        public static function makeNumericArchiveTable($period) 
     339        { 
     340                $result = new Piwik_TablePartitioning_Monthly('archive_numeric'); 
     341                $result->setTimestamp($period->getDateStart()->getTimestamp()); 
     342                return $result; 
     343        } 
     344         
     345        /** 
     346         * Utility function which creates a TablePartitioning instance for the blob 
     347         * archive data of a given period. 
     348         *  
     349         * @param $period The time period of the archive data. 
     350         * @return Piwik_TablePartitioning_Monthly 
     351         */ 
     352        public static function makeBlobArchiveTable($period) 
     353        { 
     354                $result = new Piwik_TablePartitioning_Monthly('archive_blob'); 
     355                $result->setTimestamp($period->getDateStart()->getTimestamp()); 
     356                return $result; 
     357        } 
    336358 
    337359        public function getStartDatetimeUTC() 
    338360        { 
     
    452474         
    453475        abstract public function isThereSomeVisits(); 
    454476         
    455         protected function getDoneStringFlag($flagArchiveAsAllPlugins = false) 
     477        /** 
     478         * Returns the name of the archive field used to tell the status of an archive, (ie, 
     479         * whether the archive was created succesfully or not). 
     480         *  
     481         * @param bool $flagArchiveAsAllPlugins 
     482         * @return string 
     483         */ 
     484        public function getDoneStringFlag($flagArchiveAsAllPlugins = false) 
    456485        { 
    457                 $segment = $this->getSegment()->getHash(); 
    458                 if(!$this->shouldProcessReportsAllPlugins($this->getSegment(), $this->period)) 
     486                return self::getDoneStringFlagFor( 
     487                        $this->getSegment(), $this->period, $this->getRequestedReport(), $flagArchiveAsAllPlugins); 
     488        } 
     489         
     490        /** 
     491         * Returns the name of the archive field used to tell the status of an archive, (ie, 
     492         * whether the archive was created succesfully or not). 
     493         *  
     494         * @param bool $flagArchiveAsAllPlugins 
     495         * @return string 
     496         */ 
     497        public static function getDoneStringFlagFor($segment, $period, $requestedReport, $flagArchiveAsAllPlugins = false) 
     498        { 
     499                $segmentHash = $segment->getHash(); 
     500                if(!self::shouldProcessReportsAllPluginsFor($segment, $period)) 
    459501                { 
    460                         $pluginProcessed = self::getPluginBeingProcessed($this->getRequestedReport()); 
    461 //                      Piwik::log("Plugin processed: $pluginProcessed"); 
     502                        $pluginProcessed = self::getPluginBeingProcessed($requestedReport); 
    462503                        if(!Piwik_PluginsManager::getInstance()->isPluginLoaded($pluginProcessed) 
    463504                                || $flagArchiveAsAllPlugins  
    464505                                ) 
    465506                        { 
    466507                                $pluginProcessed = 'all'; 
    467508                        } 
    468                         $segment .= '.'.$pluginProcessed; 
     509                        $segmentHash .= '.'.$pluginProcessed; 
    469510                } 
    470             return 'done' . $segment; 
     511            return 'done' . $segmentHash; 
    471512        } 
    472513         
    473514        /** 
     
    880921         */ 
    881922        public function isArchivingDisabled() 
    882923        { 
    883                 $processOneReportOnly = !$this->shouldProcessReportsAllPlugins($this->getSegment(), $this->period); 
     924                return self::isArchivingDisabledFor($this->getSegment(), $this->period); 
     925        } 
     926         
     927        public static function isArchivingDisabledFor($segment, $period) 
     928        { 
     929                $processOneReportOnly = !self::shouldProcessReportsAllPluginsFor($segment, $period); 
    884930                if($processOneReportOnly) 
    885931                { 
    886932                        // When there is a segment, archiving is not necessary allowed 
    887933                        // If browser archiving is allowed, then archiving is enabled 
    888934                        // if browser archiving is not allowed, then archiving is disabled 
    889                         if(!$this->getSegment()->isEmpty() 
    890                                 && !$this->isRequestAuthorizedToArchive() 
     935                        if(!$segment->isEmpty() 
     936                                && !self::isRequestAuthorizedToArchive() 
    891937                                && Zend_Registry::get('config')->General->browser_archiving_disabled_enforce 
    892938                        ) 
    893939                        { 
     
    896942                        } 
    897943                        return false; 
    898944                } 
    899                 $isDisabled = !$this->isRequestAuthorizedToArchive(); 
     945                $isDisabled = !self::isRequestAuthorizedToArchive(); 
    900946                return $isDisabled; 
    901947        } 
    902948         
    903         protected function isRequestAuthorizedToArchive() 
     949        protected static function isRequestAuthorizedToArchive() 
    904950        { 
    905951                return self::isBrowserTriggerArchivingEnabled() 
    906952                                || Piwik_Common::isPhpCliMode() 
     
    916962         */ 
    917963        protected function shouldProcessReportsAllPlugins($segment, $period) 
    918964        { 
     965                return self::shouldProcessReportsAllPluginsFor($segment, $period); 
     966        } 
     967         
     968        protected static function shouldProcessReportsAllPluginsFor($segment, $period) 
     969        { 
    919970                if($segment->isEmpty() && $period->getLabel() != 'range') 
    920971                { 
    921972                        return true; 
    922973                } 
    923974                 
    924                 if(is_null($this->segmentsToProcess)) 
     975                $segmentsToProcess = Piwik::getKnownSegmentsToArchive(); 
     976                if(!empty($segmentsToProcess)) 
    925977                { 
    926                         $this->segmentsToProcess = Piwik::getKnownSegmentsToArchive(); 
    927                 } 
    928                 if(!empty($this->segmentsToProcess)) 
    929                 { 
    930978                        // If the requested segment is one of the segments to pre-process 
    931979                        // we ensure that any call to the API will trigger archiving of all reports for this segment 
    932                         $segment = $this->getSegment()->getString(); 
    933                         if(in_array($segment, $this->segmentsToProcess)) 
     980                        $segment = $segment->getString(); 
     981                        if(in_array($segment, $segmentsToProcess)) 
    934982                        { 
    935983                                return true; 
    936984                        } 
    937985                } 
    938986                return false; 
    939987        } 
    940          
     988 
    941989        /** 
    942990         * When a segment is set, we shall only process the requested report (no more). 
    943991         * The requested data set will return a lot faster if we only process these reports rather than all plugins. 
  • core/TablePartitioning.php

     
    2323        protected $tableName = null; 
    2424        protected $generatedTableName = null; 
    2525        protected $timestamp = null; 
    26         protected $idSite = null; 
    27          
     26 
    2827        static public $tablesAlreadyInstalled = null; 
    2928         
    3029        public function __construct( $tableName ) 
     
    4039                $this->generatedTableName = null; 
    4140                $this->getTableName(); 
    4241        } 
    43          
    44         public function setIdSite($idSite) 
    45         { 
    46                 $this->idSite = $idSite; 
    47         } 
    4842                 
    4943        public function getTableName() 
    5044        { 
  • core/Archive/Single.php

     
    569569                $this->freeBlob($name); 
    570570                return $dataTableToLoad;                 
    571571        } 
     572         
     573        /** 
     574         * Returns true if Piwik can launch the archiving process for this archive, 
     575         * false if otherwise. 
     576         *  
     577         * @return bool 
     578         */ 
     579        public function isArchivingDisabled() 
     580        { 
     581                return Piwik_ArchiveProcessing::isArchivingDisabledFor($this->segment, $this->period); 
     582        } 
    572583} 
  • core/Archive/Array/IndexedBySite.php

     
    1717class Piwik_Archive_Array_IndexedBySite extends Piwik_Archive_Array  
    1818{ 
    1919        /** 
     20         * Used to cache the name of the table that holds the data this archive. 
     21         *  
     22         * This will only be used if the archives held by this instance are instances of 
     23         * Piwik_Archive_Single. 
     24         */ 
     25        private $tableName = null; 
     26 
     27        /** 
    2028         * @param Piwik_Site $oSite  
    2129         * @param string $strPeriod eg. 'day' 'week' etc. 
    2230         * @param string $strDate A date range, eg. 'last10', 'previous5' or 'YYYY-MM-DD,YYYY-MM-DD' 
     
    7987 
    8088        private function getValues($fields) 
    8189        { 
    82                 foreach($this->archives as $archive) 
    83                 { 
    84                         $archive->setRequestedReport( is_string($fields) ? $fields : current($fields) ); 
    85                         $archive->prepareArchive(); 
    86                 } 
    87                  
    8890                $arrayValues = array(); 
    8991                foreach($this->loadValuesFromDB($fields) as $value) 
    9092                { 
     
    9597         
    9698        private function loadValuesFromDB($fields) 
    9799        { 
     100                $requestedReport = is_string($fields) ? $fields : current($fields); 
    98101                $inNames = Piwik_Common::getSqlStringFieldsArray($fields); 
    99                 $archiveIds = $this->getArchiveIds(); 
     102 
     103                // get the archive ids 
     104                if (!$this->getFirstArchive()->isArchivingDisabled()) 
     105                { 
     106                        $archiveIds = $this->getArchiveIdsAfterLaunching($requestedReport); 
     107                } 
     108                else 
     109                { 
     110                        $archiveIds = $this->getArchiveIdsWithoutLaunching($requestedReport); 
     111                } 
     112 
     113                $archiveIds = implode(', ', array_filter($archiveIds)); 
     114 
     115                // if no archive ids are found, avoid executing any SQL queries 
    100116                if(empty($archiveIds)) 
    101117                { 
    102118                        return array(); 
    103119                } 
     120 
     121                // select archive data 
    104122                $sql = "SELECT value, name, idarchive, idsite 
    105123                                                                FROM {$this->getNumericTableName()} 
    106124                                                                WHERE idarchive IN ( $archiveIds ) 
     
    110128 
    111129        private function getFirstArchive() 
    112130        { 
    113                 reset($this->archives); 
    114                 return current($this->archives); 
     131                return reset($this->archives); 
    115132        } 
    116133 
    117         private function getArchiveIds() 
     134        /** 
     135         * Gets the archive id of every Single archive this archive holds. This method 
     136         * will launch the archiving process if appropriate. 
     137         *  
     138         * @param string $requestedReport The requested archive report. 
     139         * @return array 
     140         */ 
     141        private function getArchiveIdsAfterLaunching( $requestedReport ) 
    118142        { 
     143                // prepare archives (this will launch archiving when appropriate) 
     144                foreach($this->archives as $archive) 
     145                { 
     146                        $archive->setRequestedReport( $requestedReport ); 
     147                        $archive->prepareArchive(); 
     148                } 
     149 
     150                // collect archive ids for archives that have visits 
    119151                $archiveIds = array(); 
    120152                foreach($this->archives as $archive) 
    121153                { 
     
    125157                        } 
    126158                         
    127159                        $archiveIds[] = $archive->getIdArchive(); 
    128                          
     160                 
    129161                        if( $this->getNumericTableName() != $archive->archiveProcessing->getTableArchiveNumericName()) 
    130162                        { 
    131163                                throw new Exception("Piwik_Archive_Array_IndexedBySite::getDataTableFromNumeric() algorithm won't work if data is stored in different tables"); 
    132164                        } 
    133165                } 
    134                 return implode(', ', array_filter($archiveIds)); 
     166                 
     167                return $archiveIds; 
    135168        } 
     169 
     170        /** 
     171         * Gets the archive id of every Single archive this archive holds. This method 
     172         * will not launch the archiving process. 
     173         *  
     174         * @param string $requestedReport The requested archive report. 
     175         * @return array 
     176         */ 
     177        private function getArchiveIdsWithoutLaunching( $requestedReport ) 
     178        { 
     179                $firstArchive = $this->getFirstArchive(); 
     180                $segment = $firstArchive->getSegment(); 
     181                $period = $firstArchive->getPeriod(); 
     182                 
     183                // the flags used to tell how the archiving process for a specific archive was completed, 
     184                // if it was completed 
     185                $done = Piwik_ArchiveProcessing::getDoneStringFlagFor($segment, $period, $requestedReport); 
     186                $donePlugins = Piwik_ArchiveProcessing::getDoneStringFlagFor($segment, $period, $requestedReport, true); 
     187 
     188                // create the SQL to query every archive ID 
     189                $nameConditionSuffix = ''; 
     190                if ($done != $donePlugins) 
     191                { 
     192                        $nameConditionSuffix = "OR name = '$donePlugins'"; 
     193                } 
     194 
     195                $nameCondition = "(name = '$done' $nameConditionSuffix) AND 
     196                                                  (value = '".Piwik_ArchiveProcessing::DONE_OK."' OR 
     197                                                   value = '".Piwik_ArchiveProcessing::DONE_OK_TEMPORARY."')"; 
     198 
     199                $sql = "SELECT idsite, 
     200                               MAX(idarchive) AS idarchive 
     201                          FROM ".$this->getNumericTableName()." 
     202                         WHERE date1 = ? 
     203                           AND date2 = ? 
     204                           AND period = ? 
     205                           AND $nameCondition 
     206                           AND idsite IN (".implode(',', array_keys($this->archives)).") 
     207                      GROUP BY idsite"; 
     208                 
     209                $bind = array($period->getDateStart()->toString('Y-m-d'), 
     210                                          $period->getDateEnd()->toString('Y-m-d'), 
     211                                          $period->getId()); 
     212 
     213                // execute the query and process the results. 
     214                $archiveIds = array(); 
     215                foreach (Piwik_FetchAll($sql, $bind) as $row) 
     216                { 
     217                        $archiveIds[] = $row['idarchive']; 
     218                } 
     219                 
     220                return $archiveIds; 
     221        } 
    136222         
     223        /** 
     224         * Gets the name of the database table that holds the numeric archive data for 
     225         * this archive. 
     226         * 
     227         * @return string 
     228         */ 
    137229        private function getNumericTableName() 
    138230        { 
    139                 return $this->getFirstArchive()->archiveProcessing->getTableArchiveNumericName(); 
     231                if (is_null($this->tableName)) 
     232                { 
     233                        $table = Piwik_ArchiveProcessing::makeNumericArchiveTable($this->getFirstArchive()->getPeriod()); 
     234                        $this->tableName = $table->getTableName(); 
     235                } 
     236 
     237                return $this->tableName; 
    140238        } 
    141239} 
  • core/Archive.php

     
    321321                return $dataTable; 
    322322        } 
    323323         
    324         protected function getSegment() 
     324        public function getSegment() 
    325325        { 
    326326            return $this->segment; 
    327327        } 
  • core/Piwik.php

     
    15521552                                        Zend_Registry::get('config')->General->autocomplete_min_sites); 
    15531553                return (int)$count; 
    15541554        } 
    1555          
     1555 
    15561556        /** 
    15571557         * Segments to pre-process 
    15581558         */ 
    15591559        static public function getKnownSegmentsToArchive() 
    15601560        { 
    1561                 $segments = Zend_Registry::get('config')->Segments->toArray(); 
    1562                 return isset($segments['Segments']) ? $segments['Segments'] : ''; 
     1561                static $cachedResult = null; 
     1562 
     1563                if (is_null($cachedResult)) 
     1564                { 
     1565                        $segments = Zend_Registry::get('config')->Segments->toArray(); 
     1566                        $cachedResult = isset($segments['Segments']) ? $segments['Segments'] : ''; 
     1567                } 
     1568                 
     1569                return $cachedResult; 
    15631570        } 
    15641571 
    15651572/*