Created
December 3, 2025 00:06
-
-
Save stevebauman/6d469d319812360ac4ffdb6494b18501 to your computer and use it in GitHub Desktop.
Laravel Job Middleware OnOneServer
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| <?php | |
| namespace App\Jobs\Middleware; | |
| use Closure; | |
| use Illuminate\Contracts\Cache\Store; | |
| use Illuminate\Support\Facades\Cache; | |
| use Illuminate\Support\Facades\Date; | |
| use Illuminate\Support\InteractsWithTime; | |
| class OnOneServer | |
| { | |
| use InteractsWithTime; | |
| /** | |
| * The cache store that should be used. | |
| */ | |
| protected ?string $store = null; | |
| /** | |
| * The prefix of the lock key. | |
| */ | |
| public string $prefix = 'laravel-queue-one-server:'; | |
| /** | |
| * Create a new middleware instance. | |
| */ | |
| public function __construct( | |
| protected ?string $key = null, | |
| protected ?string $server = null, | |
| protected int $releaseAfter = 5, | |
| protected int $expiresAfter = 600, // 10 minutes | |
| ) { | |
| $this->server = $server ?? gethostname(); | |
| } | |
| /** | |
| * Process the queued job. | |
| * | |
| * @param Closure(object): void $next | |
| */ | |
| public function handle(object $job, Closure $next): void | |
| { | |
| // We're on the correct server, process the job. | |
| if (gethostname() === $this->server) { | |
| $this->forgetExpiryTime($this->cache(), $job); | |
| $next($job); | |
| return; | |
| } | |
| $cache = $this->cache(); | |
| $time = Date::now()->timestamp; | |
| $expiryTime = $this->getExpiryTime($cache, $job); | |
| // Expiry time reached, any worker can process the job. | |
| if ($expiryTime && $time >= $expiryTime) { | |
| $this->forgetExpiryTime($cache, $job); | |
| $next($job); | |
| return; | |
| } | |
| // We're not on the required server and the expiry hasn't been | |
| // reached. Set the expiry time if not set, then release the | |
| // job back to the queue for another worker to pick up. | |
| if (! $expiryTime) { | |
| $this->setExpiryTime($cache, $job, $time + $this->expiresAfter); | |
| } | |
| $job->release($this->releaseAfter); | |
| } | |
| /** | |
| * Get the expiry time from cache. | |
| */ | |
| protected function getExpiryTime(Store $cache, object $job): ?int | |
| { | |
| return $cache->get( | |
| $this->getLockKey($job).':expiry' | |
| ); | |
| } | |
| /** | |
| * Set the expiry time in cache. | |
| */ | |
| protected function setExpiryTime(Store $cache, object $job, int $expiryTime): void | |
| { | |
| $cache->put( | |
| $this->getLockKey($job).':expiry', | |
| $expiryTime, | |
| $this->expiresAfter | |
| ); | |
| } | |
| /** | |
| * Forget the expiry time from cache. | |
| */ | |
| protected function forgetExpiryTime(Store $cache, object $job): void | |
| { | |
| $cache->forget($this->getLockKey($job).':expiry'); | |
| } | |
| /** | |
| * Get the lock key for the given job. | |
| */ | |
| protected function getLockKey(object $job): string | |
| { | |
| return $this->prefix.get_class($job).':'.$this->key; | |
| } | |
| /** | |
| * Get the cache store that should be used. | |
| */ | |
| protected function cache(): Store | |
| { | |
| return Cache::store($this->store)->getStore(); | |
| } | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment