]> git.mxchange.org Git - friendica.git/blobdiff - src/Core/Worker.php
Refactor dynamic App::getConfig() to static DI::config()
[friendica.git] / src / Core / Worker.php
index 236f8b408479a16d018dfeab59dbafacf11552a7..a87fef07713b7055f4ccfb6e825e4d241254d6ad 100644 (file)
@@ -4,8 +4,9 @@
  */
 namespace Friendica\Core;
 
-use Friendica\BaseObject;
+use Friendica\Core;
 use Friendica\Database\DBA;
+use Friendica\DI;
 use Friendica\Model\Process;
 use Friendica\Util\DateTimeFormat;
 use Friendica\Util\Network;
@@ -186,7 +187,7 @@ class Worker
        private static function deferredEntries()
        {
                $stamp = (float)microtime(true);
-               $count = DBA::count('workerqueue', ["NOT `done` AND `pid` = 0 AND `next_try` > ?", DateTimeFormat::utcNow()]);
+               $count = DBA::count('workerqueue', ["NOT `done` AND `pid` = 0 AND `retrial` > ?", 0]);
                self::$db_duration += (microtime(true) - $stamp);
                self::$db_duration_count += (microtime(true) - $stamp);
                return $count;
@@ -670,7 +671,7 @@ class Worker
                                $waiting_processes = 0;
                                // Now adding all processes with workerqueue entries
                                $stamp = (float)microtime(true);
-                               $jobs = DBA::p("SELECT COUNT(*) AS `entries`, `priority` FROM `workerqueue` WHERE NOT `done` AND `next_try` < ? GROUP BY `priority`", DateTimeFormat::utcNow());
+                               $jobs = DBA::p("SELECT COUNT(*) AS `entries`, `priority` FROM `workerqueue` WHERE NOT `done` GROUP BY `priority`");
                                self::$db_duration += (microtime(true) - $stamp);
                                self::$db_duration_stat += (microtime(true) - $stamp);
                                while ($entry = DBA::fetch($jobs)) {
@@ -686,10 +687,8 @@ class Worker
                                        DBA::close($processes);
                                }
                                DBA::close($jobs);
-                               $entries = $deferred + $waiting_processes;
                        } else {
-                               $entries = self::totalEntries();
-                               $waiting_processes = max(0, $entries - $deferred);
+                               $waiting_processes =  self::totalEntries();
                                $stamp = (float)microtime(true);
                                $jobs = DBA::p("SELECT COUNT(*) AS `running`, `priority` FROM `process` INNER JOIN `workerqueue` ON `workerqueue`.`pid` = `process`.`pid` AND NOT `done` GROUP BY `priority` ORDER BY `priority`");
                                self::$db_duration += (microtime(true) - $stamp);
@@ -702,6 +701,8 @@ class Worker
                                DBA::close($jobs);
                        }
 
+                       $waiting_processes -= $deferred;
+
                        $listitem[0] = "0:" . max(0, $idle_workers);
 
                        $processlist .= ' ('.implode(', ', $listitem).')';
@@ -1084,7 +1085,9 @@ class Worker
 
                $args = ['no_cron' => !$do_cron];
 
-               get_app()->proc_run($command, $args);
+               $a = get_app();
+               $process = new Core\Process($a->getLogger(), $a->getMode(), DI::config(), $a->getBasePath());
+               $process->run($command, $args);
 
                // after spawning we have to remove the flag.
                if (Config::get('system', 'worker_daemon_mode', false)) {
@@ -1101,7 +1104,7 @@ class Worker
         * or: Worker::add(PRIORITY_HIGH, "Notifier", Delivery::DELETION, $drop_id);
         * or: Worker::add(array('priority' => PRIORITY_HIGH, 'dont_fork' => true), "CreateShadowEntry", $post_id);
         *
-        * @return boolean "false" if proc_run couldn't be executed
+        * @return boolean "false" if worker queue entry already existed or there had been an error
         * @throws \Friendica\Network\HTTPException\InternalServerErrorException
         * @note $cmd and string args are surrounded with ""
         *
@@ -1126,7 +1129,7 @@ class Worker
 
                $priority = PRIORITY_MEDIUM;
                // Don't fork from frontend tasks by default
-               $dont_fork = Config::get("system", "worker_dont_fork", false) || !\get_app()->isBackend();
+               $dont_fork = Config::get("system", "worker_dont_fork", false) || !\get_app()->getMode()->isBackend();
                $created = DateTimeFormat::utcNow();
                $force_priority = false;
 
@@ -1151,6 +1154,7 @@ class Worker
 
                $parameters = json_encode($args);
                $found = DBA::exists('workerqueue', ['parameter' => $parameters, 'done' => false]);
+               $added = false;
 
                // Quit if there was a database error - a precaution for the update process to 3.5.3
                if (DBA::errorNo() != 0) {
@@ -1158,19 +1162,22 @@ class Worker
                }
 
                if (!$found) {
-                       DBA::insert('workerqueue', ['parameter' => $parameters, 'created' => $created, 'priority' => $priority]);
+                       $added = DBA::insert('workerqueue', ['parameter' => $parameters, 'created' => $created, 'priority' => $priority]);
+                       if (!$added) {
+                               return false;
+                       }
                } elseif ($force_priority) {
                        DBA::update('workerqueue', ['priority' => $priority], ['parameter' => $parameters, 'done' => false, 'pid' => 0]);
                }
 
                // Should we quit and wait for the worker to be called as a cronjob?
                if ($dont_fork) {
-                       return true;
+                       return $added;
                }
 
                // If there is a lock then we don't have to check for too much worker
                if (!Lock::acquire('worker', 0)) {
-                       return true;
+                       return $added;
                }
 
                // If there are already enough workers running, don't fork another one
@@ -1178,60 +1185,93 @@ class Worker
                Lock::release('worker');
 
                if ($quit) {
-                       return true;
+                       return $added;
                }
 
                // We tell the daemon that a new job entry exists
                if (Config::get('system', 'worker_daemon_mode', false)) {
                        // We don't have to set the IPC flag - this is done in "tooMuchWorkers"
-                       return true;
+                       return $added;
                }
 
                // Now call the worker to execute the jobs that we just added to the queue
                self::spawnWorker();
 
-               return true;
+               return $added;
+       }
+
+       /**
+        * Returns the next retrial level for worker jobs.
+        * This function will skip levels when jobs are older.
+        *
+        * @param array $queue Worker queue entry
+        * @param integer $max_level maximum retrial level
+        * @return integer the next retrial level value
+        */
+       private static function getNextRetrial($queue, $max_level)
+       {
+               $created = strtotime($queue['created']);
+               $retrial_time = time() - $created;
+
+               $new_retrial = $queue['retrial'] + 1;
+               $total = 0;
+               for ($retrial = 0; $retrial <= $max_level + 1; ++$retrial) {
+                       $delay = (($retrial + 3) ** 4) + (rand(1, 30) * ($retrial + 1));
+                       $total += $delay;
+                       if (($total < $retrial_time) && ($retrial > $queue['retrial'])) {
+                               $new_retrial = $retrial;
+                       }
+               }
+               Logger::info('New retrial for task', ['id' => $queue['id'], 'created' => $queue['created'], 'old' => $queue['retrial'], 'new' => $new_retrial]);
+               return $new_retrial;
        }
 
        /**
         * Defers the current worker entry
+        * @return boolean had the entry been deferred?
         */
        public static function defer()
        {
-               if (empty(BaseObject::getApp()->queue)) {
-                       return;
+               if (empty(DI::app()->queue)) {
+                       return false;
                }
 
-               $queue = BaseObject::getApp()->queue;
+               $queue = DI::app()->queue;
 
                $retrial = $queue['retrial'];
                $id = $queue['id'];
                $priority = $queue['priority'];
 
-               if ($retrial > 14) {
-                       Logger::log('Id ' . $id . ' had been tried 14 times. We stop now.', Logger::DEBUG);
-                       return;
+               $max_level = Config::get('system', 'worker_defer_limit');
+
+               $new_retrial = self::getNextRetrial($queue, $max_level);
+
+               if ($new_retrial > $max_level) {
+                       Logger::info('The task exceeded the maximum retry count', ['id' => $id, 'created' => $queue['created'], 'old_prio' => $queue['priority'], 'old_retrial' => $queue['retrial'], 'max_level' => $max_level, 'retrial' => $new_retrial]);
+                       return false;
                }
 
                // Calculate the delay until the next trial
-               $delay = (($retrial + 3) ** 4) + (rand(1, 30) * ($retrial + 1));
+               $delay = (($new_retrial + 2) ** 4) + (rand(1, 30) * ($new_retrial));
                $next = DateTimeFormat::utc('now + ' . $delay . ' seconds');
 
-               if (($priority < PRIORITY_MEDIUM) && ($retrial > 2)) {
+               if (($priority < PRIORITY_MEDIUM) && ($new_retrial > 3)) {
                        $priority = PRIORITY_MEDIUM;
-               } elseif (($priority < PRIORITY_LOW) && ($retrial > 5)) {
+               } elseif (($priority < PRIORITY_LOW) && ($new_retrial > 6)) {
                        $priority = PRIORITY_LOW;
-               } elseif (($priority < PRIORITY_NEGLIGIBLE) && ($retrial > 7)) {
+               } elseif (($priority < PRIORITY_NEGLIGIBLE) && ($new_retrial > 8)) {
                        $priority = PRIORITY_NEGLIGIBLE;
                }
 
-               Logger::log('Defer execution ' . $retrial . ' of id ' . $id . ' to ' . $next . ' - priority old/new: ' . $queue['priority'] . '/' . $priority, Logger::DEBUG);
+               Logger::info('Deferred task', ['id' => $id, 'retrial' => $new_retrial, 'created' => $queue['created'], 'next_execution' => $next, 'old_prio' => $queue['priority'], 'new_prio' => $priority]);
 
                $stamp = (float)microtime(true);
-               $fields = ['retrial' => $retrial + 1, 'next_try' => $next, 'executed' => DBA::NULL_DATETIME, 'pid' => 0, 'priority' => $priority];
+               $fields = ['retrial' => $new_retrial, 'next_try' => $next, 'executed' => DBA::NULL_DATETIME, 'pid' => 0, 'priority' => $priority];
                DBA::update('workerqueue', $fields, ['id' => $id]);
                self::$db_duration += (microtime(true) - $stamp);
                self::$db_duration_write += (microtime(true) - $stamp);
+
+               return true;
        }
 
        /**