Skip to content

Commit

Permalink
[5.x] Fix JobWatcher when processing deleted instance of `Serialize…
Browse files Browse the repository at this point in the history
…sModels` (#1539)

* Replace unserialize with preg_match

* Code style

* Code style

* Code style

* Code style

* Only run preg_match when unserialize fails

* Code style

* wip

Signed-off-by: Mior Muhammad Zaki <[email protected]>

* wip

Signed-off-by: Mior Muhammad Zaki <[email protected]>

* wip

Signed-off-by: Mior Muhammad Zaki <[email protected]>

* wip

Signed-off-by: Mior Muhammad Zaki <[email protected]>

* wip

Signed-off-by: Mior Muhammad Zaki <[email protected]>

* wip

Signed-off-by: Mior Muhammad Zaki <[email protected]>

* wip

Signed-off-by: Mior Muhammad Zaki <[email protected]>

* wip

Signed-off-by: Mior Muhammad Zaki <[email protected]>

* wip

Signed-off-by: Mior Muhammad Zaki <[email protected]>

* Update JobWatcher.php

---------

Signed-off-by: Mior Muhammad Zaki <[email protected]>
Co-authored-by: Mior Muhammad Zaki <[email protected]>
Co-authored-by: Taylor Otwell <[email protected]>
  • Loading branch information
3 people authored Oct 29, 2024
1 parent b4bb841 commit 749369e
Show file tree
Hide file tree
Showing 2 changed files with 77 additions and 41 deletions.
38 changes: 30 additions & 8 deletions src/Watchers/JobWatcher.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

use Illuminate\Bus\BatchRepository;
use Illuminate\Contracts\Encryption\Encrypter;
use Illuminate\Database\Eloquent\ModelNotFoundException;
use Illuminate\Queue\Events\JobFailed;
use Illuminate\Queue\Events\JobProcessed;
use Illuminate\Queue\Queue;
Expand Down Expand Up @@ -210,25 +211,21 @@ protected function updateBatch($payload)

Telescope::$shouldRecord = false;

$command = $this->getCommand($payload['data']);
$batchId = $this->getBatchId($payload['data']);

if ($wasRecordingEnabled) {
Telescope::$shouldRecord = true;
}

$properties = ExtractProperties::from(
$command
);

if (isset($properties['batchId'])) {
$batch = app(BatchRepository::class)->find($properties['batchId']);
if (! is_null($batchId)) {
$batch = app(BatchRepository::class)->find($batchId);

if (is_null($batch)) {
return;
}

Telescope::recordUpdate(EntryUpdate::make(
$properties['batchId'], EntryType::BATCH, $batch->toArray()
$batchId, EntryType::BATCH, $batch->toArray()
));
}
}
Expand All @@ -253,4 +250,29 @@ protected function getCommand(array $data)

throw new RuntimeException('Unable to extract job payload.');
}

/**
* Get the batch ID from the given payload.
*
* @param array $data
* @return int|null
*
* @throws \RuntimeException
*/
protected function getBatchId(array $data)
{
try {
$command = $this->getCommand($data);

$properties = ExtractProperties::from($command);

return $properties['batchId'] ?? null;
} catch (ModelNotFoundException $e) {
if (preg_match('/"batchId";s:\d+:"([^"]+)"/', $data['command'], $matches)) {
return $matches[1];
}
}

return null;
}
}
80 changes: 47 additions & 33 deletions tests/Watchers/JobWatcherTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,19 @@
use Illuminate\Contracts\Bus\Dispatcher;
use Illuminate\Contracts\Debug\ExceptionHandler;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Foundation\Auth\User;
use Illuminate\Queue\Jobs\Job;
use Illuminate\Queue\QueueManager;
use Illuminate\Support\Facades\Schema;
use Illuminate\Queue\SerializesModels;
use Illuminate\Support\Str;
use Laravel\Telescope\EntryType;
use Laravel\Telescope\Tests\FeatureTestCase;
use Laravel\Telescope\Watchers\JobWatcher;
use Orchestra\Testbench\Attributes\WithMigration;
use Orchestra\Testbench\Factories\UserFactory;
use Throwable;

#[WithMigration('queue')]
class JobWatcherTest extends FeatureTestCase
{
protected function getEnvironmentSetUp($app)
Expand All @@ -31,13 +34,6 @@ protected function getEnvironmentSetUp($app)
$app->get('config')->set('logging.default', 'syslog');
}

protected function setUp(): void
{
parent::setUp();

$this->createJobsTable();
}

public function test_job_registers_entry()
{
$this->app->get(Dispatcher::class)->dispatch(new MyDatabaseJob('Awesome Laravel'));
Expand Down Expand Up @@ -130,31 +126,28 @@ public function test_it_handles_pushed_jobs()
$this->assertSame(['framework' => 'Laravel'], $entry->content['data']);
}

private function createJobsTable(): void
public function test_job_can_handle_deleted_serialized_model()
{
if (! Schema::hasTable('jobs')) {
Schema::create('jobs', function (Blueprint $table) {
$table->bigIncrements('id');
$table->string('queue')->index();
$table->longText('payload');
$table->unsignedTinyInteger('attempts');
$table->unsignedInteger('reserved_at')->nullable();
$table->unsignedInteger('available_at');
$table->unsignedInteger('created_at');
});
}

if (! Schema::hasTable('failed_jobs')) {
Schema::create('failed_jobs', function (Blueprint $table) {
$table->uuid('uuid');
$table->bigIncrements('id');
$table->text('connection');
$table->text('queue');
$table->longText('payload');
$table->longText('exception');
$table->timestamp('failed_at')->useCurrent();
});
}
$user = UserFactory::new()->create();

$this->app->get(Dispatcher::class)->dispatch(
new MockedDeleteUserJob($user)
);

$this->artisan('queue:work', [
'connection' => 'database',
'--once' => true,
])->run();

$entry = $this->loadTelescopeEntries()->first();

$this->assertSame(EntryType::JOB, $entry->type);
$this->assertSame('processed', $entry->content['status']);
$this->assertSame('database', $entry->content['connection']);
$this->assertSame(MockedDeleteUserJob::class, $entry->content['name']);
$this->assertSame('default', $entry->content['queue']);

$this->assertSame(sprintf('%s:%s', get_class($user), $user->getKey()), $entry->content['data']['user']);
}
}

Expand All @@ -177,6 +170,27 @@ public function handle()
}
}

class MockedDeleteUserJob implements ShouldQueue
{
use SerializesModels;

public $connection = 'database';

public $deleteWhenMissingModels = true;

public $user;

public function __construct(User $user)
{
$this->user = $user;
}

public function handle()
{
$this->user->delete();
}
}

class MyDatabaseJob implements ShouldQueue
{
public $connection = 'database';
Expand Down

0 comments on commit 749369e

Please sign in to comment.