Skip to content

Commit

Permalink
ManagedScheduler: after run event
Browse files Browse the repository at this point in the history
  • Loading branch information
mabar committed Dec 22, 2023
1 parent 6ef7ea3 commit 62ca45f
Show file tree
Hide file tree
Showing 10 changed files with 185 additions and 5 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
- `addLazyJob()` replaces `CallbackJobManager`
- `ManagedScheduler`
- `addLockedJobCallback()` - executes given callback when job is locked
- `addAfterRunCallback()` - executes given callback when run finishes
- `JobInfo`
- `getRepeatAfterSeconds()`- returns the seconds part of expression
- `getExtendedExpression()` - returns cron expression including seconds
Expand Down
15 changes: 15 additions & 0 deletions docs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ Cron job scheduler - with locks, parallelism and more
- [Before job event](#before-job-event)
- [After job event](#after-job-event)
- [Locked job event](#locked-job-event)
- [After run event](#after-run-event)
- [Handling errors](#handling-errors)
- [Locks and job overlapping](#locks-and-job-overlapping)
- [Parallelization and process isolation](#parallelization-and-process-isolation)
Expand Down Expand Up @@ -262,6 +263,20 @@ $scheduler->addLockedJobCallback(
);
```

### After run event

Executes after every run (every minute), even if no jobs were executed

```php
use Orisai\Scheduler\Status\RunSummary;

$scheduler->addAfterRunCallback(
function(RunSummary $summary): void {
// Executes after every run (every minute), even if no jobs were executed
},
);
```

## Handling errors

After all jobs finish, an exception `RunFailure` composing exceptions thrown by all jobs is thrown. This
Expand Down
8 changes: 7 additions & 1 deletion src/Executor/BasicJobExecutor.php
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,11 @@ public function __construct(Clock $clock, Closure $runCb)
$this->runCb = $runCb;
}

public function runJobs(array $jobSchedulesBySecond, DateTimeImmutable $runStart): Generator
public function runJobs(
array $jobSchedulesBySecond,
DateTimeImmutable $runStart,
Closure $afterRunCallback
): Generator
{
$lastSecond = $jobSchedulesBySecond !== []
? max(array_keys($jobSchedulesBySecond))
Expand All @@ -60,6 +64,8 @@ public function runJobs(array $jobSchedulesBySecond, DateTimeImmutable $runStart

$summary = new RunSummary($runStart, $this->clock->now(), $jobSummaries);

$afterRunCallback($summary);

if ($suppressedExceptions !== []) {
throw RunFailure::create($summary, $suppressedExceptions);
}
Expand Down
8 changes: 7 additions & 1 deletion src/Executor/JobExecutor.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

namespace Orisai\Scheduler\Executor;

use Closure;
use DateTimeImmutable;
use Generator;
use Orisai\Scheduler\Exception\RunFailure;
Expand All @@ -14,9 +15,14 @@ interface JobExecutor

/**
* @param array<int, array<int|string, JobSchedule>> $jobSchedulesBySecond
* @param Closure(RunSummary): void $afterRunCallback
* @return Generator<int, JobSummary, void, RunSummary>
* @throws RunFailure
*/
public function runJobs(array $jobSchedulesBySecond, DateTimeImmutable $runStart): Generator;
public function runJobs(
array $jobSchedulesBySecond,
DateTimeImmutable $runStart,
Closure $afterRunCallback
): Generator;

}
9 changes: 8 additions & 1 deletion src/Executor/ProcessJobExecutor.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

namespace Orisai\Scheduler\Executor;

use Closure;
use Cron\CronExpression;
use DateTimeImmutable;
use Generator;
Expand Down Expand Up @@ -50,7 +51,11 @@ public function setExecutable(string $script, string $command = 'scheduler:run-j
$this->command = $command;
}

public function runJobs(array $jobSchedulesBySecond, DateTimeImmutable $runStart): Generator
public function runJobs(
array $jobSchedulesBySecond,
DateTimeImmutable $runStart,
Closure $afterRunCallback
): Generator
{
$jobExecutions = [];
$jobSummaries = [];
Expand Down Expand Up @@ -102,6 +107,8 @@ public function runJobs(array $jobSchedulesBySecond, DateTimeImmutable $runStart

$summary = new RunSummary($runStart, $this->clock->now(), $jobSummaries);

$afterRunCallback($summary);

if ($suppressedExceptions !== []) {
throw RunFailure::create($summary, $suppressedExceptions);
}
Expand Down
29 changes: 28 additions & 1 deletion src/ManagedScheduler.php
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,9 @@ class ManagedScheduler implements Scheduler
/** @var list<Closure(JobInfo, JobResult): void> */
private array $afterJobCallbacks = [];

/** @var list<Closure(RunSummary): void> */
private array $afterRunCallbacks = [];

/**
* @param Closure(Throwable, JobInfo, JobResult): (void)|null $errorHandler
*/
Expand Down Expand Up @@ -161,7 +164,23 @@ public function runPromise(): Generator
}
}

return $this->executor->runJobs($this->getJobSchedulesBySecond($ids), $runStart);
return $this->executor->runJobs(
$this->getJobSchedulesBySecond($ids),
$runStart,
$this->getAfterRunCallback(),
);
}

/**
* @return Closure(RunSummary): void
*/
private function getAfterRunCallback(): Closure
{
return function (RunSummary $runSummary): void {
foreach ($this->afterRunCallbacks as $cb) {
$cb($runSummary);
}
};
}

public function run(): RunSummary
Expand Down Expand Up @@ -267,4 +286,12 @@ public function addAfterJobCallback(Closure $callback): void
$this->afterJobCallbacks[] = $callback;
}

/**
* @param Closure(RunSummary): void $callback
*/
public function addAfterRunCallback(Closure $callback): void
{
$this->afterRunCallbacks[] = $callback;
}

}
20 changes: 19 additions & 1 deletion tests/Unit/SchedulerProcessSetup.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

use Closure;
use Cron\CronExpression;
use Exception;
use Orisai\Clock\FrozenClock;
use Orisai\Scheduler\Executor\ProcessJobExecutor;
use Orisai\Scheduler\Job\CallbackJob;
Expand Down Expand Up @@ -37,7 +38,7 @@ public static function createWithDefaultExecutable(): Scheduler
return self::create();
}

public static function createEmpty(): Scheduler
public static function createEmpty(): ManagedScheduler
{
$jobManager = new SimpleJobManager();
$clock = new FrozenClock(1);
Expand All @@ -47,6 +48,23 @@ public static function createEmpty(): Scheduler
return new ManagedScheduler($jobManager, null, null, $executor, $clock);
}

public static function createWithThrowingJob(): ManagedScheduler
{
$jobManager = new SimpleJobManager();
$clock = new FrozenClock(1);
$executor = new ProcessJobExecutor($clock);
$executor->setExecutable(__DIR__ . '/scheduler-process-binary-with-throwing-job.php');

$jobManager->addJob(
new CallbackJob(static function (): void {
throw new Exception('');
}),
new CronExpression('* * * * *'),
);

return new ManagedScheduler($jobManager, null, null, $executor, $clock);
}

/**
* @param Closure(Throwable, JobInfo, JobResult): (void)|null $errorHandler
*/
Expand Down
78 changes: 78 additions & 0 deletions tests/Unit/SimpleSchedulerTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
use Cron\CronExpression;
use DateTimeImmutable;
use DateTimeZone;
use Exception;
use Generator;
use Orisai\Clock\FrozenClock;
use Orisai\Exceptions\Logic\InvalidArgument;
Expand Down Expand Up @@ -428,6 +429,48 @@ static function () use (&$i): void {
self::assertSame(1, $i);
}

public function testAfterRunEvent(): void
{
$scheduler = new SimpleScheduler();

$summary = null;
$scheduler->addAfterRunCallback(static function (RunSummary $givenSummary) use (&$summary): void {
$summary = $givenSummary;
});
self::assertNull($summary);

$scheduler->run();
self::assertNotNull($summary);
}

public function testAfterRunEventAfterException(): void
{
$scheduler = new SimpleScheduler();

$scheduler->addJob(
new CallbackJob(static function (): void {
throw new Exception('');
}),
new CronExpression('* * * * *'),
);

$summary = null;
$scheduler->addAfterRunCallback(static function (RunSummary $givenSummary) use (&$summary): void {
$summary = $givenSummary;
});
self::assertNull($summary);

$exception = null;
try {
$scheduler->run();
} catch (Throwable $exception) {
// Handled bellow
}

self::assertNotNull($summary);
self::assertNotNull($exception);
}

public function testRunSummary(): void
{
$clock = new FrozenClock(1);
Expand Down Expand Up @@ -939,4 +982,39 @@ public function testProcessExecutorWithDefaultExecutable(): void
self::assertStringContainsString('Could not open input file: bin/console', $e->getMessage());
}

public function testProcessAfterRunEvent(): void
{
$scheduler = SchedulerProcessSetup::createEmpty();

$summary = null;
$scheduler->addAfterRunCallback(static function (RunSummary $givenSummary) use (&$summary): void {
$summary = $givenSummary;
});
self::assertNull($summary);

$scheduler->run();
self::assertNotNull($summary);
}

public function testProcessAfterRunEventAfterException(): void
{
$scheduler = SchedulerProcessSetup::createWithThrowingJob();

$summary = null;
$scheduler->addAfterRunCallback(static function (RunSummary $givenSummary) use (&$summary): void {
$summary = $givenSummary;
});
self::assertNull($summary);

$exception = null;
try {
$scheduler->run();
} catch (Throwable $exception) {
// Handled bellow
}

self::assertNotNull($summary);
self::assertNotNull($exception);
}

}
17 changes: 17 additions & 0 deletions tests/Unit/scheduler-process-binary-with-throwing-job.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<?php declare(strict_types = 1);

use Orisai\Scheduler\Command\RunJobCommand;
use Symfony\Component\Console\Application;
use Tests\Orisai\Scheduler\Unit\SchedulerProcessSetup;

require_once __DIR__ . '/../../vendor/autoload.php';

$application = new Application();
$scheduler = SchedulerProcessSetup::createWithThrowingJob();

$command = new RunJobCommand($scheduler);

$application = new Application();
$application->addCommands([$command]);

$application->run();
5 changes: 5 additions & 0 deletions tools/phpstan.baseline.neon
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,11 @@ parameters:
count: 3
path: ../tests/Unit/SimpleSchedulerTest.php

-
message: "#^Call to static method PHPUnit\\\\Framework\\\\Assert\\:\\:assertNotNull\\(\\) with null will always evaluate to false\\.$#"
count: 4
path: ../tests/Unit/SimpleSchedulerTest.php

-
message: "#^Call to static method PHPUnit\\\\Framework\\\\Assert\\:\\:assertSame\\(\\) with 0 and 0 will always evaluate to true\\.$#"
count: 2
Expand Down

0 comments on commit 62ca45f

Please sign in to comment.