Skip to content

Commit b200cd7

Browse files
committed
wip
1 parent 71a125f commit b200cd7

File tree

7 files changed

+111
-47
lines changed

7 files changed

+111
-47
lines changed

database/migrations/2023_06_07_000001_create_pulse_tables.php

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -68,13 +68,14 @@ public function up(): void
6868
$table->string('user_id')->nullable();
6969
$table->string('job');
7070
$table->uuid('job_uuid');
71+
$table->unsignedInteger('attempt')->nullable();
7172
$table->string('connection');
7273
$table->string('queue');
7374
$table->datetime('processing_at')->nullable();
75+
$table->datetime('released_at')->nullable();
7476
$table->datetime('processed_at')->nullable();
7577
$table->datetime('failed_at')->nullable();
76-
$table->unsignedInteger('slow')->default(0);
77-
$table->unsignedInteger('slowest')->nullable();
78+
$table->unsignedInteger('duration')->nullable();
7879

7980
// TODO: verify this update index. Needs to find job quickly.
8081
$table->index(['job_uuid']);

dist/pulse.css

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

resources/views/livewire/queues.blade.php

Lines changed: 39 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -10,15 +10,23 @@
1010
<x-slot:actions>
1111
<div class="flex gap-4">
1212
<div class="flex items-center gap-2 text-sm text-gray-700 dark:text-gray-300 font-medium">
13-
<div class="h-0.5 w-5 rounded-full bg-[rgba(147,51,234,0.5)]"></div>
13+
<div class="h-0.5 w-4 rounded-full bg-[rgba(107,114,128,0.5)]"></div>
1414
Queued
1515
</div>
1616
<div class="flex items-center gap-2 text-sm text-gray-700 dark:text-gray-300 font-medium">
17-
<div class="h-0.5 w-5 rounded-full bg-[#9333ea]"></div>
17+
<div class="h-0.5 w-4 rounded-full bg-[rgba(147,51,234,0.5)]"></div>
18+
Processing
19+
</div>
20+
<div class="flex items-center gap-2 text-sm text-gray-700 dark:text-gray-300 font-medium">
21+
<div class="h-0.5 w-4 rounded-full bg-[#eab308]"></div>
22+
Released
23+
</div>
24+
<div class="flex items-center gap-2 text-sm text-gray-700 dark:text-gray-300 font-medium">
25+
<div class="h-0.5 w-4 rounded-full bg-[#9333ea]"></div>
1826
Processed
1927
</div>
2028
<div class="flex items-center gap-2 text-sm text-gray-700 dark:text-gray-300 font-medium">
21-
<div class="h-0.5 w-5 rounded-full bg-[#e11d48]"></div>
29+
<div class="h-0.5 w-4 rounded-full bg-[#e11d48]"></div>
2230
Failed
2331
</div>
2432
</div>
@@ -59,6 +67,8 @@ class="min-h-full flex flex-col"
5967
@php
6068
$highest = $readings->map(fn ($reading) => max(
6169
$reading->queued,
70+
$reading->processing,
71+
$reading->released,
6272
$reading->processed,
6373
$reading->failed,
6474
))->max()
@@ -69,7 +79,7 @@ class="min-h-full flex flex-col"
6979

7080
<div
7181
wire:ignore
72-
class="h-12"
82+
class="h-14"
7383
x-data="{
7484
init() {
7585
let chart = new Chart(
@@ -81,14 +91,34 @@ class="h-12"
8191
datasets: [
8292
{
8393
label: 'Queued',
84-
borderColor: 'rgba(147,51,234,0.5)',
94+
borderColor: 'rgba(107,114,128,0.5)',
8595
borderWidth: 2,
8696
borderCapStyle: 'round',
8797
data: @js(collect($readings)->pluck('queued')),
8898
pointStyle: false,
8999
tension: 0.2,
90100
spanGaps: false,
91101
},
102+
{
103+
label: 'Processing',
104+
borderColor: 'rgba(147,51,234,0.5)',
105+
borderWidth: 2,
106+
borderCapStyle: 'round',
107+
data: @js(collect($readings)->pluck('processing')),
108+
pointStyle: false,
109+
tension: 0.2,
110+
spanGaps: false,
111+
},
112+
{
113+
label: 'Released',
114+
borderColor: '#eab308',
115+
borderWidth: 2,
116+
borderCapStyle: 'round',
117+
data: @js(collect($readings)->pluck('released')),
118+
pointStyle: false,
119+
tension: 0.2,
120+
spanGaps: false,
121+
},
92122
{
93123
label: 'Processed',
94124
borderColor: '#9333ea',
@@ -160,8 +190,10 @@ class="h-12"
160190
161191
chart.data.labels = queues['{{ $queue }}'].map(reading => reading.date)
162192
chart.data.datasets[0].data = queues['{{ $queue }}'].map(reading => reading.queued)
163-
chart.data.datasets[1].data = queues['{{ $queue }}'].map(reading => reading.processed)
164-
chart.data.datasets[2].data = queues['{{ $queue }}'].map(reading => reading.failed)
193+
chart.data.datasets[1].data = queues['{{ $queue }}'].map(reading => reading.processing)
194+
chart.data.datasets[2].data = queues['{{ $queue }}'].map(reading => reading.released)
195+
chart.data.datasets[3].data = queues['{{ $queue }}'].map(reading => reading.processed)
196+
chart.data.datasets[4].data = queues['{{ $queue }}'].map(reading => reading.failed)
165197
chart.update()
166198
})
167199
}

src/Pulse.php

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -137,10 +137,16 @@ public function register(string|array $recorders): self
137137
/**
138138
* Record the given entry.
139139
*/
140-
public function record(Entry|Update $entry): self
140+
public function record(Entry|Update|array $entries): self
141141
{
142+
if (! is_array($entries)) {
143+
$entries = [$entries];
144+
}
145+
142146
if ($this->shouldRecord) {
143-
$this->entries[] = $entry;
147+
foreach ($entries as $entry) {
148+
$this->entries[] = $entry;
149+
}
144150
}
145151

146152
return $this;

src/Queries/Queues.php

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -50,8 +50,9 @@ public function __invoke(Interval $interval): Collection
5050
'date' => $currentBucket->subSeconds($i * $secondsPerPeriod)->format('Y-m-d H:i'),
5151
'queued' => 0,
5252
'processing' => 0,
53-
'failed' => 0,
53+
'released' => 0,
5454
'processed' => 0,
55+
'failed' => 0,
5556
])
5657
->reverse()
5758
->keyBy('date');
@@ -60,6 +61,7 @@ public function __invoke(Interval $interval): Collection
6061
->select('bucket', 'connection', 'queue')
6162
->selectRaw('COUNT(`queued_at`) AS `queued`')
6263
->selectRaw('COUNT(`processing_at`) AS `processing`')
64+
->selectRaw('COUNT(`released_at`) AS `released`')
6365
->selectRaw('COUNT(`processed_at`) AS `processed`')
6466
->selectRaw('COUNT(`failed_at`) AS `failed`')
6567
->fromSub(
@@ -69,6 +71,7 @@ public function __invoke(Interval $interval): Collection
6971
->select('connection', 'queue')
7072
->selectRaw('`date` AS `queued_at`')
7173
->selectRaw('NULL AS `processing_at`')
74+
->selectRaw('NULL AS `released_at`')
7275
->selectRaw('NULL AS `processed_at`')
7376
->selectRaw('NULL AS `failed_at`')
7477
// Divide the data into buckets.
@@ -81,19 +84,35 @@ public function __invoke(Interval $interval): Collection
8184
->select('connection', 'queue')
8285
->selectRaw('NULL AS `queued_at`')
8386
->addSelect('processing_at')
87+
->selectRaw('NULL AS `released_at`')
8488
->selectRaw('NULL AS `processed_at`')
8589
->selectRaw('NULL AS `failed_at`')
8690
// Divide the data into buckets.
8791
->selectRaw('FLOOR(UNIX_TIMESTAMP(CONVERT_TZ(`processing_at`, ?, @@session.time_zone)) / ?) AS `bucket`', [$now->format('P'), $secondsPerPeriod])
8892
->where('processing_at', '>=', $now->ceilSeconds($interval->totalSeconds / $maxDataPoints)->subSeconds((int) $interval->totalSeconds))
8993
->whereNotNull('processing_at')
9094
)
95+
// Released
96+
->union(fn (Builder $query) => $query
97+
->from('pulse_jobs')
98+
->select('connection', 'queue')
99+
->selectRaw('NULL AS `queued_at`')
100+
->selectRaw('NULL AS `processing_at`')
101+
->addSelect('released_at')
102+
->selectRaw('NULL AS `processed_at`')
103+
->selectRaw('NULL AS `failed_at`')
104+
// Divide the data into buckets.
105+
->selectRaw('FLOOR(UNIX_TIMESTAMP(CONVERT_TZ(`released_at`, ?, @@session.time_zone)) / ?) AS `bucket`', [$now->format('P'), $secondsPerPeriod])
106+
->where('released_at', '>=', $now->ceilSeconds($interval->totalSeconds / $maxDataPoints)->subSeconds((int) $interval->totalSeconds))
107+
->whereNotNull('released_at')
108+
)
91109
// Processed
92110
->union(fn (Builder $query) => $query
93111
->from('pulse_jobs')
94112
->select('connection', 'queue')
95113
->selectRaw('NULL AS `queued_at`')
96114
->selectRaw('NULL AS `processing_at`')
115+
->selectRaw('NULL AS `released_at`')
97116
->addSelect('processed_at')
98117
->selectRaw('NULL AS `failed_at`')
99118
// Divide the data into buckets.
@@ -107,6 +126,7 @@ public function __invoke(Interval $interval): Collection
107126
->select('connection', 'queue')
108127
->selectRaw('NULL AS `queued_at`')
109128
->selectRaw('NULL AS `processing_at`')
129+
->selectRaw('NULL AS `released_at`')
110130
->selectRaw('NULL AS `processed_at`')
111131
->addSelect('failed_at')
112132
// Divide the data into buckets.
@@ -130,7 +150,8 @@ public function __invoke(Interval $interval): Collection
130150
return [$date => (object) [
131151
'date' => $date,
132152
'queued' => $reading->queued,
133-
'processing' => $reading->queued,
153+
'processing' => $reading->processing,
154+
'released' => $reading->released,
134155
'processed' => $reading->processed,
135156
'failed' => $reading->failed,
136157
]];

src/Queries/SlowJobs.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,10 +35,10 @@ public function __invoke(Interval $interval): Collection
3535
$now = new CarbonImmutable;
3636

3737
return $this->connection()->table('pulse_jobs')
38-
->selectRaw('`job`, SUM(slow) as count, MAX(slowest) as slowest')
38+
->selectRaw('`job`, COUNT(*) AS count, MAX(duration) AS slowest')
3939
// TODO: processed_at or failed_at
4040
->where('date', '>=', $now->subSeconds((int) $interval->totalSeconds)->toDateTimeString())
41-
->where('slow', '>', 0)
41+
->where('duration', '>=', $this->config->get('pulse.slow_query_threshold'))
4242
->groupBy('job')
4343
->orderByDesc('slowest')
4444
->get();

src/Recorders/Jobs.php

Lines changed: 35 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ public function __construct(
5454
/**
5555
* Record the job.
5656
*/
57-
public function record(JobReleasedAfterException|JobFailed|JobProcessed|JobProcessing|JobQueued $event): Entry|Update|null
57+
public function record(JobReleasedAfterException|JobFailed|JobProcessed|JobProcessing|JobQueued $event): Entry|Update|array|null
5858
{
5959
if ($event->connectionName === 'sync') {
6060
return null;
@@ -65,63 +65,67 @@ public function record(JobReleasedAfterException|JobFailed|JobProcessed|JobProce
6565
if ($event instanceof JobQueued) {
6666
return new Entry($this->table, [
6767
'date' => $now->toDateTimeString(),
68-
'job' => is_string($event->job)
69-
? $event->job
70-
: $event->job::class,
68+
'job' => $event->job::class,
7169
'job_uuid' => $event->payload()['uuid'],
7270
'connection' => $event->connectionName,
7371
'queue' => $event->job->queue ?? 'default',
7472
'user_id' => $this->pulse->authenticatedUserIdResolver(),
7573
]);
7674
}
7775

78-
// TODO: Store an entry per-retry?
79-
8076
if ($event instanceof JobProcessing) {
8177
$this->lastJobStartedProcessingAt = $now;
82-
// TODO: Add update here?
8378

84-
return null;
85-
}
79+
// TODO: Allow this to be ingested immediately?
8680
87-
$duration = $this->lastJobStartedProcessingAt->diffInMilliseconds($now);
88-
$processingAt = $this->lastJobStartedProcessingAt?->toDateTimeString();
89-
$slow = $duration >= $this->config->get('pulse.slow_job_threshold') ? 1 : 0;
90-
91-
if ($event instanceof JobReleasedAfterException) {
92-
return tap(new Update(
81+
return new Update(
9382
$this->table,
94-
['job_uuid' => (string) $event->job->uuid()],
95-
fn (array $attributes) => [
96-
'processing_at' => $attributes['processing_at'] ?? $processingAt,
97-
'slowest' => max($attributes['slowest'] ?? 0, $duration),
98-
'slow' => $attributes['slow'] + $slow,
83+
['job_uuid' => (string) $event->job->uuid(), 'attempt' => null],
84+
[
85+
'attempt' => $event->job->attempts(),
86+
'processing_at' => $this->lastJobStartedProcessingAt->toDateTimeString(),
9987
],
100-
), fn () => $this->lastJobStartedProcessingAt = null);
88+
);
89+
}
90+
91+
if ($event instanceof JobReleasedAfterException) {
92+
return tap([
93+
new Update(
94+
$this->table,
95+
['job_uuid' => $event->job->uuid(), 'attempt' => $event->job->attempts()],
96+
[
97+
'released_at' => $now->toDateTimeString(),
98+
'duration' => $this->lastJobStartedProcessingAt->diffInMilliseconds($now),
99+
],
100+
),
101+
new Entry($this->table, [
102+
'date' => $now->toDateTimeString(),
103+
'job' => $event->job::class,
104+
'job_uuid' => $event->job->uuid(),
105+
'connection' => $event->connectionName,
106+
'queue' => $event->job->queue ?? 'default',
107+
]),
108+
], fn () => $this->lastJobStartedProcessingAt = null);
101109
}
102110

103111
if ($event instanceof JobProcessed) {
104112
return tap(new Update(
105113
$this->table,
106-
['job_uuid' => (string) $event->job->uuid()],
107-
fn (array $attributes) => [
108-
'processing_at' => $attributes['processing_at'] ?? $processingAt,
114+
['job_uuid' => (string) $event->job->uuid(), 'attempt' => $event->job->attempts()],
115+
[
109116
'processed_at' => $now->toDateTimeString(),
110-
'slowest' => max($attributes['slowest'] ?? 0, $duration),
111-
'slow' => $attributes['slow'] + $slow,
117+
'duration' => $this->lastJobStartedProcessingAt->diffInMilliseconds($now),
112118
],
113119
), fn () => $this->lastJobStartedProcessingAt = null);
114120
}
115121

116122
if ($event instanceof JobFailed) {
117123
return tap(new Update(
118124
$this->table,
119-
['job_uuid' => (string) $event->job->uuid()],
120-
fn (array $attributes) => [
121-
'processing_at' => $attributes['processing_at'] ?? $processingAt,
125+
['job_uuid' => (string) $event->job->uuid(), 'attempt' => $event->job->attempts()],
126+
[
122127
'failed_at' => $now->toDateTimeString(),
123-
'slowest' => max($attributes['slowest'] ?? 0, $duration),
124-
'slow' => $attributes['slow'] + $slow,
128+
'duration' => $this->lastJobStartedProcessingAt->diffInMilliseconds($now),
125129
],
126130
), fn () => $this->lastJobStartedProcessingAt = null);
127131
}

0 commit comments

Comments
 (0)