Skip to content

Commit

Permalink
Capture job dispatch duration (#106)
Browse files Browse the repository at this point in the history
  • Loading branch information
avosalmon authored Feb 18, 2025
1 parent f1c8b9e commit fdbe72f
Show file tree
Hide file tree
Showing 8 changed files with 44 additions and 23 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
namespace Laravel\Nightwatch\Hooks;

use Illuminate\Queue\Events\JobQueued;
use Illuminate\Queue\Events\JobQueueing;
use Laravel\Nightwatch\Core;
use Laravel\Nightwatch\State\CommandState;
use Laravel\Nightwatch\State\RequestState;
Expand All @@ -11,7 +12,7 @@
/**
* @internal
*/
final class JobQueuedListener
final class QueuedJobListener
{
/**
* @param Core<RequestState|CommandState> $nightwatch
Expand All @@ -22,7 +23,7 @@ public function __construct(
//
}

public function __invoke(JobQueued $event): void
public function __invoke(JobQueueing|JobQueued $event): void
{
try {
$this->nightwatch->sensor->queuedJob($event);
Expand Down
5 changes: 3 additions & 2 deletions src/NightwatchServiceProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
use Illuminate\Notifications\Events\NotificationSending;
use Illuminate\Notifications\Events\NotificationSent;
use Illuminate\Queue\Events\JobQueued;
use Illuminate\Queue\Events\JobQueueing;
use Illuminate\Routing\Events\PreparingResponse;
use Illuminate\Routing\Events\ResponsePrepared;
use Illuminate\Routing\Events\RouteMatched;
Expand All @@ -54,12 +55,12 @@
use Laravel\Nightwatch\Hooks\ExceptionHandlerResolvedHandler;
use Laravel\Nightwatch\Hooks\HttpClientFactoryResolvedHandler;
use Laravel\Nightwatch\Hooks\HttpKernelResolvedHandler;
use Laravel\Nightwatch\Hooks\JobQueuedListener;
use Laravel\Nightwatch\Hooks\LogoutListener;
use Laravel\Nightwatch\Hooks\MailListener;
use Laravel\Nightwatch\Hooks\NotificationListener;
use Laravel\Nightwatch\Hooks\PreparingResponseListener;
use Laravel\Nightwatch\Hooks\QueryExecutedListener;
use Laravel\Nightwatch\Hooks\QueuedJobListener;
use Laravel\Nightwatch\Hooks\RequestBootedHandler;
use Laravel\Nightwatch\Hooks\RequestHandledListener;
use Laravel\Nightwatch\Hooks\ResponsePreparedListener;
Expand Down Expand Up @@ -264,7 +265,7 @@ private function registerHooks(): void
/**
* @see \Laravel\Nightwatch\Records\QueuedJob
*/
$events->listen(JobQueued::class, (new JobQueuedListener($core))(...));
$events->listen([JobQueueing::class, JobQueued::class], (new QueuedJobListener($core))(...));

/**
* @see \Laravel\Nightwatch\Records\Notification
Expand Down
3 changes: 2 additions & 1 deletion src/SensorManager.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
use Illuminate\Notifications\Events\NotificationSent;
use Illuminate\Queue\Events\JobAttempted;
use Illuminate\Queue\Events\JobQueued;
use Illuminate\Queue\Events\JobQueueing;
use Laravel\Nightwatch\Sensors\CacheEventSensor;
use Laravel\Nightwatch\Sensors\CommandSensor;
use Laravel\Nightwatch\Sensors\ExceptionSensor;
Expand Down Expand Up @@ -179,7 +180,7 @@ public function log(LogRecord $record): void
$sensor($record);
}

public function queuedJob(JobQueued $event): void
public function queuedJob(JobQueueing|JobQueued $event): void
{
$sensor = $this->queuedJobSensor ??= new QueuedJobSensor(
executionState: $this->executionState,
Expand Down
6 changes: 1 addition & 5 deletions src/Sensors/MailSensor.php
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,6 @@ final class MailSensor
{
private ?float $startTime = null;

private ?int $duration = null;

public function __construct(
private RequestState|CommandState $executionState,
private Clock $clock,
Expand All @@ -40,7 +38,6 @@ public function __invoke(MessageSending|MessageSent $event): void

if ($event instanceof MessageSending) {
$this->startTime = $now;
$this->duration = null;

return;
}
Expand All @@ -51,7 +48,6 @@ public function __invoke(MessageSending|MessageSent $event): void
throw new RuntimeException("No start time found for [{$class}].");
}

$this->duration = (int) round(($now - $this->startTime) * 1_000_000);
$this->executionState->mail++;

$this->executionState->records->write(new Mail(
Expand All @@ -71,7 +67,7 @@ class: $class,
cc: count($event->message->getCc()),
bcc: count($event->message->getBcc()),
attachments: count($event->message->getAttachments()),
duration: $this->duration,
duration: (int) round(($now - $this->startTime) * 1_000_000),
failed: false, // TODO: The framework doesn't dispatch a failed event.
));
}
Expand Down
6 changes: 1 addition & 5 deletions src/Sensors/NotificationSensor.php
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,6 @@ final class NotificationSensor
{
private ?float $startTime = null;

private ?int $duration = null;

public function __construct(
private RequestState|CommandState $executionState,
private Clock $clock,
Expand All @@ -37,7 +35,6 @@ public function __invoke(NotificationSending|NotificationSent $event): void

if ($event instanceof NotificationSending) {
$this->startTime = $now;
$this->duration = null;

return;
}
Expand All @@ -52,7 +49,6 @@ public function __invoke(NotificationSending|NotificationSent $event): void
$class = $event->notification::class;
}

$this->duration = (int) round(($now - $this->startTime) * 1_000_000);
$this->executionState->notifications++;

$this->executionState->records->write(new Notification(
Expand All @@ -67,7 +63,7 @@ public function __invoke(NotificationSending|NotificationSent $event): void
user: $this->executionState->user->id(),
channel: $event->channel,
class: $class,
duration: $this->duration,
duration: (int) round(($now - $this->startTime) * 1_000_000),
failed: false, // TODO: The framework doesn't dispatch the `NotificationFailed` event.
));
}
Expand Down
24 changes: 20 additions & 4 deletions src/Sensors/QueuedJobSensor.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,18 +4,21 @@

use Illuminate\Events\CallQueuedListener;
use Illuminate\Queue\Events\JobQueued;
use Illuminate\Queue\Events\JobQueueing;
use Laravel\Nightwatch\Clock;
use Laravel\Nightwatch\Concerns\NormalizesQueue;
use Laravel\Nightwatch\Records\QueuedJob;
use Laravel\Nightwatch\State\CommandState;
use Laravel\Nightwatch\State\RequestState;
use ReflectionClass;
use RuntimeException;

use function hash;
use function is_object;
use function is_string;
use function method_exists;
use function property_exists;
use function round;

/**
* @internal
Expand All @@ -24,6 +27,8 @@ final class QueuedJobSensor
{
use NormalizesQueue;

private ?float $startTime = null;

/**
* @param array<string, array{ queue?: string, driver?: string, prefix?: string, suffix?: string }> $connectionConfig
*/
Expand All @@ -35,19 +40,30 @@ public function __construct(
//
}

public function __invoke(JobQueued $event): void
public function __invoke(JobQueueing|JobQueued $event): void
{
$nowMicrotime = $this->clock->microtime();
$now = $this->clock->microtime();

if ($event instanceof JobQueueing) {
$this->startTime = $now;

return;
}

$name = match (true) {
is_string($event->job) => $event->job,
method_exists($event->job, 'displayName') => $event->job->displayName(),
default => $event->job::class,
};

if ($this->startTime === null) {
throw new RuntimeException("No start time found for [{$name}].");
}

$this->executionState->jobsQueued++;

$this->executionState->records->write(new QueuedJob(
timestamp: $nowMicrotime,
timestamp: $now,
deploy: $this->executionState->deploy,
server: $this->executionState->server,
_group: hash('md5', $name),
Expand All @@ -60,7 +76,7 @@ public function __invoke(JobQueued $event): void
name: $name,
connection: $event->connectionName,
queue: $this->normalizeQueue($event->connectionName, $this->resolveQueue($event)),
duration: 0, // TODO
duration: (int) round(($now - $this->startTime) * 1_000_000)
));
}

Expand Down
11 changes: 10 additions & 1 deletion tests/Feature/Sensors/QueuedJobSensorTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Foundation\Testing\RefreshDatabaseState;
use Illuminate\Queue\Events\JobQueued;
use Illuminate\Queue\Events\JobQueueing;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
use Illuminate\Support\Facades\Bus;
Expand Down Expand Up @@ -74,7 +75,7 @@
'name' => 'MyJob',
'connection' => 'database',
'queue' => 'default',
'duration' => 0,
'duration' => 5200,
],
]);
});
Expand Down Expand Up @@ -184,6 +185,14 @@
'suffix' => '-production',
]);

nightwatch()->sensor->queuedJob(new JobQueueing(
connectionName: 'my-sqs-queue',
queue: 'https://sqs.us-east-1.amazonaws.com/your-account-id/queue-name-production',
job: 'MyJobClass',
payload: '{"uuid":"00000000-0000-0000-0000-000000000000"}',
delay: 0,
));

nightwatch()->sensor->queuedJob(new JobQueued(
connectionName: 'my-sqs-queue',
queue: 'https://sqs.us-east-1.amazonaws.com/your-account-id/queue-name-production',
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
<?php

use Illuminate\Queue\Events\JobQueued;
use Laravel\Nightwatch\Hooks\JobQueuedListener;
use Illuminate\Queue\Events\JobQueueing;
use Laravel\Nightwatch\Hooks\QueuedJobListener;
use Laravel\Nightwatch\SensorManager;

it('gracefully handles exceptions', function () {
Expand All @@ -11,14 +12,14 @@

public function __construct() {}

public function queuedJob(JobQueued $event): void
public function queuedJob(JobQueueing|JobQueued $event): void
{
$this->thrown = true;

throw new RuntimeException('Whoops!');
}
});
$handler = new JobQueuedListener($nightwatch);
$handler = new QueuedJobListener($nightwatch);

$handler(new JobQueued('redis', 'default', '1', fn () => null, '{}', 0));

Expand Down

0 comments on commit fdbe72f

Please sign in to comment.