Skip to content

Commit 67f3f84

Browse files
[12.x] Implement releaseAfter method in RateLimited middleware (#55671)
* Add test for new releaseAfter method * Implement releaseAfter method
1 parent e333cc8 commit 67f3f84

File tree

3 files changed

+61
-2
lines changed

3 files changed

+61
-2
lines changed

src/Illuminate/Queue/Middleware/RateLimited.php

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,13 @@ class RateLimited
2525
*/
2626
protected $limiterName;
2727

28+
/**
29+
* The number of seconds before a job should be available again if the limit is exceeded.
30+
*
31+
* @var \DateTimeInterface|int|null
32+
*/
33+
public $releaseAfter;
34+
2835
/**
2936
* Indicates if the job should be released if the limit is exceeded.
3037
*
@@ -89,7 +96,7 @@ protected function handleJob($job, $next, array $limits)
8996
foreach ($limits as $limit) {
9097
if ($this->limiter->tooManyAttempts($limit->key, $limit->maxAttempts)) {
9198
return $this->shouldRelease
92-
? $job->release($this->getTimeUntilNextRetry($limit->key))
99+
? $job->release($this->releaseAfter ?: $this->getTimeUntilNextRetry($limit->key))
93100
: false;
94101
}
95102

@@ -99,6 +106,19 @@ protected function handleJob($job, $next, array $limits)
99106
return $next($job);
100107
}
101108

109+
/**
110+
* Set the delay (in seconds) to release the job back to the queue.
111+
*
112+
* @param \DateTimeInterface|int $releaseAfter
113+
* @return $this
114+
*/
115+
public function releaseAfter($releaseAfter)
116+
{
117+
$this->releaseAfter = $releaseAfter;
118+
119+
return $this;
120+
}
121+
102122
/**
103123
* Do not release the job back to the queue if the limit is exceeded.
104124
*

src/Illuminate/Queue/Middleware/RateLimitedWithRedis.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ protected function handleJob($job, $next, array $limits)
5050
foreach ($limits as $limit) {
5151
if ($this->tooManyAttempts($limit->key, $limit->maxAttempts, $limit->decaySeconds)) {
5252
return $this->shouldRelease
53-
? $job->release($this->getTimeUntilNextRetry($limit->key))
53+
? $job->release($this->releaseAfter ?: $this->getTimeUntilNextRetry($limit->key))
5454
: false;
5555
}
5656
}

tests/Integration/Queue/RateLimitedTest.php

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,18 @@ public function testJobsCanHaveConditionalRateLimits()
138138
$this->assertJobWasReleased(NonAdminTestJob::class);
139139
}
140140

141+
public function testRateLimitedJobsCanBeSkippedOnLimitReachedAndReleasedAfter()
142+
{
143+
$rateLimiter = $this->app->make(RateLimiter::class);
144+
145+
$rateLimiter->for('test', function ($job) {
146+
return Limit::perHour(1);
147+
});
148+
149+
$this->assertJobRanSuccessfully(RateLimitedReleaseAfterTestJob::class);
150+
$this->assertJobWasReleasedAfter(RateLimitedReleaseAfterTestJob::class, 60);
151+
}
152+
141153
public function testMiddlewareSerialization()
142154
{
143155
$rateLimited = new RateLimited('limiterName');
@@ -192,6 +204,25 @@ protected function assertJobWasReleased($class)
192204
$this->assertFalse($class::$handled);
193205
}
194206

207+
protected function assertJobWasReleasedAfter($class, $releaseAfter)
208+
{
209+
$class::$handled = false;
210+
$instance = new CallQueuedHandler(new Dispatcher($this->app), $this->app);
211+
212+
$job = m::mock(Job::class);
213+
214+
$job->shouldReceive('hasFailed')->once()->andReturn(false);
215+
$job->shouldReceive('release')->once()->withArgs([$releaseAfter]);
216+
$job->shouldReceive('isReleased')->andReturn(true);
217+
$job->shouldReceive('isDeletedOrReleased')->once()->andReturn(true);
218+
219+
$instance->call($job, [
220+
'command' => serialize($command = new $class),
221+
]);
222+
223+
$this->assertFalse($class::$handled);
224+
}
225+
195226
protected function assertJobWasSkipped($class)
196227
{
197228
$class::$handled = false;
@@ -341,6 +372,14 @@ public function middleware()
341372
}
342373
}
343374

375+
class RateLimitedReleaseAfterTestJob extends RateLimitedTestJob
376+
{
377+
public function middleware()
378+
{
379+
return [(new RateLimited('test'))->releaseAfter(60)];
380+
}
381+
}
382+
344383
enum BackedEnumNamedRateLimited: string
345384
{
346385
case FOO = 'bar';

0 commit comments

Comments
 (0)