forked from GNUsocial/gnu-social
		
	Cron split into Cronish and OpportunisticQM
/main/cron changed to /main/runqueue The key-required functionality is not throughly tested yet.
This commit is contained in:
		| @@ -294,8 +294,9 @@ $default = | ||||
|         'plugins' => | ||||
|         array('core' => array( | ||||
|                             'AuthCrypt' => array(), | ||||
|                             'Cron' => array(), | ||||
|                             'Cronish' => array(), | ||||
|                             'LRDD' => array(), | ||||
|                             'OpportunisticQM' => array(), | ||||
|                             'StrictTransportSecurity' => array(), | ||||
|                         ), | ||||
|               'default' => array( | ||||
|   | ||||
| @@ -67,8 +67,8 @@ abstract class QueueManager extends IoManager | ||||
|                     self::$qm = new UnQueueManager(); | ||||
|                 } else { | ||||
|                     switch ($type) { | ||||
|                      case 'cron': | ||||
|                         self::$qm = new CronQueueManager(); | ||||
|                      case 'opportunistic': | ||||
|                         self::$qm = new OpportunisticQueueManager(); | ||||
|                         break; | ||||
|                      case 'db': | ||||
|                         self::$qm = new DBQueueManager(); | ||||
|   | ||||
							
								
								
									
										42
									
								
								plugins/Cronish/CronishPlugin.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										42
									
								
								plugins/Cronish/CronishPlugin.php
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,42 @@ | ||||
| <?php | ||||
|  | ||||
| class CronishPlugin extends Plugin { | ||||
|     public function onCronHourly() | ||||
|     { | ||||
|         common_debug('CRON: Running hourly cron job!'); | ||||
|     } | ||||
|  | ||||
|     public function onCronDaily() | ||||
|     { | ||||
|         common_debug('CRON: Running daily cron job!'); | ||||
|     } | ||||
|  | ||||
|     public function onCronWeekly() | ||||
|     { | ||||
|         common_debug('CRON: Running weekly cron job!'); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * When the page has finished rendering, let's do some cron jobs | ||||
|      * if we have the time. | ||||
|      */ | ||||
|     public function onEndActionExecute($status, Action $action) | ||||
|     { | ||||
|         $cron = new Cronish();  | ||||
|         $cron->callTimedEvents(); | ||||
|  | ||||
|         return true; | ||||
|     } | ||||
|  | ||||
|     public function onPluginVersion(&$versions) | ||||
|     { | ||||
|         $versions[] = array('name' => 'Cronish', | ||||
|                             'version' => GNUSOCIAL_VERSION, | ||||
|                             'author' => 'Mikael Nordfeldth', | ||||
|                             'homepage' => 'http://www.gnu.org/software/social/', | ||||
|                             'description' => | ||||
|                             // TRANS: Plugin description. | ||||
|                             _m('Cronish plugin that executes events on a near-hour/day/week basis.')); | ||||
|         return true; | ||||
|     } | ||||
| } | ||||
							
								
								
									
										58
									
								
								plugins/Cronish/lib/cronish.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										58
									
								
								plugins/Cronish/lib/cronish.php
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,58 @@ | ||||
| <?php | ||||
| /** | ||||
|  * GNU social cron-on-visit class | ||||
|  * | ||||
|  * Keeps track, through Config dataobject class, of relative time since the  | ||||
|  * last run in order to to run event handlers with certain intervals. | ||||
|  * | ||||
|  * @category  Cron | ||||
|  * @package   GNUsocial | ||||
|  * @author    Mikael Nordfeldth <mmn@hethane.se> | ||||
|  * @copyright 2013 Free Software Foundation, Inc. | ||||
|  * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0 | ||||
|  * @link      http://status.net/ | ||||
|  */ | ||||
| class Cronish | ||||
| { | ||||
|     /** | ||||
|      * Will call events as close as it gets to one hour. Event handlers | ||||
|      * which use this MUST be as quick as possible, maybe only adding a | ||||
|      * queue item to be handled later or something. Otherwise execution | ||||
|      * will timeout for PHP - or at least cause unnecessary delays for | ||||
|      * the unlucky user who visits the site exactly at one of these events. | ||||
|      */ | ||||
|     public function callTimedEvents() | ||||
|     { | ||||
|         $timers = array('hourly' => 3600, | ||||
|                         'daily'  => 86400, | ||||
|                         'weekly' => 604800); | ||||
|  | ||||
|         foreach($timers as $name=>$interval) { | ||||
|             $run = false; | ||||
|  | ||||
|             $lastrun = new Config(); | ||||
|             $lastrun->section = 'cron'; | ||||
|             $lastrun->setting = 'last_' . $name; | ||||
|             $found = $lastrun->find(true); | ||||
|  | ||||
|             if (!$found) { | ||||
|                 $lastrun->value = time(); | ||||
|                 if ($lastrun->insert() === false) { | ||||
|                     common_log(LOG_WARNING, "Could not save 'cron' setting '{$name}'"); | ||||
|                     continue; | ||||
|                 } | ||||
|                 $run = true; | ||||
|             } elseif ($lastrun->value < time() - $interval) { | ||||
|                 $orig    = clone($lastrun); | ||||
|                 $lastrun->value = time(); | ||||
|                 $lastrun->update($orig); | ||||
|                 $run = true; | ||||
|             } | ||||
|  | ||||
|             if ($run === true) { | ||||
|                 // such as CronHourly, CronDaily, CronWeekly | ||||
|                 Event::handle('Cron' . ucfirst($name)); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
							
								
								
									
										46
									
								
								plugins/OpportunisticQM/OpportunisticQMPlugin.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										46
									
								
								plugins/OpportunisticQM/OpportunisticQMPlugin.php
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,46 @@ | ||||
| <?php | ||||
|  | ||||
| class OpportunisticQMPlugin extends Plugin { | ||||
|     public $qmkey = false; | ||||
|     public $secs_per_action = 1; // total seconds to run script per action | ||||
|     public $rel_to_pageload = true;  // relative to pageload or queue start | ||||
|  | ||||
|     public function onRouterInitialized($m) | ||||
|     { | ||||
|         $m->connect('main/runqueue', array('action' => 'runqueue')); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * When the page has finished rendering, let's do some cron jobs | ||||
|      * if we have the time. | ||||
|      */ | ||||
|     public function onEndActionExecute($status, Action $action) | ||||
|     { | ||||
|         if ($action instanceof RunqueueAction) { | ||||
|             return true; | ||||
|         } | ||||
|  | ||||
|         global $_startTime; | ||||
|  | ||||
|         $args = array( | ||||
|                     'qmkey' => common_config('opportunisticqm', 'qmkey'), | ||||
|                     'max_execution_time' => $this->secs_per_action, | ||||
|                     'started_at'      => $this->rel_to_pageload ? $_startTime : null, | ||||
|                 ); | ||||
|         $qm = new OpportunisticQueueManager($args);  | ||||
|         $qm->runQueue(); | ||||
|         return true; | ||||
|     } | ||||
|  | ||||
|     public function onPluginVersion(&$versions) | ||||
|     { | ||||
|         $versions[] = array('name' => 'OpportunisticQM', | ||||
|                             'version' => GNUSOCIAL_VERSION, | ||||
|                             'author' => 'Mikael Nordfeldth', | ||||
|                             'homepage' => 'http://www.gnu.org/software/social/', | ||||
|                             'description' => | ||||
|                             // TRANS: Plugin description. | ||||
|                             _m('Opportunistic queue manager plugin for background processing.')); | ||||
|         return true; | ||||
|     } | ||||
| } | ||||
							
								
								
									
										61
									
								
								plugins/OpportunisticQM/actions/runqueue.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										61
									
								
								plugins/OpportunisticQM/actions/runqueue.php
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,61 @@ | ||||
| <?php | ||||
| /* | ||||
|  * StatusNet - the distributed open-source microblogging tool | ||||
|  * Copyright (C) 2008, 2009, StatusNet, Inc. | ||||
|  * | ||||
|  * This program is free software: you can redistribute it and/or modify | ||||
|  * it under the terms of the GNU Affero General Public License as published by | ||||
|  * the Free Software Foundation, either version 3 of the License, or | ||||
|  * (at your option) any later version. | ||||
|  * | ||||
|  * This program is distributed in the hope that it will be useful, | ||||
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|  * GNU Affero General Public License for more details. | ||||
|  * | ||||
|  * You should have received a copy of the GNU Affero General Public License | ||||
|  * along with this program.  If not, see <http://www.gnu.org/licenses/>. | ||||
|  */ | ||||
|  | ||||
| if (!defined('GNUSOCIAL')) { exit(1); } | ||||
|  | ||||
| class RunqueueAction extends Action | ||||
| { | ||||
|     protected $qm = null; | ||||
|  | ||||
|     protected function prepare(array $args=array()) | ||||
|     { | ||||
|         parent::prepare($args); | ||||
|  | ||||
|         $args = array(); | ||||
|  | ||||
|         foreach (array('qmkey') as $key) { | ||||
|             if ($this->arg($key) !== null) { | ||||
|                 $args[$key] = $this->arg($key); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         try { | ||||
|             $this->qm = new OpportunisticQueueManager($args); | ||||
|         } catch (RunQueueBadKeyException $e) { | ||||
|             return false; | ||||
|         } | ||||
|  | ||||
|         header('Content-type: text/plain; charset=utf-8'); | ||||
|  | ||||
|         return true; | ||||
|     } | ||||
|  | ||||
|     protected function handle() { | ||||
|         // We don't need any of the parent functionality from parent::handle() here. | ||||
|  | ||||
|         // runQueue is a loop that works until limits have passed or there is no more work | ||||
|         if ($this->qm->runQueue() === true) { | ||||
|             // We don't have any more work | ||||
|             $this->text('0'); | ||||
|         } else { | ||||
|             // There were still items left in queue when we aborted | ||||
|             $this->text('1'); | ||||
|         } | ||||
|     } | ||||
| } | ||||
							
								
								
									
										102
									
								
								plugins/OpportunisticQM/lib/opportunisticqueuemanager.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										102
									
								
								plugins/OpportunisticQM/lib/opportunisticqueuemanager.php
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,102 @@ | ||||
| <?php | ||||
| /** | ||||
|  * GNU social queue-manager-on-visit class | ||||
|  * | ||||
|  * Will run events for a certain time, or until finished. | ||||
|  * | ||||
|  * Configure remote key if wanted with $config['opportunisticqm']['qmkey'] and | ||||
|  * use with /main/runqueue?qmkey=abc123 | ||||
|  * | ||||
|  * @category  Cron | ||||
|  * @package   GNUsocial | ||||
|  * @author    Mikael Nordfeldth <mmn@hethane.se> | ||||
|  * @copyright 2013 Free Software Foundation, Inc. | ||||
|  * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0 | ||||
|  * @link      http://status.net/ | ||||
|  */ | ||||
| class OpportunisticQueueManager extends DBQueueManager | ||||
| { | ||||
|     protected $qmkey = false; | ||||
|     protected $max_execution_time = null; | ||||
|     protected $max_queue_items = null; | ||||
|  | ||||
|     protected $started_at = null; | ||||
|     protected $handled_items = 0; | ||||
|  | ||||
|     const MAXEXECTIME = 30; // typically just used for the /main/cron action | ||||
|  | ||||
|     public function __construct(array $args=array()) { | ||||
|         foreach (get_class_vars(get_class($this)) as $key=>$val) { | ||||
|             if (array_key_exists($key, $args)) { | ||||
|                 $this->$key = $args[$key]; | ||||
|             } | ||||
|         } | ||||
|         $this->verifyKey(); | ||||
|  | ||||
|         if ($this->started_at === null) { | ||||
|             $this->started_at = time(); | ||||
|         } | ||||
|  | ||||
|         if ($this->max_execution_time === null) { | ||||
|             $this->max_execution_time = ini_get('max_execution_time') ?: self::MAXEXECTIME; | ||||
|         } | ||||
|  | ||||
|         return parent::__construct(); | ||||
|     } | ||||
|  | ||||
|     protected function verifyKey() | ||||
|     { | ||||
|         if ($this->qmkey !== common_config('opportunisticqm', 'qmkey')) { | ||||
|             throw new RunQueueBadKeyException($this->qmkey); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     public function canContinue() | ||||
|     { | ||||
|         $time_passed = time() - $this->started_at; | ||||
|          | ||||
|         // Only continue if limit values are sane | ||||
|         if ($time_passed <= 0 && (!is_null($this->max_queue_items) && $this->max_queue_items <= 0)) { | ||||
|             return false; | ||||
|         } | ||||
|         // If too much time has passed, stop | ||||
|         if ($time_passed >= $this->max_execution_time) { | ||||
|             return false; | ||||
|         } | ||||
|         // If we have a max-item-limit, check if it has been passed | ||||
|         if (!is_null($this->max_queue_items) && $this->handled_items >= $this->max_queue_items) { | ||||
|             return false; | ||||
|         } | ||||
|  | ||||
|         return true; | ||||
|     } | ||||
|  | ||||
|     public function poll() | ||||
|     { | ||||
|         $this->handled_items++; | ||||
|         if (!parent::poll()) { | ||||
|             throw new RunQueueOutOfWorkException(); | ||||
|         } | ||||
|         return true; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Takes care of running through the queue items, returning when | ||||
|      * the limits setup in __construct are met. | ||||
|      * | ||||
|      * @return true on workqueue finished, false if there are still items in the queue | ||||
|      */ | ||||
|     public function runQueue() | ||||
|     { | ||||
|         while ($this->canContinue()) { | ||||
|             try { | ||||
|                 $this->poll(); | ||||
|             } catch (RunQueueOutOfWorkException $e) { | ||||
|                 common_debug('Opportunistic queue manager finished.'); | ||||
|                 return true; | ||||
|             } | ||||
|         } | ||||
|         common_debug('Opportunistic queue manager passed execution time/item handling limit without being out of work.'); | ||||
|         return false; | ||||
|     } | ||||
| } | ||||
							
								
								
									
										39
									
								
								plugins/OpportunisticQM/lib/runqueuebadkeyexception.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										39
									
								
								plugins/OpportunisticQM/lib/runqueuebadkeyexception.php
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,39 @@ | ||||
| <?php | ||||
| /** | ||||
|  * Class for the GNU social cron exception when a bad key is used | ||||
|  * | ||||
|  * PHP version 5 | ||||
|  * | ||||
|  * LICENCE: This program is free software: you can redistribute it and/or modify | ||||
|  * it under the terms of the GNU Affero General Public License as published by | ||||
|  * the Free Software Foundation, either version 3 of the License, or | ||||
|  * (at your option) any later version. | ||||
|  * | ||||
|  * This program is distributed in the hope that it will be useful, | ||||
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|  * GNU Affero General Public License for more details. | ||||
|  * | ||||
|  * You should have received a copy of the GNU Affero General Public License | ||||
|  * along with this program.  If not, see <http://www.gnu.org/licenses/>. | ||||
|  * | ||||
|  * @category  Exception | ||||
|  * @package   GNUsocial | ||||
|  * @author    Mikael Nordfeldth <mmn@hethane.se> | ||||
|  * @copyright 2013 Free Software Foundation, Inc. | ||||
|  * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPLv3 | ||||
|  * @link      http://www.gnu.org/software/social/ | ||||
|  */ | ||||
|  | ||||
| if (!defined('GNUSOCIAL')) { exit(1); } | ||||
|  | ||||
| class RunQueueBadKeyException extends ClientException | ||||
| { | ||||
|     public $qmkey; | ||||
|  | ||||
|     public function __construct($qmkey) | ||||
|     { | ||||
|         $this->qmkey = $qmkey; | ||||
|         parent::__construct(_('Bad queue manager key was used.')); | ||||
|     } | ||||
| } | ||||
							
								
								
									
										36
									
								
								plugins/OpportunisticQM/lib/runqueueoutofworkexception.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										36
									
								
								plugins/OpportunisticQM/lib/runqueueoutofworkexception.php
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,36 @@ | ||||
| <?php | ||||
| /** | ||||
|  * Class for the GNU social cron exception when there is no more work to be done | ||||
|  * | ||||
|  * PHP version 5 | ||||
|  * | ||||
|  * LICENCE: This program is free software: you can redistribute it and/or modify | ||||
|  * it under the terms of the GNU Affero General Public License as published by | ||||
|  * the Free Software Foundation, either version 3 of the License, or | ||||
|  * (at your option) any later version. | ||||
|  * | ||||
|  * This program is distributed in the hope that it will be useful, | ||||
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|  * GNU Affero General Public License for more details. | ||||
|  * | ||||
|  * You should have received a copy of the GNU Affero General Public License | ||||
|  * along with this program.  If not, see <http://www.gnu.org/licenses/>. | ||||
|  * | ||||
|  * @category  Exception | ||||
|  * @package   GNUsocial | ||||
|  * @author    Mikael Nordfeldth <mmn@hethane.se> | ||||
|  * @copyright 2013 Free Software Foundation, Inc. | ||||
|  * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPLv3 | ||||
|  * @link      http://www.gnu.org/software/social/ | ||||
|  */ | ||||
|  | ||||
| if (!defined('GNUSOCIAL')) { exit(1); } | ||||
|  | ||||
| class RunQueueOutOfWorkException extends ServerException | ||||
| { | ||||
|     public function __construct() | ||||
|     { | ||||
|         parent::__construct(_('Opportunistic queue manager is out of work (no more items).')); | ||||
|     } | ||||
| } | ||||
		Reference in New Issue
	
	Block a user