repository = $repository; $this->clock = $clock ?? static fn(): int => time(); } /** * Claims the job, or returns null when a live lease holds it. * * @since 1.14.0 * * @param JobState $state Current state. * @param string $owner Claiming runner's token. * @param int $budget_seconds This batch's time budget. * * @return JobState|null Saved state with the lease applied, or null. */ public function claim( JobState $state, string $owner, int $budget_seconds ): ?JobState { $now = ( $this->clock )(); $held_by_other = '' !== $state->lease_owner() && $state->lease_owner() !== $owner && $state->lease_expires() > $now; if ( $held_by_other ) { return null; } $claimed = $state->with_lease( $owner, $now + max( self::FLOOR_SECONDS, $budget_seconds * 2 ) ); $this->repository->save( $claimed ); return $claimed; } /** * Gives up the lease. * * @since 1.14.0 * * @param JobState $state State holding the lease. */ public function release( JobState $state ): JobState { return $state->without_lease(); } /** * Whether the job claims to be running but nothing is actually holding it. * * @since 1.14.0 * * @param JobState $state State to inspect. */ public function is_stale( JobState $state ): bool { return 'running' === $state->status() && $state->lease_expires() <= ( $this->clock )(); } }