opportunisticqueuemanager.php 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151
  1. <?php
  2. // This file is part of GNU social - https://www.gnu.org/software/social
  3. //
  4. // GNU social is free software: you can redistribute it and/or modify
  5. // it under the terms of the GNU Affero General Public License as published by
  6. // the Free Software Foundation, either version 3 of the License, or
  7. // (at your option) any later version.
  8. //
  9. // GNU social is distributed in the hope that it will be useful,
  10. // but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. // GNU Affero General Public License for more details.
  13. //
  14. // You should have received a copy of the GNU Affero General Public License
  15. // along with GNU social. If not, see <http://www.gnu.org/licenses/>.
  16. /**
  17. * GNU social queue-manager-on-visit class
  18. *
  19. * Will run events for a certain time, or until finished.
  20. *
  21. * Configure remote key if wanted with $config['opportunisticqm']['qmkey'] and
  22. * use with /main/runqueue?qmkey=abc123
  23. *
  24. * @category Cron
  25. * @package GNUsocial
  26. * @author Mikael Nordfeldth <mmn@hethane.se>
  27. * @copyright 2013 Free Software Foundation, Inc http://www.fsf.org
  28. * @license https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later
  29. */
  30. defined('GNUSOCIAL') || die();
  31. class OpportunisticQueueManager extends QueueManager
  32. {
  33. protected $qmkey = false;
  34. protected $max_execution_time = null;
  35. protected $max_execution_margin = null; // margin to PHP's max_execution_time
  36. protected $max_queue_items = null;
  37. protected $start_cpu_time = null;
  38. protected $handled_items = 0;
  39. protected $verbosity = null;
  40. // typically just used for the /main/cron action, only used if php.ini max_execution_time is 0
  41. const MAXEXECTIME = 20;
  42. public function __construct(array $args = [])
  43. {
  44. foreach (get_class_vars(get_class($this)) as $key=>$val) {
  45. if (array_key_exists($key, $args)) {
  46. $this->$key = $args[$key];
  47. }
  48. }
  49. $this->verifyKey();
  50. if (is_null($this->start_cpu_time)) {
  51. $this->start_cpu_time = hrtime(true);
  52. }
  53. if (is_null($this->max_execution_time)) {
  54. $this->max_execution_time = ini_get('max_execution_time') ?: self::MAXEXECTIME;
  55. }
  56. if (is_null($this->max_execution_margin)) {
  57. // think PHP's max exec time, minus this value to have time for timeouts etc.
  58. $this->max_execution_margin = common_config('http', 'connect_timeout') + 1;
  59. }
  60. return parent::__construct();
  61. }
  62. protected function verifyKey()
  63. {
  64. if ($this->qmkey !== common_config('opportunisticqm', 'qmkey')) {
  65. throw new RunQueueBadKeyException($this->qmkey);
  66. }
  67. }
  68. public function enqueue($object, $key)
  69. {
  70. // Nothing to do, should never get called
  71. throw new ServerException('OpportunisticQueueManager::enqueue should never be called');
  72. }
  73. public function canContinue()
  74. {
  75. $time_passed = (hrtime(true) - $this->start_cpu_time) / 1000000000;
  76. // Only continue if limit values are sane
  77. if ($time_passed <= 0 && (!is_null($this->max_queue_items) && $this->max_queue_items <= 0)) {
  78. return false;
  79. }
  80. // If too much time has passed, stop
  81. if ($time_passed >= $this->max_execution_time ||
  82. $time_passed > ini_get('max_execution_time') - $this->max_execution_margin) {
  83. return false;
  84. }
  85. // If we have a max-item-limit, check if it has been passed
  86. if (!is_null($this->max_queue_items) && $this->handled_items >= $this->max_queue_items) {
  87. return false;
  88. }
  89. return true;
  90. }
  91. public function poll()
  92. {
  93. $qm = $this->get();
  94. if ($qm->pollInterval() <= 0 && ! $qm instanceof UnQueueManager) {
  95. // Special case for UnQueueManager as it is a default plugin
  96. // and does not require queues, since it does everything immediately
  97. throw new ServerException('OpportunisticQM cannot work together' .
  98. 'with queues that do not implement polling');
  99. }
  100. ++$this->handled_items;
  101. return $qm->poll();
  102. }
  103. /**
  104. * Takes care of running through the queue items, returning when
  105. * the limits setup in __construct are met.
  106. *
  107. * @return true on workqueue finished, false if there are still items in the queue
  108. */
  109. public function runQueue()
  110. {
  111. while ($this->canContinue()) {
  112. if (!$this->poll()) {
  113. // Out of work
  114. return true;
  115. }
  116. }
  117. if ($this->handled_items > 0) {
  118. common_debug('Opportunistic queue manager passed execution time/item ' .
  119. 'handling limit without being out of work.');
  120. return true;
  121. } elseif ($this->verbosity > 1) {
  122. $time_passed = (hrtime(true) - $this->start_cpu_time) / 1000000000;
  123. common_debug('Opportunistic queue manager did not have time to start ' .
  124. "on this action (max: {$this->max_execution_time}" .
  125. " exceeded: {$time_passed}).");
  126. }
  127. return false;
  128. }
  129. }