From 3540c01cf75b733e4210a9e673ad1a5b2070d9cd Mon Sep 17 00:00:00 2001 From: Tim MacDonald Date: Mon, 28 Aug 2023 16:10:53 +1000 Subject: [PATCH 1/5] Allow failed jobs to be counted by "connection" and "queue" --- .../Failed/CountableFailedJobProvider.php | 15 +++ .../Failed/DatabaseFailedJobProvider.php | 14 ++- .../Failed/DatabaseUuidFailedJobProvider.php | 13 ++- .../Queue/Failed/FileFailedJobProvider.php | 14 ++- .../Queue/Failed/NullFailedJobProvider.php | 6 +- tests/Queue/DatabaseFailedJobProviderTest.php | 91 ++++++++++++++++++- 6 files changed, 138 insertions(+), 15 deletions(-) create mode 100644 src/Illuminate/Queue/Failed/CountableFailedJobProvider.php diff --git a/src/Illuminate/Queue/Failed/CountableFailedJobProvider.php b/src/Illuminate/Queue/Failed/CountableFailedJobProvider.php new file mode 100644 index 000000000000..8593fde5608a --- /dev/null +++ b/src/Illuminate/Queue/Failed/CountableFailedJobProvider.php @@ -0,0 +1,15 @@ +getTable()->count(); + return $this->getTable() + ->when($connection, fn ($builder) => $builder->whereConnection($connection)) + ->when($queue, fn ($builder) => $builder->whereQueue($queue)) + ->count(); } /** diff --git a/src/Illuminate/Queue/Failed/DatabaseUuidFailedJobProvider.php b/src/Illuminate/Queue/Failed/DatabaseUuidFailedJobProvider.php index 2121703c11e3..4166401f02cd 100644 --- a/src/Illuminate/Queue/Failed/DatabaseUuidFailedJobProvider.php +++ b/src/Illuminate/Queue/Failed/DatabaseUuidFailedJobProvider.php @@ -2,12 +2,11 @@ namespace Illuminate\Queue\Failed; -use Countable; use DateTimeInterface; use Illuminate\Database\ConnectionResolverInterface; use Illuminate\Support\Facades\Date; -class DatabaseUuidFailedJobProvider implements Countable, FailedJobProviderInterface, PrunableFailedJobProvider +class DatabaseUuidFailedJobProvider implements CountableFailedJobProvider, FailedJobProviderInterface, PrunableFailedJobProvider { /** * The connection resolver implementation. @@ -146,10 +145,16 @@ public function prune(DateTimeInterface $before) /** * Count the failed jobs. + * + * @param string|null $connection + * @param string|null $queue */ - public function count(): int + public function count($connection = null, $queue = null): int { - return $this->getTable()->count(); + return $this->getTable() + ->when($connection, fn ($builder) => $builder->whereConnection($connection)) + ->when($queue, fn ($builder) => $builder->whereQueue($queue)) + ->count(); } /** diff --git a/src/Illuminate/Queue/Failed/FileFailedJobProvider.php b/src/Illuminate/Queue/Failed/FileFailedJobProvider.php index 8e429bacd327..6388d2e2e6dc 100644 --- a/src/Illuminate/Queue/Failed/FileFailedJobProvider.php +++ b/src/Illuminate/Queue/Failed/FileFailedJobProvider.php @@ -206,9 +206,19 @@ protected function write(array $jobs) /** * Count the failed jobs. + * + * @param string|null $connection + * @param string|null $queue + * @return int */ - public function count(): int + public function count($connection = null, $queue = null) { - return count($this->read()); + if (($connection ?? $queue) === null) { + return count($this->read()); + } + + return collect($this->read()) + ->filter(fn ($job) => $job->connection === ($connection ?? $job->connection) && $job->queue === ($queue ?? $job->queue)) + ->count(); } } diff --git a/src/Illuminate/Queue/Failed/NullFailedJobProvider.php b/src/Illuminate/Queue/Failed/NullFailedJobProvider.php index f100462e805a..dbb395ce7ca3 100644 --- a/src/Illuminate/Queue/Failed/NullFailedJobProvider.php +++ b/src/Illuminate/Queue/Failed/NullFailedJobProvider.php @@ -65,8 +65,12 @@ public function flush($hours = null) /** * Count the failed jobs. + * + * @param string|null $connection + * @param string|null $queue + * @return int */ - public function count(): int + public function count($connection = null, $queue = null) { return 0; } diff --git a/tests/Queue/DatabaseFailedJobProviderTest.php b/tests/Queue/DatabaseFailedJobProviderTest.php index 65847c0c4a7f..a8e84a8950c1 100644 --- a/tests/Queue/DatabaseFailedJobProviderTest.php +++ b/tests/Queue/DatabaseFailedJobProviderTest.php @@ -90,13 +90,96 @@ public function testJobsCanBeCounted() }); $provider = new DatabaseFailedJobProvider($db->getDatabaseManager(), 'default', 'failed_jobs'); - $this->assertCount(0, $provider); + $this->assertSame(0, $provider->count()); $provider->log('database', 'default', json_encode(['uuid' => (string) Str::uuid()]), new RuntimeException()); - $this->assertCount(1, $provider); + $this->assertSame(1, $provider->count()); $provider->log('database', 'default', json_encode(['uuid' => (string) Str::uuid()]), new RuntimeException()); - $provider->log('database', 'default', json_encode(['uuid' => (string) Str::uuid()]), new RuntimeException()); - $this->assertCount(3, $provider); + $provider->log('another-connection', 'another-queue', json_encode(['uuid' => (string) Str::uuid()]), new RuntimeException()); + $this->assertSame(3, $provider->count()); + } + + public function testJobsCanBeCountedByConnection() + { + $db = new DB; + $db->addConnection([ + 'driver' => 'sqlite', + 'database' => ':memory:', + ]); + $db->getConnection()->getSchemaBuilder()->create('failed_jobs', function (Blueprint $table) { + $table->id(); + $table->text('connection'); + $table->text('queue'); + $table->longText('payload'); + $table->longText('exception'); + $table->timestamp('failed_at')->useCurrent(); + }); + $provider = new DatabaseFailedJobProvider($db->getDatabaseManager(), 'default', 'failed_jobs'); + + $provider->log('connection-1', 'default', json_encode(['uuid' => (string) Str::uuid()]), new RuntimeException()); + $provider->log('connection-2', 'default', json_encode(['uuid' => (string) Str::uuid()]), new RuntimeException()); + $this->assertSame(1, $provider->count('connection-1')); + $this->assertSame(1, $provider->count('connection-2')); + + $provider->log('connection-1', 'default', json_encode(['uuid' => (string) Str::uuid()]), new RuntimeException()); + $this->assertSame(2, $provider->count('connection-1')); + $this->assertSame(1, $provider->count('connection-2')); + } + + public function testJobsCanBeCountedByQueue() + { + $db = new DB; + $db->addConnection([ + 'driver' => 'sqlite', + 'database' => ':memory:', + ]); + $db->getConnection()->getSchemaBuilder()->create('failed_jobs', function (Blueprint $table) { + $table->id(); + $table->text('connection'); + $table->text('queue'); + $table->longText('payload'); + $table->longText('exception'); + $table->timestamp('failed_at')->useCurrent(); + }); + $provider = new DatabaseFailedJobProvider($db->getDatabaseManager(), 'default', 'failed_jobs'); + + $provider->log('database', 'queue-1', json_encode(['uuid' => (string) Str::uuid()]), new RuntimeException()); + $provider->log('database', 'queue-2', json_encode(['uuid' => (string) Str::uuid()]), new RuntimeException()); + $this->assertSame(1, $provider->count(queue: 'queue-1')); + $this->assertSame(1, $provider->count(queue: 'queue-2')); + + $provider->log('database', 'queue-1', json_encode(['uuid' => (string) Str::uuid()]), new RuntimeException()); + $this->assertSame(2, $provider->count(queue: 'queue-1')); + $this->assertSame(1, $provider->count(queue: 'queue-2')); + } + + public function testJobsCanBeCountedByQueueAndConnection() + { + $db = new DB; + $db->addConnection([ + 'driver' => 'sqlite', + 'database' => ':memory:', + ]); + $db->getConnection()->getSchemaBuilder()->create('failed_jobs', function (Blueprint $table) { + $table->id(); + $table->text('connection'); + $table->text('queue'); + $table->longText('payload'); + $table->longText('exception'); + $table->timestamp('failed_at')->useCurrent(); + }); + $provider = new DatabaseFailedJobProvider($db->getDatabaseManager(), 'default', 'failed_jobs'); + + $provider->log('connection-1', 'queue-99', json_encode(['uuid' => (string) Str::uuid()]), new RuntimeException()); + $provider->log('connection-1', 'queue-99', json_encode(['uuid' => (string) Str::uuid()]), new RuntimeException()); + $provider->log('connection-2', 'queue-99', json_encode(['uuid' => (string) Str::uuid()]), new RuntimeException()); + $provider->log('connection-1', 'queue-1', json_encode(['uuid' => (string) Str::uuid()]), new RuntimeException()); + $provider->log('connection-2', 'queue-1', json_encode(['uuid' => (string) Str::uuid()]), new RuntimeException()); + $provider->log('connection-2', 'queue-1', json_encode(['uuid' => (string) Str::uuid()]), new RuntimeException()); + $this->assertSame(2, $provider->count('connection-1', 'queue-99')); + $this->assertSame(1, $provider->count('connection-2', 'queue-99')); + $this->assertSame(1, $provider->count('connection-1', 'queue-1')); + $this->assertSame(2, $provider->count('connection-2', 'queue-1')); } } From 9e55fcc34ae04228f4acabebf3604820e94b9d70 Mon Sep 17 00:00:00 2001 From: Tim MacDonald Date: Tue, 29 Aug 2023 09:36:53 +1000 Subject: [PATCH 2/5] Fix and improve tests --- .../Queue/Failed/FileFailedJobProvider.php | 2 +- .../Queue/Failed/NullFailedJobProvider.php | 2 +- tests/Queue/FileFailedJobProviderTest.php | 54 ++++++++++++++++--- 3 files changed, 48 insertions(+), 10 deletions(-) diff --git a/src/Illuminate/Queue/Failed/FileFailedJobProvider.php b/src/Illuminate/Queue/Failed/FileFailedJobProvider.php index 6388d2e2e6dc..e26f02579cd7 100644 --- a/src/Illuminate/Queue/Failed/FileFailedJobProvider.php +++ b/src/Illuminate/Queue/Failed/FileFailedJobProvider.php @@ -7,7 +7,7 @@ use DateTimeInterface; use Illuminate\Support\Facades\Date; -class FileFailedJobProvider implements Countable, FailedJobProviderInterface, PrunableFailedJobProvider +class FileFailedJobProvider implements CountableFailedJobProvider, FailedJobProviderInterface, PrunableFailedJobProvider { /** * The file path where the failed job file should be stored. diff --git a/src/Illuminate/Queue/Failed/NullFailedJobProvider.php b/src/Illuminate/Queue/Failed/NullFailedJobProvider.php index dbb395ce7ca3..9b6a0901a378 100644 --- a/src/Illuminate/Queue/Failed/NullFailedJobProvider.php +++ b/src/Illuminate/Queue/Failed/NullFailedJobProvider.php @@ -4,7 +4,7 @@ use Countable; -class NullFailedJobProvider implements Countable, FailedJobProviderInterface +class NullFailedJobProvider implements CountableFailedJobProvider, FailedJobProviderInterface { /** * Log a failed job into storage. diff --git a/tests/Queue/FileFailedJobProviderTest.php b/tests/Queue/FileFailedJobProviderTest.php index 00d304131cda..3ecbfec8e4d5 100644 --- a/tests/Queue/FileFailedJobProviderTest.php +++ b/tests/Queue/FileFailedJobProviderTest.php @@ -142,23 +142,61 @@ public function testEmptyFailedJobsByDefault() public function testJobsCanBeCounted() { - $this->assertCount(0, $this->provider); + $this->assertSame(0, $this->provider->count()); - $this->logFailedJob(); - $this->assertCount(1, $this->provider); + $this->logFailedJob('database', 'default'); + $this->assertSame(1, $this->provider->count()); - $this->logFailedJob(); - $this->logFailedJob(); - $this->assertCount(3, $this->provider); + $this->logFailedJob('database', 'default'); + $this->logFailedJob('another-connection', 'another-queue'); + $this->assertSame(3, $this->provider->count()); + } + + public function testJobsCanBeCountedByConnection() + { + $this->logFailedJob('connection-1', 'default'); + $this->logFailedJob('connection-2', 'default'); + $this->assertSame(1, $this->provider->count('connection-1')); + $this->assertSame(1, $this->provider->count('connection-2')); + + $this->logFailedJob('connection-1', 'default'); + $this->assertSame(2, $this->provider->count('connection-1')); + $this->assertSame(1, $this->provider->count('connection-2')); + } + + public function testJobsCanBeCountedByQueue() + { + $this->logFailedJob('database', 'queue-1'); + $this->logFailedJob('database', 'queue-2'); + $this->assertSame(1, $this->provider->count(queue: 'queue-1')); + $this->assertSame(1, $this->provider->count(queue: 'queue-2')); + + $this->logFailedJob('database', 'queue-1'); + $this->assertSame(2, $this->provider->count(queue: 'queue-1')); + $this->assertSame(1, $this->provider->count(queue: 'queue-2')); + } + + public function testJobsCanBeCountedByQueueAndConnection() + { + $this->logFailedJob('connection-1', 'queue-99'); + $this->logFailedJob('connection-1', 'queue-99'); + $this->logFailedJob('connection-2', 'queue-99'); + $this->logFailedJob('connection-1', 'queue-1'); + $this->logFailedJob('connection-2', 'queue-1'); + $this->logFailedJob('connection-2', 'queue-1'); + $this->assertSame(2, $this->provider->count('connection-1', 'queue-99')); + $this->assertSame(1, $this->provider->count('connection-2', 'queue-99')); + $this->assertSame(1, $this->provider->count('connection-1', 'queue-1')); + $this->assertSame(2, $this->provider->count('connection-2', 'queue-1')); } - public function logFailedJob() + public function logFailedJob($connection = 'connection', $queue = 'queue') { $uuid = Str::uuid(); $exception = new Exception("Something went wrong at job [{$uuid}]."); - $this->provider->log('connection', 'queue', json_encode(['uuid' => (string) $uuid]), $exception); + $this->provider->log($connection, $queue, json_encode(['uuid' => (string) $uuid]), $exception); return [(string) $uuid, $exception]; } From 84eca9b207a46b2fce9db34c4127152f52efeb01 Mon Sep 17 00:00:00 2001 From: Tim MacDonald Date: Tue, 29 Aug 2023 09:43:12 +1000 Subject: [PATCH 3/5] additional tests --- .../DatabaseUuidFailedJobProviderTest.php | 95 +++++++++++++++++-- 1 file changed, 89 insertions(+), 6 deletions(-) diff --git a/tests/Queue/DatabaseUuidFailedJobProviderTest.php b/tests/Queue/DatabaseUuidFailedJobProviderTest.php index 23bc6715933f..d99284a89986 100644 --- a/tests/Queue/DatabaseUuidFailedJobProviderTest.php +++ b/tests/Queue/DatabaseUuidFailedJobProviderTest.php @@ -28,13 +28,96 @@ public function testJobsCanBeCounted() }); $provider = new DatabaseUuidFailedJobProvider($db->getDatabaseManager(), 'default', 'failed_jobs'); - $this->assertCount(0, $provider); + $this->assertSame(0, $provider->count()); - $provider->log('database', 'default', json_encode(['uuid' => (string) Str::uuid()]), new RuntimeException()); - $this->assertCount(1, $provider); + $provider->log('connection-1', 'queue-1', json_encode(['uuid' => (string) Str::uuid()]), new RuntimeException()); + $this->assertSame(1, $provider->count()); - $provider->log('database', 'default', json_encode(['uuid' => (string) Str::uuid()]), new RuntimeException()); - $provider->log('database', 'default', json_encode(['uuid' => (string) Str::uuid()]), new RuntimeException()); - $this->assertCount(3, $provider); + $provider->log('connection-1', 'queue-1', json_encode(['uuid' => (string) Str::uuid()]), new RuntimeException()); + $provider->log('connection-2', 'queue-2', json_encode(['uuid' => (string) Str::uuid()]), new RuntimeException()); + $this->assertSame(3, $provider->count()); + } + + public function testJobsCanBeCountedByConnection() + { + $db = new DB; + $db->addConnection([ + 'driver' => 'sqlite', + 'database' => ':memory:', + ]); + $db->getConnection()->getSchemaBuilder()->create('failed_jobs', function (Blueprint $table) { + $table->uuid(); + $table->text('connection'); + $table->text('queue'); + $table->longText('payload'); + $table->longText('exception'); + $table->timestamp('failed_at')->useCurrent(); + }); + $provider = new DatabaseUuidFailedJobProvider($db->getDatabaseManager(), 'default', 'failed_jobs'); + + $provider->log('connection-1', 'default', json_encode(['uuid' => (string) Str::uuid()]), new RuntimeException()); + $provider->log('connection-2', 'default', json_encode(['uuid' => (string) Str::uuid()]), new RuntimeException()); + $this->assertSame(1, $provider->count('connection-1')); + $this->assertSame(1, $provider->count('connection-2')); + + $provider->log('connection-1', 'default', json_encode(['uuid' => (string) Str::uuid()]), new RuntimeException()); + $this->assertSame(2, $provider->count('connection-1')); + $this->assertSame(1, $provider->count('connection-2')); + } + + public function testJobsCanBeCountedByQueue() + { + $db = new DB; + $db->addConnection([ + 'driver' => 'sqlite', + 'database' => ':memory:', + ]); + $db->getConnection()->getSchemaBuilder()->create('failed_jobs', function (Blueprint $table) { + $table->uuid(); + $table->text('connection'); + $table->text('queue'); + $table->longText('payload'); + $table->longText('exception'); + $table->timestamp('failed_at')->useCurrent(); + }); + $provider = new DatabaseUuidFailedJobProvider($db->getDatabaseManager(), 'default', 'failed_jobs'); + + $provider->log('database', 'queue-1', json_encode(['uuid' => (string) Str::uuid()]), new RuntimeException()); + $provider->log('database', 'queue-2', json_encode(['uuid' => (string) Str::uuid()]), new RuntimeException()); + $this->assertSame(1, $provider->count(queue: 'queue-1')); + $this->assertSame(1, $provider->count(queue: 'queue-2')); + + $provider->log('database', 'queue-1', json_encode(['uuid' => (string) Str::uuid()]), new RuntimeException()); + $this->assertSame(2, $provider->count(queue: 'queue-1')); + $this->assertSame(1, $provider->count(queue: 'queue-2')); + } + + public function testJobsCanBeCountedByQueueAndConnection() + { + $db = new DB; + $db->addConnection([ + 'driver' => 'sqlite', + 'database' => ':memory:', + ]); + $db->getConnection()->getSchemaBuilder()->create('failed_jobs', function (Blueprint $table) { + $table->uuid(); + $table->text('connection'); + $table->text('queue'); + $table->longText('payload'); + $table->longText('exception'); + $table->timestamp('failed_at')->useCurrent(); + }); + $provider = new DatabaseUuidFailedJobProvider($db->getDatabaseManager(), 'default', 'failed_jobs'); + + $provider->log('connection-1', 'queue-99', json_encode(['uuid' => (string) Str::uuid()]), new RuntimeException()); + $provider->log('connection-1', 'queue-99', json_encode(['uuid' => (string) Str::uuid()]), new RuntimeException()); + $provider->log('connection-2', 'queue-99', json_encode(['uuid' => (string) Str::uuid()]), new RuntimeException()); + $provider->log('connection-1', 'queue-1', json_encode(['uuid' => (string) Str::uuid()]), new RuntimeException()); + $provider->log('connection-2', 'queue-1', json_encode(['uuid' => (string) Str::uuid()]), new RuntimeException()); + $provider->log('connection-2', 'queue-1', json_encode(['uuid' => (string) Str::uuid()]), new RuntimeException()); + $this->assertSame(2, $provider->count('connection-1', 'queue-99')); + $this->assertSame(1, $provider->count('connection-2', 'queue-99')); + $this->assertSame(1, $provider->count('connection-1', 'queue-1')); + $this->assertSame(2, $provider->count('connection-2', 'queue-1')); } } From 4e1604c863a10df0c32ff7fe173deff70ad3b411 Mon Sep 17 00:00:00 2001 From: Tim MacDonald Date: Tue, 29 Aug 2023 10:38:56 +1000 Subject: [PATCH 4/5] lint --- src/Illuminate/Queue/Failed/FileFailedJobProvider.php | 1 - src/Illuminate/Queue/Failed/NullFailedJobProvider.php | 2 -- 2 files changed, 3 deletions(-) diff --git a/src/Illuminate/Queue/Failed/FileFailedJobProvider.php b/src/Illuminate/Queue/Failed/FileFailedJobProvider.php index e26f02579cd7..f79efc05f39f 100644 --- a/src/Illuminate/Queue/Failed/FileFailedJobProvider.php +++ b/src/Illuminate/Queue/Failed/FileFailedJobProvider.php @@ -3,7 +3,6 @@ namespace Illuminate\Queue\Failed; use Closure; -use Countable; use DateTimeInterface; use Illuminate\Support\Facades\Date; diff --git a/src/Illuminate/Queue/Failed/NullFailedJobProvider.php b/src/Illuminate/Queue/Failed/NullFailedJobProvider.php index 9b6a0901a378..a086134c98f3 100644 --- a/src/Illuminate/Queue/Failed/NullFailedJobProvider.php +++ b/src/Illuminate/Queue/Failed/NullFailedJobProvider.php @@ -2,8 +2,6 @@ namespace Illuminate\Queue\Failed; -use Countable; - class NullFailedJobProvider implements CountableFailedJobProvider, FailedJobProviderInterface { /** From 38debe4fadbb48f49d9af114aefdd6aff3dd7997 Mon Sep 17 00:00:00 2001 From: Tim MacDonald Date: Tue, 29 Aug 2023 10:40:49 +1000 Subject: [PATCH 5/5] standardise --- src/Illuminate/Queue/Failed/DatabaseUuidFailedJobProvider.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Illuminate/Queue/Failed/DatabaseUuidFailedJobProvider.php b/src/Illuminate/Queue/Failed/DatabaseUuidFailedJobProvider.php index 4166401f02cd..f51c46f571b0 100644 --- a/src/Illuminate/Queue/Failed/DatabaseUuidFailedJobProvider.php +++ b/src/Illuminate/Queue/Failed/DatabaseUuidFailedJobProvider.php @@ -148,8 +148,9 @@ public function prune(DateTimeInterface $before) * * @param string|null $connection * @param string|null $queue + * @return int */ - public function count($connection = null, $queue = null): int + public function count($connection = null, $queue = null) { return $this->getTable() ->when($connection, fn ($builder) => $builder->whereConnection($connection))