Job.php 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137
  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 OCP\BackgroundJob\IJob;
  9. use OCP\BackgroundJob\IJobList;
  10. use Symfony\Component\Console\Command\Command;
  11. use Symfony\Component\Console\Input\InputArgument;
  12. use Symfony\Component\Console\Input\InputInterface;
  13. use Symfony\Component\Console\Input\InputOption;
  14. use Symfony\Component\Console\Output\OutputInterface;
  15. class Job extends Command {
  16. public function __construct(
  17. protected IJobList $jobList,
  18. ) {
  19. parent::__construct();
  20. }
  21. protected function configure(): void {
  22. $this
  23. ->setName('background-job:execute')
  24. ->setDescription('Execute a single background job manually')
  25. ->addArgument(
  26. 'job-id',
  27. InputArgument::REQUIRED,
  28. 'The ID of the job in the database'
  29. )
  30. ->addOption(
  31. 'force-execute',
  32. null,
  33. InputOption::VALUE_NONE,
  34. 'Force execute the background job, independent from last run and being reserved'
  35. )
  36. ;
  37. }
  38. protected function execute(InputInterface $input, OutputInterface $output): int {
  39. $jobId = (int)$input->getArgument('job-id');
  40. $job = $this->jobList->getById($jobId);
  41. if ($job === null) {
  42. $output->writeln('<error>Job with ID ' . $jobId . ' could not be found in the database</error>');
  43. return 1;
  44. }
  45. $this->printJobInfo($jobId, $job, $output);
  46. $output->writeln('');
  47. $lastRun = $job->getLastRun();
  48. if ($input->getOption('force-execute')) {
  49. $lastRun = 0;
  50. $output->writeln('<comment>Forcing execution of the job</comment>');
  51. $output->writeln('');
  52. $this->jobList->resetBackgroundJob($job);
  53. }
  54. $job = $this->jobList->getById($jobId);
  55. if ($job === null) {
  56. $output->writeln('<error>Something went wrong when trying to retrieve Job with ID ' . $jobId . ' from database</error>');
  57. return 1;
  58. }
  59. /** @psalm-suppress DeprecatedMethod Calling execute until it is removed, then will switch to start */
  60. $job->execute($this->jobList);
  61. $job = $this->jobList->getById($jobId);
  62. if (($job === null) || ($lastRun !== $job->getLastRun())) {
  63. $output->writeln('<info>Job executed!</info>');
  64. $output->writeln('');
  65. if ($job instanceof \OCP\BackgroundJob\TimedJob) {
  66. $this->printJobInfo($jobId, $job, $output);
  67. }
  68. } else {
  69. $output->writeln('<comment>Job was not executed because it is not due</comment>');
  70. $output->writeln('Specify the <question>--force-execute</question> option to run it anyway');
  71. }
  72. return 0;
  73. }
  74. protected function printJobInfo(int $jobId, IJob $job, OutputInterface $output): void {
  75. $row = $this->jobList->getDetailsById($jobId);
  76. $lastRun = new \DateTime();
  77. $lastRun->setTimestamp((int)$row['last_run']);
  78. $lastChecked = new \DateTime();
  79. $lastChecked->setTimestamp((int)$row['last_checked']);
  80. $reservedAt = new \DateTime();
  81. $reservedAt->setTimestamp((int)$row['reserved_at']);
  82. $output->writeln('Job class: ' . get_class($job));
  83. $output->writeln('Arguments: ' . json_encode($job->getArgument()));
  84. $isTimedJob = $job instanceof \OCP\BackgroundJob\TimedJob;
  85. if ($isTimedJob) {
  86. $output->writeln('Type: timed');
  87. } elseif ($job instanceof \OCP\BackgroundJob\QueuedJob) {
  88. $output->writeln('Type: queued');
  89. } else {
  90. $output->writeln('Type: job');
  91. }
  92. $output->writeln('');
  93. $output->writeln('Last checked: ' . $lastChecked->format(\DateTimeInterface::ATOM));
  94. if ((int)$row['reserved_at'] === 0) {
  95. $output->writeln('Reserved at: -');
  96. } else {
  97. $output->writeln('Reserved at: <comment>' . $reservedAt->format(\DateTimeInterface::ATOM) . '</comment>');
  98. }
  99. $output->writeln('Last executed: ' . $lastRun->format(\DateTimeInterface::ATOM));
  100. $output->writeln('Last duration: ' . $row['execution_duration']);
  101. if ($isTimedJob) {
  102. $reflection = new \ReflectionClass($job);
  103. $intervalProperty = $reflection->getProperty('interval');
  104. $intervalProperty->setAccessible(true);
  105. $interval = $intervalProperty->getValue($job);
  106. $nextRun = new \DateTime();
  107. $nextRun->setTimestamp($row['last_run'] + $interval);
  108. if ($nextRun > new \DateTime()) {
  109. $output->writeln('Next execution: <comment>' . $nextRun->format(\DateTimeInterface::ATOM) . '</comment>');
  110. } else {
  111. $output->writeln('Next execution: <info>' . $nextRun->format(\DateTimeInterface::ATOM) . '</info>');
  112. }
  113. }
  114. }
  115. }