Skip to content

Commit 1da2839

Browse files
authored
[13.x] Cache getLockForPopping() result in DatabaseQueue (#59435)
The lock type is determined by database engine and version, which never change during a queue worker's lifetime. Cache the result to avoid repeated PDO attribute lookups, Stringable allocations, regex parsing, and version_compare calls on every job pop. Fixes #59350
1 parent 930e42f commit 1da2839

2 files changed

Lines changed: 35 additions & 3 deletions

File tree

src/Illuminate/Queue/DatabaseQueue.php

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,13 @@ class DatabaseQueue extends Queue implements QueueContract, ClearableQueue
4444
*/
4545
protected $retryAfter = 60;
4646

47+
/**
48+
* The cached lock type for popping jobs.
49+
*
50+
* @var string|bool|null
51+
*/
52+
protected $lockForPopping = null;
53+
4754
/**
4855
* Create a new database queue instance.
4956
*
@@ -338,6 +345,10 @@ protected function getNextAvailableJob($queue)
338345
*/
339346
protected function getLockForPopping()
340347
{
348+
if ($this->lockForPopping !== null) {
349+
return $this->lockForPopping;
350+
}
351+
341352
$databaseEngine = $this->database->getPdo()->getAttribute(PDO::ATTR_DRIVER_NAME);
342353
$databaseVersion = $this->database->getConfig('version') ?? $this->database->getPdo()->getAttribute(PDO::ATTR_SERVER_VERSION);
343354

@@ -354,14 +365,14 @@ protected function getLockForPopping()
354365
($databaseEngine === 'pgsql' && version_compare($databaseVersion, '9.5', '>=')) ||
355366
($databaseEngine === 'vitess' && version_compare($databaseVersion, '19.0', '>='))
356367
) {
357-
return 'FOR UPDATE SKIP LOCKED';
368+
return $this->lockForPopping = 'FOR UPDATE SKIP LOCKED';
358369
}
359370

360371
if ($databaseEngine === 'sqlsrv') {
361-
return 'with(rowlock,updlock,readpast)';
372+
return $this->lockForPopping = 'with(rowlock,updlock,readpast)';
362373
}
363374

364-
return true;
375+
return $this->lockForPopping = true;
365376
}
366377

367378
/**

tests/Queue/QueueDatabaseQueueUnitTest.php

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -196,6 +196,27 @@ public function testBuildDatabaseRecordWithPayloadAtTheEnd()
196196
$this->assertArrayHasKey('payload', $record);
197197
$this->assertArrayHasKey('payload', array_slice($record, -1, 1, true));
198198
}
199+
200+
public function testGetLockForPoppingIsCached()
201+
{
202+
$database = m::mock(Connection::class);
203+
$queue = new DatabaseQueue($database, 'table', 'default');
204+
205+
$pdo = m::mock(\PDO::class);
206+
$pdo->shouldReceive('getAttribute')->with(\PDO::ATTR_DRIVER_NAME)->once()->andReturn('mysql');
207+
$pdo->shouldReceive('getAttribute')->with(\PDO::ATTR_SERVER_VERSION)->once()->andReturn('8.0.36');
208+
209+
$database->shouldReceive('getPdo')->andReturn($pdo);
210+
$database->shouldReceive('getConfig')->with('version')->andReturn(null);
211+
212+
$method = new \ReflectionMethod($queue, 'getLockForPopping');
213+
214+
$result1 = $method->invoke($queue);
215+
$result2 = $method->invoke($queue);
216+
217+
$this->assertSame('FOR UPDATE SKIP LOCKED', $result1);
218+
$this->assertSame($result1, $result2);
219+
}
199220
}
200221

201222
class MyTestJob

0 commit comments

Comments
 (0)