PageRenderTime 55ms CodeModel.GetById 28ms RepoModel.GetById 0ms app.codeStats 0ms

/core/CronArchive/SharedSiteIds.php

https://github.com/CodeYellowBV/piwik
PHP | 176 lines | 102 code | 35 blank | 39 comment | 12 complexity | deb336c61a463cee9c2324461ed766d2 MD5 | raw file
Possible License(s): LGPL-3.0, JSON, MIT, GPL-3.0, LGPL-2.1, GPL-2.0, AGPL-1.0, BSD-2-Clause, BSD-3-Clause
  1. <?php
  2. /**
  3. * Piwik - free/libre analytics platform
  4. *
  5. * @link http://piwik.org
  6. * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
  7. *
  8. */
  9. namespace Piwik\CronArchive;
  10. use Exception;
  11. use Piwik\CliMulti\Process;
  12. use Piwik\Option;
  13. /**
  14. * This class saves all to be processed siteIds in an Option named 'SharedSiteIdsToArchive' and processes all sites
  15. * within that list. If a user starts multiple archiver those archiver will help to finish processing that list.
  16. */
  17. class SharedSiteIds
  18. {
  19. private $siteIds = array();
  20. private $currentSiteId;
  21. private $done = false;
  22. public function __construct($websiteIds)
  23. {
  24. if (empty($websiteIds)) {
  25. $websiteIds = array();
  26. }
  27. $self = $this;
  28. $this->siteIds = $this->runExclusive(function () use ($self, $websiteIds) {
  29. // if there are already sites to be archived registered, prefer the list of existing archive, meaning help
  30. // to finish this queue of sites instead of starting a new queue
  31. $existingWebsiteIds = $self->getAllSiteIdsToArchive();
  32. if (!empty($existingWebsiteIds)) {
  33. return $existingWebsiteIds;
  34. }
  35. $self->setSiteIdsToArchive($websiteIds);
  36. return $websiteIds;
  37. });
  38. }
  39. public function getInitialSiteIds()
  40. {
  41. return $this->siteIds;
  42. }
  43. /**
  44. * Get the number of total websites that needs to be processed.
  45. *
  46. * @return int
  47. */
  48. public function getNumSites()
  49. {
  50. return count($this->siteIds);
  51. }
  52. /**
  53. * Get the number of already processed websites (not necessarily all of those where processed by this archiver).
  54. *
  55. * @return int
  56. */
  57. public function getNumProcessedWebsites()
  58. {
  59. if ($this->done) {
  60. return $this->getNumSites();
  61. }
  62. if (empty($this->currentSiteId)) {
  63. return 0;
  64. }
  65. $index = array_search($this->currentSiteId, $this->siteIds);
  66. if (false === $index) {
  67. return 0;
  68. }
  69. return $index + 1;
  70. }
  71. public function setSiteIdsToArchive($siteIds)
  72. {
  73. if (!empty($siteIds)) {
  74. Option::set('SharedSiteIdsToArchive', implode(',', $siteIds));
  75. } else {
  76. Option::delete('SharedSiteIdsToArchive');
  77. }
  78. }
  79. public function getAllSiteIdsToArchive()
  80. {
  81. Option::clearCachedOption('SharedSiteIdsToArchive');
  82. $siteIdsToArchive = Option::get('SharedSiteIdsToArchive');
  83. if (empty($siteIdsToArchive)) {
  84. return array();
  85. }
  86. return explode(',', trim($siteIdsToArchive));
  87. }
  88. /**
  89. * If there are multiple archiver running on the same node it makes sure only one of them performs an action and it
  90. * will wait until another one has finished. Any closure you pass here should be very fast as other processes wait
  91. * for this closure to finish otherwise. Currently only used for making multiple archivers at the same time work.
  92. * If a closure takes more than 5 seconds we assume it is dead and simply continue.
  93. *
  94. * @param \Closure $closure
  95. * @return mixed
  96. * @throws \Exception
  97. */
  98. private function runExclusive($closure)
  99. {
  100. $process = new Process('archive.sharedsiteids');
  101. while ($process->isRunning() && $process->getSecondsSinceCreation() < 5) {
  102. // wait max 5 seconds, such an operation should not take longer
  103. usleep(25 * 1000);
  104. }
  105. $process->startProcess();
  106. try {
  107. $result = $closure();
  108. } catch (Exception $e) {
  109. $process->finishProcess();
  110. throw $e;
  111. }
  112. $process->finishProcess();
  113. return $result;
  114. }
  115. /**
  116. * Get the next site id that needs to be processed or null if all site ids where processed.
  117. *
  118. * @return int|null
  119. */
  120. public function getNextSiteId()
  121. {
  122. $self = $this;
  123. $this->currentSiteId = $this->runExclusive(function () use ($self) {
  124. $siteIds = $self->getAllSiteIdsToArchive();
  125. if (empty($siteIds)) {
  126. return null;
  127. }
  128. $nextSiteId = array_shift($siteIds);
  129. $self->setSiteIdsToArchive($siteIds);
  130. return $nextSiteId;
  131. });
  132. if (is_null($this->currentSiteId)) {
  133. $this->done = true;
  134. }
  135. return $this->currentSiteId;
  136. }
  137. public static function isSupported()
  138. {
  139. return Process::isSupported();
  140. }
  141. }