*/ class BackgroundJobController extends Controller { private const DEFAULT_MAX_EVENTS = 1000; private const DEFAULT_MAX_RUNTIME_SECONDS = 600; private const DEFAULT_MAX_MEMORY_USAGE = 64_000_000; private const MAX_RETENTION_PERIOD_HOURS = 24 * 3; protected int $maxEvents = self::DEFAULT_MAX_EVENTS; protected int $maxRuntimeSeconds = self::DEFAULT_MAX_RUNTIME_SECONDS; protected int $maxMemoryUsage = self::DEFAULT_MAX_MEMORY_USAGE; public function options($actionID): array { return ['maxEvents', 'maxRuntimeSeconds', 'maxMemoryUsage']; } /** * Runs the background job processor * Options: * --max-runtime-seconds 600 * --max-events 1000 * --max-memory-usage 64000000 */ public function actionRun(): void { echo "Starting background job processor at: " . (new \DateTimeImmutable())->format("Y-m-d H:i:s.u") . "\n"; $connection = \Yii::$app->getDb(); $connection->enableLogging = false; $processor = new BackgroundJobProcessor($connection); while (!$this->needsRestart($processor)) { $row = $processor->getJobAndSetStarted(); if ($row) { $processor->processRow($row); } else { usleep(100_000); } } echo "Stopping background job processor at: " . (new \DateTimeImmutable())->format("Y-m-d H:i:s.u") . "\n"; } private function needsRestart(BackgroundJobProcessor $processor): bool { if ($processor->getProcessedEvents() >= $this->maxEvents) { echo "Stopping because maximum number of processed events has been reached.\n"; return true; } if ($processor->getRuntimeInSeconds() >= $this->maxRuntimeSeconds) { echo "Stopping because maximum runtime has been reached.\n"; return true; } if (memory_get_peak_usage() >= $this->maxMemoryUsage) { echo "Stopping because maximum memory usage has been reached.\n"; return true; } return false; } /** * Cleans up old tasks from database */ public function actionCleanup(): void { $deletedJobs = BackgroundJobScheduler::cleanup(self::MAX_RETENTION_PERIOD_HOURS); echo "Deleted $deletedJobs jobs.\n"; } }