Skip to content

Commit

Permalink
ManagedScheduler: locked job event
Browse files Browse the repository at this point in the history
  • Loading branch information
mabar committed Dec 19, 2023
1 parent 9aea8e2 commit 6ef7ea3
Show file tree
Hide file tree
Showing 4 changed files with 107 additions and 14 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
- `addJob()` accepts parameter `repeatAfterSeconds`
- `addJob()` accepts parameter `timeZone`
- `addLazyJob()` replaces `CallbackJobManager`
- `ManagedScheduler`
- `addLockedJobCallback()` - executes given callback when job is locked
- `JobInfo`
- `getRepeatAfterSeconds()`- returns the seconds part of expression
- `getExtendedExpression()` - returns cron expression including seconds
Expand Down
36 changes: 33 additions & 3 deletions docs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@ Cron job scheduler - with locks, parallelism and more
- [Seconds](#seconds)
- [Timezones](#timezones)
- [Events](#events)
- [Before job event](#before-job-event)
- [After job event](#after-job-event)
- [Locked job event](#locked-job-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 @@ -211,17 +214,31 @@ Myanmar Standard Time is UTC+06:30.

## Events

Run callbacks before and after job to collect statistics, etc.
Run callbacks to collect statistics, etc.

Check [job info and result](#job-info-and-result) for available status info

### Before job event

Executes before job start

```php
use Orisai\Scheduler\Status\JobInfo;
use Orisai\Scheduler\Status\JobResult;

$scheduler->addBeforeJobCallback(
function(JobInfo $info): void {
// Executes before job start
},
);
```

### After job event

Executes after job finish

```php
use Orisai\Scheduler\Status\JobInfo;
use Orisai\Scheduler\Status\JobResult;

$scheduler->addAfterJobCallback(
function(JobInfo $info, JobResult $result): void {
Expand All @@ -230,7 +247,20 @@ $scheduler->addAfterJobCallback(
);
```

Check [job info and result](#job-info-and-result) for available status info
### Locked job event

Executes when [lock](#locks-and-job-overlapping) for given job is acquired by another process

```php
use Orisai\Scheduler\Status\JobInfo;
use Orisai\Scheduler\Status\JobResult;

$scheduler->addLockedJobCallback(
function(JobInfo $info, JobResult $result): void {
// Executes when lock for given job is acquired by another process
},
);
```

## Handling errors

Expand Down
34 changes: 24 additions & 10 deletions src/ManagedScheduler.php
Original file line number Diff line number Diff line change
Expand Up @@ -42,11 +42,14 @@ class ManagedScheduler implements Scheduler

private Clock $clock;

/** @var list<Closure(JobInfo, JobResult): void> */
private array $lockedJobCallbacks = [];

/** @var list<Closure(JobInfo): void> */
private array $beforeJob = [];
private array $beforeJobCallbacks = [];

/** @var list<Closure(JobInfo, JobResult): void> */
private array $afterJob = [];
private array $afterJobCallbacks = [];

/**
* @param Closure(Throwable, JobInfo, JobResult): (void)|null $errorHandler
Expand Down Expand Up @@ -192,18 +195,21 @@ private function runInternal($id, JobSchedule $jobSchedule, int $runSecond): arr
$lock = $this->lockFactory->createLock("Orisai.Scheduler.Job/$id");

if (!$lock->acquire()) {
$result = new JobResult($expression, $info->getStart(), JobResultState::skip());

foreach ($this->lockedJobCallbacks as $cb) {
$cb($info, $result);
}

return [
new JobSummary(
$info,
new JobResult($expression, $info->getStart(), JobResultState::skip()),
),
new JobSummary($info, $result),
null,
];
}

$throwable = null;
try {
foreach ($this->beforeJob as $cb) {
foreach ($this->beforeJobCallbacks as $cb) {
$cb($info);
}

Expand All @@ -219,7 +225,7 @@ private function runInternal($id, JobSchedule $jobSchedule, int $runSecond): arr
$throwable === null ? JobResultState::done() : JobResultState::fail(),
);

foreach ($this->afterJob as $cb) {
foreach ($this->afterJobCallbacks as $cb) {
$cb($info, $result);
}

Expand All @@ -237,20 +243,28 @@ private function runInternal($id, JobSchedule $jobSchedule, int $runSecond): arr
];
}

/**
* @param Closure(JobInfo, JobResult): void $callback
*/
public function addLockedJobCallback(Closure $callback): void
{
$this->lockedJobCallbacks[] = $callback;
}

/**
* @param Closure(JobInfo): void $callback
*/
public function addBeforeJobCallback(Closure $callback): void
{
$this->beforeJob[] = $callback;
$this->beforeJobCallbacks[] = $callback;
}

/**
* @param Closure(JobInfo, JobResult): void $callback
*/
public function addAfterJobCallback(Closure $callback): void
{
$this->afterJob[] = $callback;
$this->afterJobCallbacks[] = $callback;
}

}
49 changes: 48 additions & 1 deletion tests/Unit/SimpleSchedulerTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -210,7 +210,7 @@ static function () use (&$i): void {
self::assertCount(3, $errors);
}

public function testEvents(): void
public function testJobEvents(): void
{
$errorHandler = static function (): void {
// Noop
Expand Down Expand Up @@ -784,6 +784,53 @@ static function () use (&$i): void {
self::assertSame(3, $i);
}

public function testLockedJobEvent(): void
{
$lockFactory = new TestLockFactory(new InMemoryStore(), false);
$clock = new FrozenClock(1);
$scheduler = new SimpleScheduler(null, $lockFactory, null, $clock);

$now = $clock->now();
$cbs = new CallbackList();

$job = new CallbackJob(Closure::fromCallable([$cbs, 'job1']));
$scheduler->addJob($job, new CronExpression('* * * * *'));

$afterCollected = [];
$afterCb = static function (JobInfo $info, JobResult $result) use (&$afterCollected): void {
$afterCollected[] = [$info, $result];
};
$scheduler->addLockedJobCallback($afterCb);

$scheduler->run();
$scheduler->runJob(0);
self::assertCount(0, $afterCollected);

$lock = $lockFactory->createLock('Orisai.Scheduler.Job/0');
$lock->acquire();
$scheduler->run();
self::assertEquals(
[
[
new JobInfo(
0,
'Tests\Orisai\Scheduler\Doubles\CallbackList::job1()',
'* * * * *',
0,
0,
$now,
),
new JobResult(new CronExpression('* * * * *'), $now, JobResultState::skip()),
],
],
$afterCollected,
);
self::assertCount(1, $afterCollected);

$scheduler->runJob(0);
self::assertCount(2, $afterCollected);
}

public function testRepeat(): void
{
$clock = new FrozenClock(1);
Expand Down

0 comments on commit 6ef7ea3

Please sign in to comment.