Skip to content

Commit 4028c1d

Browse files
authored
[12.x] Use displayName() for custom job identification (#57499)
* Add jobTypeIdentifier() method for custom job identification * Use displayName() for custom job identification in locks and middleware * Update UniqueBroadcastEvent to work with displayName() in lock key
1 parent 263ea42 commit 4028c1d

File tree

8 files changed

+257
-8
lines changed

8 files changed

+257
-8
lines changed

src/Illuminate/Broadcasting/UniqueBroadcastEvent.php

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,6 @@ class UniqueBroadcastEvent extends BroadcastEvent implements ShouldBeUnique
2929
*/
3030
public function __construct($event)
3131
{
32-
$this->uniqueId = get_class($event);
33-
3432
if (method_exists($event, 'uniqueId')) {
3533
$this->uniqueId .= $event->uniqueId();
3634
} elseif (property_exists($event, 'uniqueId')) {

src/Illuminate/Bus/UniqueLock.php

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,10 @@ public static function getKey($job)
6969
? $job->uniqueId()
7070
: ($job->uniqueId ?? '');
7171

72-
return 'laravel_unique_job:'.get_class($job).':'.$uniqueId;
72+
$jobName = method_exists($job, 'displayName')
73+
? $job->displayName()
74+
: get_class($job);
75+
76+
return 'laravel_unique_job:'.$jobName.':'.$uniqueId;
7377
}
7478
}

src/Illuminate/Queue/Middleware/ThrottlesExceptions.php

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -256,7 +256,11 @@ protected function getKey($job)
256256
return $this->prefix.$job->job->uuid();
257257
}
258258

259-
return $this->prefix.hash('xxh128', get_class($job));
259+
$jobName = method_exists($job, 'displayName')
260+
? $job->displayName()
261+
: get_class($job);
262+
263+
return $this->prefix.hash('xxh128', $jobName);
260264
}
261265

262266
/**

src/Illuminate/Queue/Middleware/WithoutOverlapping.php

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -154,8 +154,14 @@ public function shared()
154154
*/
155155
public function getLockKey($job)
156156
{
157-
return $this->shareKey
158-
? $this->prefix.$this->key
159-
: $this->prefix.get_class($job).':'.$this->key;
157+
if ($this->shareKey) {
158+
return $this->prefix.$this->key;
159+
}
160+
161+
$jobName = method_exists($job, 'displayName')
162+
? $job->displayName()
163+
: get_class($job);
164+
165+
return $this->prefix.$jobName.':'.$this->key;
160166
}
161167
}

tests/Integration/Broadcasting/BroadcastManagerTest.php

Lines changed: 39 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,35 @@ public function testUniqueEventsCanBeBroadcast()
7575
Bus::assertNotDispatched(UniqueBroadcastEvent::class);
7676
Queue::assertPushed(UniqueBroadcastEvent::class);
7777

78-
$lockKey = 'laravel_unique_job:'.UniqueBroadcastEvent::class.':'.TestEventUnique::class;
78+
$lockKey = 'laravel_unique_job:'.TestEventUnique::class.':';
79+
$this->assertFalse($this->app->get(Cache::class)->lock($lockKey, 10)->get());
80+
}
81+
82+
public function testUniqueEventsCanBeBroadcastWithUniqueIdFromProperty()
83+
{
84+
Bus::fake();
85+
Queue::fake();
86+
87+
Broadcast::queue(new TestEventUniqueWithIdProperty);
88+
89+
Bus::assertNotDispatched(UniqueBroadcastEvent::class);
90+
Queue::assertPushed(UniqueBroadcastEvent::class);
91+
92+
$lockKey = 'laravel_unique_job:'.TestEventUniqueWithIdProperty::class.':unique-id-property';
93+
$this->assertFalse($this->app->get(Cache::class)->lock($lockKey, 10)->get());
94+
}
95+
96+
public function testUniqueEventsCanBeBroadcastWithUniqueIdFromMethod()
97+
{
98+
Bus::fake();
99+
Queue::fake();
100+
101+
Broadcast::queue(new TestEventUniqueWithIdMethod);
102+
103+
Bus::assertNotDispatched(UniqueBroadcastEvent::class);
104+
Queue::assertPushed(UniqueBroadcastEvent::class);
105+
106+
$lockKey = 'laravel_unique_job:'.TestEventUniqueWithIdMethod::class.':unique-id-method';
79107
$this->assertFalse($this->app->get(Cache::class)->lock($lockKey, 10)->get());
80108
}
81109

@@ -178,6 +206,16 @@ public function broadcastOn()
178206
}
179207
}
180208

209+
class TestEventUniqueWithIdProperty extends TestEventUnique
210+
{
211+
public string $uniqueId = 'unique-id-property';
212+
}
213+
214+
class TestEventUniqueWithIdMethod extends TestEventUnique
215+
{
216+
public string $uniqueId = 'unique-id-method';
217+
}
218+
181219
class TestEventRescue implements ShouldBroadcast, ShouldRescue
182220
{
183221
/**

tests/Integration/Queue/ThrottlesExceptionsTest.php

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
use Exception;
66
use Illuminate\Bus\Dispatcher;
77
use Illuminate\Bus\Queueable;
8+
use Illuminate\Cache\RateLimiter;
89
use Illuminate\Contracts\Debug\ExceptionHandler;
910
use Illuminate\Contracts\Queue\Job;
1011
use Illuminate\Queue\CallQueuedHandler;
@@ -345,6 +346,85 @@ public function release()
345346
$middleware->report(fn () => false);
346347
$middleware->handle($job, $next);
347348
}
349+
350+
public function testUsesJobClassNameForCacheKey()
351+
{
352+
$rateLimiter = $this->mock(RateLimiter::class);
353+
354+
$job = new class
355+
{
356+
public $released = false;
357+
358+
public function release()
359+
{
360+
$this->released = true;
361+
362+
return $this;
363+
}
364+
};
365+
366+
$expectedKey = 'laravel_throttles_exceptions:'.hash('xxh128', get_class($job));
367+
368+
$rateLimiter->shouldReceive('tooManyAttempts')
369+
->once()
370+
->with($expectedKey, 10)
371+
->andReturn(false);
372+
373+
$rateLimiter->shouldReceive('hit')
374+
->once()
375+
->with($expectedKey, 600);
376+
377+
$next = function ($job) {
378+
throw new RuntimeException('Whoops!');
379+
};
380+
381+
$middleware = new ThrottlesExceptions();
382+
$middleware->handle($job, $next);
383+
384+
$this->assertTrue($job->released);
385+
}
386+
387+
public function testUsesDisplayNameForCacheKeyWhenAvailable()
388+
{
389+
$rateLimiter = $this->mock(RateLimiter::class);
390+
391+
$job = new class
392+
{
393+
public $released = false;
394+
395+
public function release()
396+
{
397+
$this->released = true;
398+
399+
return $this;
400+
}
401+
402+
public function displayName(): string
403+
{
404+
return 'App\\Actions\\ThrottlesExceptionsTestAction';
405+
}
406+
};
407+
408+
$expectedKey = 'laravel_throttles_exceptions:'.hash('xxh128', 'App\\Actions\\ThrottlesExceptionsTestAction');
409+
410+
$rateLimiter->shouldReceive('tooManyAttempts')
411+
->once()
412+
->with($expectedKey, 10)
413+
->andReturn(false);
414+
415+
$rateLimiter->shouldReceive('hit')
416+
->once()
417+
->with($expectedKey, 600);
418+
419+
$next = function ($job) {
420+
throw new RuntimeException('Whoops!');
421+
};
422+
423+
$middleware = new ThrottlesExceptions();
424+
$middleware->handle($job, $next);
425+
426+
$this->assertTrue($job->released);
427+
}
348428
}
349429

350430
class CircuitBreakerTestJob

tests/Integration/Queue/UniqueJobTest.php

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
use Exception;
66
use Illuminate\Bus\Queueable;
7+
use Illuminate\Bus\UniqueLock;
78
use Illuminate\Container\Container;
89
use Illuminate\Contracts\Cache\Repository as Cache;
910
use Illuminate\Contracts\Queue\ShouldBeUnique;
@@ -169,6 +170,62 @@ protected function getLockKey($job)
169170
{
170171
return 'laravel_unique_job:'.(is_string($job) ? $job : get_class($job)).':';
171172
}
173+
174+
public function testLockUsesDisplayNameWhenAvailable()
175+
{
176+
Bus::fake();
177+
178+
$lockKey = 'laravel_unique_job:App\\Actions\\UniqueTestAction:';
179+
180+
dispatch(new UniqueTestJobWithDisplayName);
181+
$this->runQueueWorkerCommand(['--once' => true]);
182+
Bus::assertDispatched(UniqueTestJobWithDisplayName::class);
183+
184+
$this->assertFalse(
185+
$this->app->get(Cache::class)->lock($lockKey, 10)->get()
186+
);
187+
188+
Bus::assertDispatchedTimes(UniqueTestJobWithDisplayName::class);
189+
dispatch(new UniqueTestJobWithDisplayName);
190+
$this->runQueueWorkerCommand(['--once' => true]);
191+
Bus::assertDispatchedTimes(UniqueTestJobWithDisplayName::class);
192+
193+
$this->assertFalse(
194+
$this->app->get(Cache::class)->lock($lockKey, 10)->get()
195+
);
196+
}
197+
198+
public function testUniqueLockCreatesKeyWithClassName()
199+
{
200+
$this->assertEquals(
201+
'laravel_unique_job:'.UniqueTestJob::class.':',
202+
UniqueLock::getKey(new UniqueTestJob)
203+
);
204+
}
205+
206+
public function testUniqueLockCreatesKeyWithIdAndClassName()
207+
{
208+
$this->assertEquals(
209+
'laravel_unique_job:'.UniqueIdTestJob::class.':unique-id-1',
210+
UniqueLock::getKey(new UniqueIdTestJob)
211+
);
212+
}
213+
214+
public function testUniqueLockCreatesKeyWithDisplayNameWhenAvailable()
215+
{
216+
$this->assertEquals(
217+
'laravel_unique_job:App\\Actions\\UniqueTestAction:unique-id-2',
218+
UniqueLock::getKey(new UniqueIdTestJobWithDisplayName)
219+
);
220+
}
221+
222+
public function testUniqueLockCreatesKeyWithIdAndDisplayNameWhenAvailable()
223+
{
224+
$this->assertEquals(
225+
'laravel_unique_job:App\\Actions\\UniqueTestAction:unique-id-2',
226+
UniqueLock::getKey(new UniqueIdTestJobWithDisplayName)
227+
);
228+
}
172229
}
173230

174231
class UniqueTestJob implements ShouldQueue, ShouldBeUnique
@@ -239,3 +296,32 @@ public function uniqueVia(): Cache
239296
return Container::getInstance()->make(Cache::class);
240297
}
241298
}
299+
300+
class UniqueIdTestJob extends UniqueTestJob
301+
{
302+
public function uniqueId(): string
303+
{
304+
return 'unique-id-1';
305+
}
306+
}
307+
308+
class UniqueTestJobWithDisplayName extends UniqueTestJob
309+
{
310+
public function displayName(): string
311+
{
312+
return 'App\\Actions\\UniqueTestAction';
313+
}
314+
}
315+
316+
class UniqueIdTestJobWithDisplayName extends UniqueTestJob
317+
{
318+
public function uniqueId(): string
319+
{
320+
return 'unique-id-2';
321+
}
322+
323+
public function displayName(): string
324+
{
325+
return 'App\\Actions\\UniqueTestAction';
326+
}
327+
}

tests/Integration/Queue/WithoutOverlappingJobsTest.php

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,31 @@ public function testGetLock()
151151
(new WithoutOverlapping('key'))->withPrefix('prefix:')->shared()->getLockKey($job)
152152
);
153153
}
154+
155+
public function testGetLockUsesDisplayName()
156+
{
157+
$job = new OverlappingTestJobWithDisplayName;
158+
159+
$this->assertSame(
160+
'laravel-queue-overlap:App\\Actions\\WithoutOverlappingTestAction:key',
161+
(new WithoutOverlapping('key'))->getLockKey($job)
162+
);
163+
164+
$this->assertSame(
165+
'laravel-queue-overlap:key',
166+
(new WithoutOverlapping('key'))->shared()->getLockKey($job)
167+
);
168+
169+
$this->assertSame(
170+
'prefix:App\\Actions\\WithoutOverlappingTestAction:key',
171+
(new WithoutOverlapping('key'))->withPrefix('prefix:')->getLockKey($job)
172+
);
173+
174+
$this->assertSame(
175+
'prefix:key',
176+
(new WithoutOverlapping('key'))->withPrefix('prefix:')->shared()->getLockKey($job)
177+
);
178+
}
154179
}
155180

156181
class OverlappingTestJob
@@ -221,3 +246,11 @@ public function middleware()
221246
return [(new WithoutOverlapping)->shared()];
222247
}
223248
}
249+
250+
class OverlappingTestJobWithDisplayName extends OverlappingTestJob
251+
{
252+
public function displayName(): string
253+
{
254+
return 'App\\Actions\\WithoutOverlappingTestAction';
255+
}
256+
}

0 commit comments

Comments
 (0)