JobWorker.php 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140
  1. <?php
  2. declare(strict_types=1);
  3. /**
  4. * SPDX-FileCopyrightText: 2021 Nextcloud GmbH and Nextcloud contributors
  5. * SPDX-License-Identifier: AGPL-3.0-or-later
  6. */
  7. namespace OC\Core\Command\Background;
  8. use OC\Core\Command\InterruptedException;
  9. use OC\Files\SetupManager;
  10. use OCP\BackgroundJob\IJobList;
  11. use OCP\ITempManager;
  12. use Psr\Log\LoggerInterface;
  13. use Symfony\Component\Console\Input\InputArgument;
  14. use Symfony\Component\Console\Input\InputInterface;
  15. use Symfony\Component\Console\Input\InputOption;
  16. use Symfony\Component\Console\Output\OutputInterface;
  17. class JobWorker extends JobBase {
  18. public function __construct(
  19. protected IJobList $jobList,
  20. protected LoggerInterface $logger,
  21. private ITempManager $tempManager,
  22. private SetupManager $setupManager,
  23. ) {
  24. parent::__construct($jobList, $logger);
  25. }
  26. protected function configure(): void {
  27. parent::configure();
  28. $this
  29. ->setName('background-job:worker')
  30. ->setDescription('Run a background job worker')
  31. ->addArgument(
  32. 'job-classes',
  33. InputArgument::OPTIONAL | InputArgument::IS_ARRAY,
  34. 'The classes of the jobs to look for in the database'
  35. )
  36. ->addOption(
  37. 'once',
  38. null,
  39. InputOption::VALUE_NONE,
  40. 'Only execute the worker once (as a regular cron execution would do it)'
  41. )
  42. ->addOption(
  43. 'interval',
  44. 'i',
  45. InputOption::VALUE_OPTIONAL,
  46. 'Interval in seconds in which the worker should repeat already processed jobs (set to 0 for no repeat)',
  47. 5
  48. )
  49. ;
  50. }
  51. protected function execute(InputInterface $input, OutputInterface $output): int {
  52. $jobClasses = $input->getArgument('job-classes');
  53. $jobClasses = empty($jobClasses) ? null : $jobClasses;
  54. if ($jobClasses !== null) {
  55. // at least one class is invalid
  56. foreach ($jobClasses as $jobClass) {
  57. if (!class_exists($jobClass)) {
  58. $output->writeln('<error>Invalid job class: ' . $jobClass . '</error>');
  59. return 1;
  60. }
  61. }
  62. }
  63. while (true) {
  64. // Handle canceling of the process
  65. try {
  66. $this->abortIfInterrupted();
  67. } catch (InterruptedException $e) {
  68. $output->writeln('<info>Background job worker stopped</info>');
  69. break;
  70. }
  71. $this->printSummary($input, $output);
  72. usleep(50000);
  73. $job = $this->jobList->getNext(false, $jobClasses);
  74. if (!$job) {
  75. if ($input->getOption('once') === true) {
  76. if ($jobClasses === null) {
  77. $output->writeln('No job is currently queued', OutputInterface::VERBOSITY_VERBOSE);
  78. } else {
  79. $output->writeln('No job of classes [' . implode(', ', $jobClasses) . '] is currently queued', OutputInterface::VERBOSITY_VERBOSE);
  80. }
  81. $output->writeln('Exiting...', OutputInterface::VERBOSITY_VERBOSE);
  82. break;
  83. }
  84. $output->writeln('Waiting for new jobs to be queued', OutputInterface::VERBOSITY_VERBOSE);
  85. // Re-check interval for new jobs
  86. sleep(1);
  87. continue;
  88. }
  89. $output->writeln('Running job ' . get_class($job) . ' with ID ' . $job->getId());
  90. if ($output->isVerbose()) {
  91. $this->printJobInfo($job->getId(), $job, $output);
  92. }
  93. /** @psalm-suppress DeprecatedMethod Calling execute until it is removed, then will switch to start */
  94. $job->execute($this->jobList);
  95. $output->writeln('Job ' . $job->getId() . ' has finished', OutputInterface::VERBOSITY_VERBOSE);
  96. // clean up after unclean jobs
  97. $this->setupManager->tearDown();
  98. $this->tempManager->clean();
  99. $this->jobList->setLastJob($job);
  100. $this->jobList->unlockJob($job);
  101. if ($input->getOption('once') === true) {
  102. break;
  103. }
  104. }
  105. return 0;
  106. }
  107. private function printSummary(InputInterface $input, OutputInterface $output): void {
  108. if (!$output->isVeryVerbose()) {
  109. return;
  110. }
  111. $output->writeln('<comment>Summary</comment>');
  112. $counts = [];
  113. foreach ($this->jobList->countByClass() as $row) {
  114. $counts[] = $row;
  115. }
  116. $this->writeTableInOutputFormat($input, $output, $counts);
  117. }
  118. }