SAAS-1229 - initial work on bandwidth limit within Airtime; overhaul TaskFactory to get tasks reflectively

This commit is contained in:
Duncan Sommerville 2015-11-19 16:08:25 -05:00
parent c328515f4b
commit 6c2d1f008b
4 changed files with 126 additions and 32 deletions

View File

@ -3,8 +3,7 @@
/**
* Class TaskManager
*
* When adding a new task, the new AirtimeTask class will also need to be added
* as a class constant and to the array in TaskFactory
* Background class for 'asynchronous' task management for Airtime stations
*/
final class TaskManager {
@ -34,8 +33,8 @@ final class TaskManager {
* Private constructor so class is uninstantiable
*/
private function __construct() {
foreach (array_keys(TaskFactory::$tasks) as $v) {
$this->_taskList[$v] = false;
foreach (TaskFactory::getTasks() as $k => $task) {
$this->_taskList[$task] = false;
}
}
@ -278,12 +277,12 @@ class ImportCleanupTask implements AirtimeTask {
/**
* Class StationPodcastTask
*
* Checks the Station podcast rollover timer and resets monthly allotted
* Checks the Station podcast rollover timer and resets allotted
* downloads if enough time has passed (default: 1 month)
*/
class StationPodcastTask implements AirtimeTask {
const STATION_PODCAST_RESET_TIMER_SECONDS = 2.628e+6; // 1 month XXX: should we use datetime roll for this instead?
const STATION_PODCAST_RESET_TIMER_SECONDS = 2.628e+6; // 1 month
/**
* Check whether or not the download counter for the station podcast should be reset
@ -305,32 +304,74 @@ class StationPodcastTask implements AirtimeTask {
}
/**
* TODO: this and the above StationPodcastTask should probably be unified since the
* behaviour is essentially identical
*
* Class BandwidthLimitTask
*
* Checks the bandwidth limit rollover timer and resets the allotted
* limit if enough time has passed (default: 1 month)
*/
class BandwidthLimitTask implements AirtimeTask {
const BANDWIDTH_LIMIT_RESET_TIMER_SECONDS = 2.628e+6;
/**
* Check whether the task should be run
*
* @return bool true if the task needs to be run, otherwise false
*/
public function shouldBeRun() {
$lastReset = Application_Model_Preference::getBandwidthLimitResetTimer();
return empty($lastReset) || (microtime(true) > ($lastReset + self::BANDWIDTH_LIMIT_RESET_TIMER_SECONDS));
}
/**
* Run the task
*
* @return void
*/
public function run() {
Application_Model_Preference::resetStationPodcastDownloadCounter();
Application_Model_Preference::setBandwidthLimitResetTimer(microtime(true));
}
}
/**
* Class TaskFactory Factory class to abstract task instantiation
*/
class TaskFactory {
/**
* PHP doesn't have ENUMs so declare them as constants
* Task types - values don't really matter as long as they're unique
* Check if the class with the given name implements AirtimeTask
*
* @param $c string class name
*
* @return bool true if the class $c implements AirtimeTask
*/
const UPGRADE = "upgrade";
const CELERY = "celery";
const PODCAST = "podcast";
const IMPORT = "import";
const STATION_PODCAST = "station-podcast";
private static function _isTask($c) {
$reflect = new ReflectionClass($c);
$clazz = version_compare(phpversion(), '5.5.0', '<') ? "AirtimeTask" : AirtimeTask::class;
return $reflect->implementsInterface($clazz);
}
/**
* @var array map of arbitrary identifiers to class names to be instantiated reflectively
* Filter all declared classes to get all classes implementing the AirtimeTask interface
*
* @return array all classes implementing the AirtimeTask interface
*/
public static $tasks = array(
"upgrade" => "UpgradeTask",
"celery" => "CeleryTask",
"podcast" => "PodcastTask",
"import" => "ImportCleanupTask",
"station-podcast" => "StationPodcastTask",
);
private static function _getTaskClasses() {
return array_filter(get_declared_classes(), array(__CLASS__, "_isTask"));
}
/**
* @return array
*/
public static function getTasks() {
return self::_getTaskClasses();
}
/**
* Get an AirtimeTask based on a task type
@ -341,7 +382,7 @@ class TaskFactory {
* task exists or is implemented
*/
public static function getTask($task) {
return new self::$tasks[$task]();
return new $task();
}
}

View File

@ -11,6 +11,8 @@ class ApiController extends Zend_Controller_Action
public function init()
{
$this->view->layout()->disableLayout();
$this->_helper->viewRenderer->setNoRender(true);
//Ignore API key and session authentication for these APIs:
$ignoreAuth = array("live-info",
@ -101,14 +103,6 @@ class ApiController extends Zend_Controller_Action
exit();
}
public function pollCeleryAction() {
$this->view->layout()->disableLayout();
$this->_helper->viewRenderer->setNoRender(true);
$taskManager = TaskManager::getInstance();
$taskManager->runTask(TaskFactory::CELERY);
}
public function versionAction()
{
$this->_helper->json->sendJson( array(
@ -136,6 +130,35 @@ class ApiController extends Zend_Controller_Action
$this->_helper->json->sendJson(array());
}
/**
* Manually trigger the TaskManager task to poll for completed Celery tasks
*/
public function pollCeleryAction() {
$taskManager = TaskManager::getInstance();
$clazz = version_compare(phpversion(), '5.5.0', '<') ? get_class(new CeleryTask) : CeleryTask::class;
$taskManager->runTask($clazz);
}
/**
* TODO: move this function into a more generic analytics REST controller
*
* Update station bandwidth usage based on icecast log data
*/
public function bandwidthUsageAction() {
// FIXME: this function is using placeholder names
$bandwidthUsage = json_decode($this->getRequest()->getParam("bandwidth_usage"));
$usageBytes = 0;
foreach ($bandwidthUsage as $entry) {
Logging::info($entry);
// TODO: store the IP address for future use
$ts = strtotime($entry->timestamp);
if ($ts > Application_Model_Preference::getBandwidthLimitUpdateTimer()) {
$usageBytes += $entry->bytes;
}
}
Application_Model_Preference::incrementBandwidthLimitCounter($usageBytes);
}
//Used by the SaaS monitoring
public function onAirLightAction()
{

View File

@ -64,7 +64,8 @@ class PageLayoutInitPlugin extends Zend_Controller_Plugin_Abstract
// We can't afford to wait 7 minutes to run an upgrade: users could
// have several minutes of database errors while waiting for a
// schema change upgrade to happen after a deployment
$taskManager->runTask(TaskFactory::UPGRADE);
$clazz = version_compare(phpversion(), '5.5.0', '<') ? get_class(new UpgradeTask) : UpgradeTask::class;
$taskManager->runTask($clazz);
// Piggyback the TaskManager onto API calls. This provides guaranteed consistency
// (there is at least one API call made from pypo to Airtime every 7 minutes) and

View File

@ -1584,4 +1584,33 @@ class Application_Model_Preference
public static function setStationPodcastPrivacy($value) {
self::setValue("station_podcast_privacy", $value);
}
public static function getBandwidthLimitCounter() {
return self::getValue("bandwidth_limit_counter");
}
public static function incrementBandwidthLimitCounter($value) {
$counter = intval(self::getValue("bandwidth_limit_counter"));
self::setValue("bandwidth_limit_counter", $counter + intval($value));
}
public static function resetBandwidthLimitCounter() {
self::setValue("bandwidth_limit_counter", 0);
}
public static function getBandwidthLimitResetTimer() {
return self::getValue("bandwidth_limit_reset_timer");
}
public static function setBandwidthLimitResetTimer($value) {
self::setValue("bandwidth_limit_reset_timer", $value);
}
public static function getBandwidthLimitUpdateTimer() {
return self::getValue("bandwidth_limit_reset_timer");
}
public static function setBandwidthLimitUpdateTimer() {
self::setValue("bandwidth_limit_reset_timer", microtime(true));
}
}