diff --git a/classes/cron_processor.php b/classes/cron_processor.php index 59b66ab..31dc36b 100644 --- a/classes/cron_processor.php +++ b/classes/cron_processor.php @@ -42,6 +42,12 @@ class cron_processor implements processor { /** @var int */ protected $samplems; + /** @var bool */ + protected static $alreadyprofiling = false; + + /** @var bool */ + protected $hasoverlapped = false; + /** * Initialises the processor * @@ -51,7 +57,10 @@ public function init(manager $manager) { $this->sampletime = $manager->get_starttime(); $manager->get_timer()->setCallback(function () use ($manager) { - $this->on_interval($manager); + if (!$this->hasoverlapped) { + // Once overlapping has happened once, we prevent all future partial saving. + $this->on_interval($manager); + } }); // Preload config values to avoid DB access during processing. See manager::get_altconnection() for more information. @@ -61,7 +70,7 @@ public function init(manager $manager) { function () use ($manager) { $manager->get_timer()->stop(); $manager->get_profiler()->stop(); - $this->on_interval($manager); + $this->on_interval($manager, true); if ($this->tasksampleset) { $this->process($manager, microtime(true)); } @@ -91,8 +100,22 @@ public function get_min_duration(): float { * We then check for a new task with the current sample. * * @param manager $manager + * @param bool $isfinal Set to true when this is called during shutdown. */ - public function on_interval(manager $manager) { + public function on_interval(manager $manager, bool $isfinal = false) { + // We want to prevent doubling up of processing, so skip if an existing process is still executing. + // The profile logs will be kept and processed the next time. + if (self::$alreadyprofiling) { + $this->hasoverlapped = true; + debugging('tool_excimer: starting cron_processor::on_interval when previous call has not yet finished'); + if ($isfinal) { + // This should never happen. + debugging('tool_excimer: alreadyprofiling is true during final on_interval.'); + } + return; + } + self::$alreadyprofiling = true; + $profiler = $manager->get_profiler(); $log = $profiler->flush(); $memoryusage = memory_get_usage(); // Record and set initial memory usage at this point. @@ -135,6 +158,8 @@ public function on_interval(manager $manager) { // So it needs to be saved each loop. $this->sampletime = $sampletime; } + + self::$alreadyprofiling = false; } /** diff --git a/classes/web_processor.php b/classes/web_processor.php index 2925f3e..c8b0d28 100644 --- a/classes/web_processor.php +++ b/classes/web_processor.php @@ -42,6 +42,10 @@ class web_processor implements processor { protected $samplems; /** @var bool */ protected $partialsave; + /** @var bool */ + protected static $alreadyprofiling = false; + /** @var bool */ + protected $hasoverlapped = false; /** * Construct the web processor. @@ -78,7 +82,10 @@ public function init(manager $manager) { if ($this->partialsave) { $manager->get_timer()->setCallback(function () use ($manager) { - $this->process($manager, false); + // Once overlapping has happened once, we prevent all future partial saving. + if (!$this->hasoverlapped) { + $this->process($manager, false); + } }); } @@ -111,6 +118,19 @@ public function get_min_duration(): float { * @throws \dml_exception */ public function process(manager $manager, bool $isfinal) { + // We want to prevent overlapping of processing, so skip if an existing process is still executing. + // The profile logs will be kept and processed the next time. + if (self::$alreadyprofiling) { + $this->hasoverlapped = true; + debugging('tool_excimer: starting web_processor::process when previous process has not yet finished'); + if ($isfinal) { + // This should never happen. + debugging('tool_excimer: alreadyprofiling is true during final process.'); + } + return; + } + self::$alreadyprofiling = true; + $log = $manager->get_profiler()->flush(); $this->sampleset->add_many_samples($log); @@ -134,5 +154,7 @@ public function process(manager $manager, bool $isfinal) { } $this->profile->save_record(); } + + self::$alreadyprofiling = false; } }