From 5ea5fc7a47c9436cc3d7a077c62db2f72a1f3c4f Mon Sep 17 00:00:00 2001 From: Nandor Kraszlan Date: Sat, 2 Mar 2024 01:39:43 +0000 Subject: [PATCH 1/2] Start Job debouncing feature --- .../Contracts/Queue/Debouncable.php | 10 ++++ .../Foundation/Bus/Dispatchable.php | 23 ++++++++ src/Illuminate/Queue/CallQueuedHandler.php | 7 ++- src/Illuminate/Queue/Middleware/Debounced.php | 58 +++++++++++++++++++ 4 files changed, 97 insertions(+), 1 deletion(-) create mode 100644 src/Illuminate/Contracts/Queue/Debouncable.php create mode 100644 src/Illuminate/Queue/Middleware/Debounced.php diff --git a/src/Illuminate/Contracts/Queue/Debouncable.php b/src/Illuminate/Contracts/Queue/Debouncable.php new file mode 100644 index 000000000000..037cfac216b8 --- /dev/null +++ b/src/Illuminate/Contracts/Queue/Debouncable.php @@ -0,0 +1,10 @@ +uniqueId(); + } + + cache()->forever($key, now()->addSeconds($wait)->toISOString()); + cache()->increment($key . '.count'); + + return (new PendingDispatch($dispatchable))->delay($wait); + } + /** * Dispatch the job with the given arguments unless the given truth test passes. * diff --git a/src/Illuminate/Queue/CallQueuedHandler.php b/src/Illuminate/Queue/CallQueuedHandler.php index 5bee1d9ebb4c..2a1ffc86eb29 100644 --- a/src/Illuminate/Queue/CallQueuedHandler.php +++ b/src/Illuminate/Queue/CallQueuedHandler.php @@ -14,6 +14,7 @@ use Illuminate\Contracts\Queue\ShouldBeUniqueUntilProcessing; use Illuminate\Database\Eloquent\ModelNotFoundException; use Illuminate\Pipeline\Pipeline; +use Illuminate\Queue\Middleware\Debounced; use ReflectionClass; use RuntimeException; @@ -118,7 +119,11 @@ protected function dispatchThroughMiddleware(Job $job, $command) } return (new Pipeline($this->container))->send($command) - ->through(array_merge(method_exists($command, 'middleware') ? $command->middleware() : [], $command->middleware ?? [])) + ->through(array_merge( + method_exists($command, 'middleware') ? $command->middleware() : [], + $command->middleware ?? [], + [new Debounced()] + )) ->then(function ($command) use ($job) { return $this->dispatcher->dispatchNow( $command, $this->resolveHandler($job, $command) diff --git a/src/Illuminate/Queue/Middleware/Debounced.php b/src/Illuminate/Queue/Middleware/Debounced.php new file mode 100644 index 000000000000..ac155a325e32 --- /dev/null +++ b/src/Illuminate/Queue/Middleware/Debounced.php @@ -0,0 +1,58 @@ +uniqueId(); + } + + if (!in_array(InteractsWithQueue::class, class_uses_recursive($job), true)) { + // using the class-string so there's a hard reference + $traitName = class_basename(InteractsWithQueue::class); + throw new \InvalidArgumentException("The Debounced jobs must use the $traitName trait."); + } + + $count = cache()->pull($key . '.count', 1); + + if ($count > 1) { + // this is an earlier job, so we should delete it + $job->delete(); + + // decrement the count + cache()->forever($key . '.count', $count - 1); + return; + } + + if ($intendedExecutionTime = cache()->pull($key)) { + $intendedExecutionTime = Carbon::parse($intendedExecutionTime); + + if ($intendedExecutionTime->gt(now())) { + // ensure that the intended execution time from the last job is used + $job->release($intendedExecutionTime->diffInSeconds(now(), false)); + return; + } + } + + // todo - this is still marked as RUNNING and DONE in the console for every job despite only the last job executes (JobProcessing, JobProcessed events still fire) + $next($job); + } +} From 903c9261c5e925a2f8fc5782f964d8ac6c66a69f Mon Sep 17 00:00:00 2001 From: Nandor Kraszlan Date: Sat, 2 Mar 2024 01:48:32 +0000 Subject: [PATCH 2/2] Remove unused interface --- src/Illuminate/Contracts/Queue/Debouncable.php | 10 ---------- 1 file changed, 10 deletions(-) delete mode 100644 src/Illuminate/Contracts/Queue/Debouncable.php diff --git a/src/Illuminate/Contracts/Queue/Debouncable.php b/src/Illuminate/Contracts/Queue/Debouncable.php deleted file mode 100644 index 037cfac216b8..000000000000 --- a/src/Illuminate/Contracts/Queue/Debouncable.php +++ /dev/null @@ -1,10 +0,0 @@ -