Friendica Communications Platform (please note that this is a clone of the repository at github, issues are handled there) https://friendi.ca
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

265 lines
7.2 KiB

11 years ago
10 years ago
11 years ago
11 years ago
  1. <?php
  2. if (!file_exists("boot.php") AND (sizeof($_SERVER["argv"]) != 0)) {
  3. $directory = dirname($_SERVER["argv"][0]);
  4. if (substr($directory, 0, 1) != "/")
  5. $directory = $_SERVER["PWD"]."/".$directory;
  6. $directory = realpath($directory."/..");
  7. chdir($directory);
  8. }
  9. require_once("boot.php");
  10. function poller_run(&$argv, &$argc){
  11. global $a, $db;
  12. if(is_null($a)) {
  13. $a = new App;
  14. }
  15. if(is_null($db)) {
  16. @include(".htconfig.php");
  17. require_once("include/dba.php");
  18. $db = new dba($db_host, $db_user, $db_pass, $db_data);
  19. unset($db_host, $db_user, $db_pass, $db_data);
  20. };
  21. if (poller_max_connections_reached())
  22. return;
  23. if (App::maxload_reached())
  24. return;
  25. // Checking the number of workers
  26. if (poller_too_much_workers(1)) {
  27. poller_kill_stale_workers();
  28. return;
  29. }
  30. if(($argc <= 1) OR ($argv[1] != "no_cron")) {
  31. // Run the cron job that calls all other jobs
  32. proc_run("php","include/cron.php");
  33. // Run the cronhooks job separately from cron for being able to use a different timing
  34. proc_run("php","include/cronhooks.php");
  35. // Cleaning dead processes
  36. poller_kill_stale_workers();
  37. } else
  38. // Sleep four seconds before checking for running processes again to avoid having too many workers
  39. sleep(4);
  40. // Checking number of workers
  41. if (poller_too_much_workers(2))
  42. return;
  43. $starttime = time();
  44. while ($r = q("SELECT * FROM `workerqueue` WHERE `executed` = '0000-00-00 00:00:00' ORDER BY `created` LIMIT 1")) {
  45. // Constantly check the number of available database connections to let the frontend be accessible at any time
  46. if (poller_max_connections_reached())
  47. return;
  48. // Count active workers and compare them with a maximum value that depends on the load
  49. if (poller_too_much_workers(3))
  50. return;
  51. q("UPDATE `workerqueue` SET `executed` = '%s', `pid` = %d WHERE `id` = %d AND `executed` = '0000-00-00 00:00:00'",
  52. dbesc(datetime_convert()),
  53. intval(getmypid()),
  54. intval($r[0]["id"]));
  55. // Assure that there are no tasks executed twice
  56. $id = q("SELECT `id` FROM `workerqueue` WHERE `id` = %d AND `pid` = %d",
  57. intval($r[0]["id"]),
  58. intval(getmypid()));
  59. if (!$id) {
  60. logger("Queue item ".$r[0]["id"]." was executed multiple times - skip this execution", LOGGER_DEBUG);
  61. continue;
  62. }
  63. $argv = json_decode($r[0]["parameter"]);
  64. $argc = count($argv);
  65. // Check for existance and validity of the include file
  66. $include = $argv[0];
  67. if (!validate_include($include)) {
  68. logger("Include file ".$argv[0]." is not valid!");
  69. q("DELETE FROM `workerqueue` WHERE `id` = %d", intval($r[0]["id"]));
  70. continue;
  71. }
  72. require_once($include);
  73. $funcname=str_replace(".php", "", basename($argv[0]))."_run";
  74. if (function_exists($funcname)) {
  75. logger("Process ".getmypid()." - ID ".$r[0]["id"].": ".$funcname." ".$r[0]["parameter"]);
  76. $funcname($argv, $argc);
  77. logger("Process ".getmypid()." - ID ".$r[0]["id"].": ".$funcname." - done");
  78. q("DELETE FROM `workerqueue` WHERE `id` = %d", intval($r[0]["id"]));
  79. } else
  80. logger("Function ".$funcname." does not exist");
  81. // Quit the poller once every hour
  82. if (time() > ($starttime + 3600))
  83. return;
  84. }
  85. }
  86. /**
  87. * @brief Checks if the number of database connections has reached a critical limit.
  88. *
  89. * @return bool Are more than 3/4 of the maximum connections used?
  90. */
  91. function poller_max_connections_reached() {
  92. // Fetch the max value from the config. This is needed when the system cannot detect the correct value by itself.
  93. $max = get_config("system", "max_connections");
  94. if ($max == 0) {
  95. // the maximum number of possible user connections can be a system variable
  96. $r = q("SHOW VARIABLES WHERE `variable_name` = 'max_user_connections'");
  97. if ($r)
  98. $max = $r[0]["Value"];
  99. // Or it can be granted. This overrides the system variable
  100. $r = q("SHOW GRANTS");
  101. if ($r)
  102. foreach ($r AS $grants) {
  103. $grant = array_pop($grants);
  104. if (stristr($grant, "GRANT USAGE ON"))
  105. if (preg_match("/WITH MAX_USER_CONNECTIONS (\d*)/", $grant, $match))
  106. $max = $match[1];
  107. }
  108. }
  109. // If $max is set we will use the processlist to determine the current number of connections
  110. // The processlist only shows entries of the current user
  111. if ($max != 0) {
  112. $r = q("SHOW PROCESSLIST");
  113. if (!$r)
  114. return false;
  115. $used = count($r);
  116. logger("Connection usage (user values): ".$used."/".$max, LOGGER_DEBUG);
  117. $level = $used / $max;
  118. if ($level >= (3/4)) {
  119. logger("Maximum level (3/4) of user connections reached: ".$used."/".$max);
  120. return true;
  121. }
  122. }
  123. // We will now check for the system values.
  124. // This limit could be reached although the user limits are fine.
  125. $r = q("SHOW VARIABLES WHERE `variable_name` = 'max_connections'");
  126. if (!$r)
  127. return false;
  128. $max = intval($r[0]["Value"]);
  129. if ($max == 0)
  130. return false;
  131. $r = q("SHOW STATUS WHERE `variable_name` = 'Threads_connected'");
  132. if (!$r)
  133. return false;
  134. $used = intval($r[0]["Value"]);
  135. if ($used == 0)
  136. return false;
  137. logger("Connection usage (system values): ".$used."/".$max, LOGGER_DEBUG);
  138. $level = $used / $max;
  139. if ($level < (3/4))
  140. return false;
  141. logger("Maximum level (3/4) of system connections reached: ".$used."/".$max);
  142. return true;
  143. }
  144. /**
  145. * @brief fix the queue entry if the worker process died
  146. *
  147. */
  148. function poller_kill_stale_workers() {
  149. $r = q("SELECT `pid`, `executed` FROM `workerqueue` WHERE `executed` != '0000-00-00 00:00:00'");
  150. if (!is_array($r) || count($r) == 0) {
  151. // No processing here needed
  152. return;
  153. }
  154. foreach($r AS $pid)
  155. if (!posix_kill($pid["pid"], 0))
  156. q("UPDATE `workerqueue` SET `executed` = '0000-00-00 00:00:00', `pid` = 0 WHERE `pid` = %d",
  157. intval($pid["pid"]));
  158. else {
  159. // Kill long running processes
  160. $duration = (time() - strtotime($pid["executed"])) / 60;
  161. if ($duration > 180) {
  162. logger("Worker process ".$pid["pid"]." took more than 3 hours. It will be killed now.");
  163. posix_kill($pid["pid"], SIGTERM);
  164. // Question: If a process is stale: Should we remove it or should we reschedule it?
  165. // By now we rescheduling it. It's maybe not the wisest decision?
  166. q("UPDATE `workerqueue` SET `executed` = '0000-00-00 00:00:00', `pid` = 0 WHERE `pid` = %d",
  167. intval($pid["pid"]));
  168. } else
  169. logger("Worker process ".$pid["pid"]." now runs for ".round($duration)." minutes. That's okay.", LOGGER_DEBUG);
  170. }
  171. }
  172. function poller_too_much_workers($stage) {
  173. $queues = get_config("system", "worker_queues");
  174. if ($queues == 0)
  175. $queues = 4;
  176. $active = poller_active_workers();
  177. // Decrease the number of workers at higher load
  178. $load = current_load();
  179. if($load) {
  180. $maxsysload = intval(get_config('system','maxloadavg'));
  181. if($maxsysload < 1)
  182. $maxsysload = 50;
  183. $maxworkers = $queues;
  184. // Some magical mathemathics to reduce the workers
  185. $exponent = 3;
  186. $slope = $maxworkers / pow($maxsysload, $exponent);
  187. $queues = ceil($slope * pow(max(0, $maxsysload - $load), $exponent));
  188. logger("Current load stage ".$stage.": ".$load." - maximum: ".$maxsysload." - current queues: ".$active." - maximum: ".$queues, LOGGER_DEBUG);
  189. }
  190. return($active >= $queues);
  191. }
  192. function poller_active_workers() {
  193. $workers = q("SELECT COUNT(*) AS `workers` FROM `workerqueue` WHERE `executed` != '0000-00-00 00:00:00'");
  194. return($workers[0]["workers"]);
  195. }
  196. if (array_search(__file__,get_included_files())===0){
  197. poller_run($_SERVER["argv"],$_SERVER["argc"]);
  198. killme();
  199. }
  200. ?>