X-Git-Url: https://git.mxchange.org/?a=blobdiff_plain;f=src%2FCore%2FWorker.php;h=897a80111d67d4d6cc6c7309c8ee645ae3894d02;hb=1b6727fb9c1aa31a606af0b2b7d7153605fb838c;hp=140d0e014d5f348aa7f910909bba5472639cc3e3;hpb=219182d41ea84faec0164aed80d0dfbe15f2067d;p=friendica.git diff --git a/src/Core/Worker.php b/src/Core/Worker.php index 140d0e014d..897a80111d 100644 --- a/src/Core/Worker.php +++ b/src/Core/Worker.php @@ -4,15 +4,11 @@ */ namespace Friendica\Core; -use Friendica\Core\Addon; -use Friendica\Core\Config; -use Friendica\Core\System; +use dba; use Friendica\Database\DBM; use Friendica\Model\Process; use Friendica\Util\DateTimeFormat; -use Friendica\Util\Lock; use Friendica\Util\Network; -use dba; require_once 'include/dba.php'; @@ -45,7 +41,7 @@ class Worker self::$up_start = microtime(true); // At first check the maximum load. We shouldn't continue with a high load - if ($a->maxload_reached()) { + if ($a->isMaxLoadReached()) { logger('Pre check: maximum load reached, quitting.', LOGGER_DEBUG); return; } @@ -54,9 +50,9 @@ class Worker self::startProcess(); // Kill stale processes every 5 minutes - $last_cleanup = Config::get('system', 'poller_last_cleaned', 0); + $last_cleanup = Config::get('system', 'worker_last_cleaned', 0); if (time() > ($last_cleanup + 300)) { - Config::set('system', 'poller_last_cleaned', time()); + Config::set('system', 'worker_last_cleaned', time()); self::killStaleWorkers(); } @@ -79,7 +75,7 @@ class Worker } // Possibly there are too much database processes that block the system - if ($a->max_processes_reached()) { + if ($a->isMaxProcessesReached()) { logger('Pre check: maximum processes reached, quitting.', LOGGER_DEBUG); return; } @@ -108,16 +104,16 @@ class Worker } // If possible we will fetch new jobs for this worker - if (!$refetched && Lock::set('poller_worker_process', 0)) { + if (!$refetched && Lock::acquire('worker_process', 0)) { $stamp = (float)microtime(true); $refetched = self::findWorkerProcesses($passing_slow); self::$db_duration += (microtime(true) - $stamp); - Lock::remove('poller_worker_process'); + Lock::release('worker_process'); } } // To avoid the quitting of multiple workers only one worker at a time will execute the check - if (Lock::set('poller_worker', 0)) { + if (Lock::acquire('worker', 0)) { $stamp = (float)microtime(true); // Count active workers and compare them with a maximum value that depends on the load if (self::tooMuchWorkers()) { @@ -130,7 +126,7 @@ class Worker logger('Memory limit reached, quitting.', LOGGER_DEBUG); return; } - Lock::remove('poller_worker'); + Lock::release('worker'); self::$db_duration += (microtime(true) - $stamp); } @@ -140,7 +136,12 @@ class Worker return; } } - logger("Couldn't select a workerqueue entry, quitting.", LOGGER_DEBUG); + + // Cleaning up. Possibly not needed, but it doesn't harm anything. + if (Config::get('system', 'worker_daemon_mode', false)) { + self::IPCSetJobState(false); + } + logger("Couldn't select a workerqueue entry, quitting process " . getmypid() . ".", LOGGER_DEBUG); } /** @@ -150,12 +151,7 @@ class Worker */ private static function totalEntries() { - $s = dba::fetch_first("SELECT COUNT(*) AS `total` FROM `workerqueue` WHERE `executed` <= ? AND NOT `done`", NULL_DATE); - if (DBM::is_result($s)) { - return $s["total"]; - } else { - return 0; - } + return dba::count('workerqueue', ["`executed` <= ? AND NOT `done`", NULL_DATE]); } /** @@ -201,13 +197,13 @@ class Worker $mypid = getmypid(); // Quit when in maintenance - if (Config::get('system', 'maintenance', true)) { + if (Config::get('system', 'maintenance', false, true)) { logger("Maintenance mode - quit process ".$mypid, LOGGER_DEBUG); return false; } // Constantly check the number of parallel database processes - if ($a->max_processes_reached()) { + if ($a->isMaxProcessesReached()) { logger("Max processes reached for process ".$mypid, LOGGER_DEBUG); return false; } @@ -244,7 +240,7 @@ class Worker $stamp = (float)microtime(true); if (dba::update('workerqueue', ['done' => true], ['id' => $queue["id"]])) { - Config::set('system', 'last_poller_execution', DateTimeFormat::utcNow()); + Config::set('system', 'last_worker_execution', DateTimeFormat::utcNow()); } self::$db_duration = (microtime(true) - $stamp); @@ -285,7 +281,7 @@ class Worker $stamp = (float)microtime(true); if (dba::update('workerqueue', ['done' => true], ['id' => $queue["id"]])) { - Config::set('system', 'last_poller_execution', DateTimeFormat::utcNow()); + Config::set('system', 'last_worker_execution', DateTimeFormat::utcNow()); } self::$db_duration = (microtime(true) - $stamp); } else { @@ -313,7 +309,7 @@ class Worker $argc = count($argv); - $new_process_id = uniqid("wrk", true); + $new_process_id = System::processID("wrk"); logger("Process ".$mypid." - Prio ".$queue["priority"]." - ID ".$queue["id"].": ".$funcname." ".$queue["parameter"]." - Process PID: ".$new_process_id); @@ -325,6 +321,8 @@ class Worker $a->performance["start"] = microtime(true); $a->performance["database"] = 0; $a->performance["database_write"] = 0; + $a->performance["cache"] = 0; + $a->performance["cache_write"] = 0; $a->performance["network"] = 0; $a->performance["file"] = 0; $a->performance["rendering"] = 0; @@ -354,7 +352,7 @@ class Worker $a->process_id = $old_process_id; unset($a->queue); - $duration = number_format(microtime(true) - $stamp, 3); + $duration = (microtime(true) - $stamp); self::$up_start = microtime(true); @@ -389,9 +387,10 @@ class Worker if (Config::get("system", "profiler")) { $duration = microtime(true)-$a->performance["start"]; + $o = ''; if (Config::get("rendertime", "callstack")) { if (isset($a->callstack["database"])) { - $o = "\nDatabase Read:\n"; + $o .= "\nDatabase Read:\n"; foreach ($a->callstack["database"] as $func => $time) { $time = round($time, 3); if ($time > 0) { @@ -408,6 +407,24 @@ class Worker } } } + if (isset($a->callstack["dache"])) { + $o .= "\nCache Read:\n"; + foreach ($a->callstack["dache"] as $func => $time) { + $time = round($time, 3); + if ($time > 0) { + $o .= $func.": ".$time."\n"; + } + } + } + if (isset($a->callstack["dache_write"])) { + $o .= "\nCache Write:\n"; + foreach ($a->callstack["dache_write"] as $func => $time) { + $time = round($time, 3); + if ($time > 0) { + $o .= $func.": ".$time."\n"; + } + } + } if (isset($a->callstack["network"])) { $o .= "\nNetwork:\n"; foreach ($a->callstack["network"] as $func => $time) { @@ -417,18 +434,20 @@ class Worker } } } - } else { - $o = ''; } logger( "ID ".$queue["id"].": ".$funcname.": ".sprintf( - "DB: %s/%s, Net: %s, I/O: %s, Other: %s, Total: %s".$o, + "DB: %s/%s, Cache: %s/%s, Net: %s, I/O: %s, Other: %s, Total: %s".$o, number_format($a->performance["database"] - $a->performance["database_write"], 2), number_format($a->performance["database_write"], 2), + number_format($a->performance["cache"], 2), + number_format($a->performance["cache_write"], 2), number_format($a->performance["network"], 2), number_format($a->performance["file"], 2), - number_format($duration - ($a->performance["database"] + $a->performance["network"] + $a->performance["file"]), 2), + number_format($duration - ($a->performance["database"] + + $a->performance["cache"] + $a->performance["cache_write"] + + $a->performance["network"] + $a->performance["file"]), 2), number_format($duration, 2) ), LOGGER_DEBUG @@ -564,6 +583,7 @@ class Worker // We killed the stale process. // To avoid a blocking situation we reschedule the process at the beginning of the queue. // Additionally we are lowering the priority. (But not PRIORITY_CRITICAL) + $new_priority = $entry["priority"]; if ($entry["priority"] == PRIORITY_HIGH) { $new_priority = PRIORITY_MEDIUM; } elseif ($entry["priority"] == PRIORITY_MEDIUM) { @@ -577,7 +597,7 @@ class Worker ['id' => $entry["id"]] ); } else { - logger("Worker process ".$entry["pid"]." (".implode(" ", $argv).") now runs for ".round($duration)." of ".$max_duration." allowed minutes. That's okay.", LOGGER_DEBUG); + logger("Worker process ".$entry["pid"]." (".substr(json_encode($argv), 0, 50).") now runs for ".round($duration)." of ".$max_duration." allowed minutes. That's okay.", LOGGER_DEBUG); } } } @@ -601,12 +621,15 @@ class Worker if ($load) { $maxsysload = intval(Config::get("system", "maxloadavg", 50)); - $maxworkers = $queues; + /* Default exponent 3 causes queues to rapidly decrease as load increases. + * If you have 20 max queues at idle, then you get only 5 queues at 37.1% of $maxsysload. + * For some environments, this rapid decrease is not needed. + * With exponent 1, you could have 20 max queues at idle and 13 at 37% of $maxsysload. + */ + $exponent = intval(Config::get('system', 'worker_load_exponent', 3)); + $slope = pow(max(0, $maxsysload - $load) / $maxsysload, $exponent); + $queues = intval(ceil($slope * $maxqueues)); - // Some magical mathemathics to reduce the workers - $exponent = 3; - $slope = $maxworkers / pow($maxsysload, $exponent); - $queues = ceil($slope * pow(max(0, $maxsysload - $load), $exponent)); $processlist = ''; if (Config::get('system', 'worker_debug')) { @@ -664,13 +687,22 @@ class Worker logger("Load: ".$load."/".$maxsysload." - processes: ".$active."/".$entries.$processlist." - maximum: ".$queues."/".$maxqueues, LOGGER_DEBUG); // Are there fewer workers running as possible? Then fork a new one. - if (!Config::get("system", "worker_dont_fork") && ($queues > ($active + 1)) && ($entries > 1)) { + if (!Config::get("system", "worker_dont_fork", false) && ($queues > ($active + 1)) && ($entries > 1)) { logger("Active workers: ".$active."/".$queues." Fork a new worker.", LOGGER_DEBUG); - self::spawnWorker(); + if (Config::get('system', 'worker_daemon_mode', false)) { + self::IPCSetJobState(true); + } else { + self::spawnWorker(); + } } } - return $active >= $queues; + // if there are too much worker, we don't spawn a new one. + if (Config::get('system', 'worker_daemon_mode', false) && ($active > $queues)) { + self::IPCSetJobState(false); + } + + return $active > $queues; } /** @@ -680,9 +712,7 @@ class Worker */ private static function activeWorkers() { - $workers = dba::fetch_first("SELECT COUNT(*) AS `processes` FROM `process` WHERE `command` = 'Worker.php'"); - - return $workers["processes"]; + return dba::count('process', ['command' => 'Worker.php']); } /** @@ -768,7 +798,7 @@ class Worker $limit = min($queue_length, ceil($slope * pow($jobs, $exponent))); logger('Total: '.$jobs.' - Maximum: '.$queue_length.' - jobs per queue: '.$limit, LOGGER_DEBUG); - + $ids = []; if (self::passingSlow($highest_priority)) { // Are there waiting processes with a higher priority than the currently highest? $result = dba::select( @@ -849,7 +879,7 @@ class Worker dba::close($r); $stamp = (float)microtime(true); - if (!Lock::set('poller_worker_process')) { + if (!Lock::acquire('worker_process')) { return false; } self::$lock_duration = (microtime(true) - $stamp); @@ -858,7 +888,7 @@ class Worker $found = self::findWorkerProcesses($passing_slow); self::$db_duration += (microtime(true) - $stamp); - Lock::remove('poller_worker_process'); + Lock::release('worker_process'); if ($found) { $r = dba::select('workerqueue', [], ['pid' => getmypid(), 'done' => false]); @@ -935,9 +965,9 @@ class Worker self::clearProcesses(); - $workers = dba::fetch_first("SELECT COUNT(*) AS `processes` FROM `process` WHERE `command` = 'worker.php'"); + $workers = self::activeWorkers(); - if ($workers["processes"] == 0) { + if ($workers == 0) { self::callWorker(); } } @@ -975,12 +1005,23 @@ class Worker } /** + * @brief Spawns a new worker * @return void */ - public static function spawnWorker() + public static function spawnWorker($do_cron = false) { - $args = ["scripts/worker.php", "no_cron"]; + $args = ["bin/worker.php"]; + + if (!$do_cron) { + $args[] = "no_cron"; + } + get_app()->proc_run($args); + + // after spawning we have to remove the flag. + if (Config::get('system', 'worker_daemon_mode', false)) { + self::IPCSetJobState(false); + } } /** @@ -1015,7 +1056,7 @@ class Worker } $priority = PRIORITY_MEDIUM; - $dont_fork = Config::get("system", "worker_dont_fork"); + $dont_fork = Config::get("system", "worker_dont_fork", false); $created = DateTimeFormat::utcNow(); $run_parameter = array_shift($args); @@ -1052,18 +1093,24 @@ class Worker } // If there is a lock then we don't have to check for too much worker - if (!Lock::set('poller_worker', 0)) { + if (!Lock::acquire('worker', 0)) { return true; } // If there are already enough workers running, don't fork another one $quit = self::tooMuchWorkers(); - Lock::remove('poller_worker'); + Lock::release('worker'); if ($quit) { return true; } + // 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; + } + // Now call the worker to execute the jobs that we just added to the queue self::spawnWorker(); @@ -1096,4 +1143,33 @@ class Worker { return Process::deleteByPid(); } + + /** + * Set the flag if some job is waiting + * + * @brief Set the flag if some job is waiting + * @param boolean $jobs Is there a waiting job? + */ + public static function IPCSetJobState($jobs) + { + dba::update('worker-ipc', ['jobs' => $jobs], ['key' => 1], true); + } + + /** + * Checks if some worker job waits to be executed + * + * @brief Checks if some worker job waits to be executed + * @return bool + */ + public static function IPCJobsExists() + { + $row = dba::selectFirst('worker-ipc', ['jobs'], ['key' => 1]); + + // When we don't have a row, no job is running + if (!DBM::is_result($row)) { + return false; + } + + return (bool)$row['jobs']; + } }