108 lines
5.0 KiB
PHP
108 lines
5.0 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace App\Console\Commands\Enhance;
|
|
|
|
use App\Models\EnhanceJob;
|
|
use Illuminate\Console\Command;
|
|
|
|
final class EnhanceHealthCommand extends Command
|
|
{
|
|
protected $signature = 'enhance:health {--json : Output machine-readable JSON}';
|
|
|
|
protected $description = 'Report operational health and lifecycle metrics for Enhance jobs.';
|
|
|
|
public function handle(): int
|
|
{
|
|
$payload = $this->payload();
|
|
|
|
if ((bool) $this->option('json')) {
|
|
$this->line(json_encode($payload, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES));
|
|
|
|
return self::SUCCESS;
|
|
}
|
|
|
|
$this->info('Enhance health');
|
|
$this->newLine();
|
|
|
|
$this->table(['Metric', 'Value'], [
|
|
['Configured engine', $payload['engine']],
|
|
['Configured queue', $payload['queue']],
|
|
['Worker URL configured', $payload['worker_configured'] ? 'yes' : 'no'],
|
|
['Storage disk', $payload['storage_disk']],
|
|
['Total jobs', $payload['counts']['total']],
|
|
['Pending jobs', $payload['counts']['pending']],
|
|
['Queued jobs', $payload['counts']['queued']],
|
|
['Processing jobs', $payload['counts']['processing']],
|
|
['Completed jobs', $payload['counts']['completed']],
|
|
['Failed jobs', $payload['counts']['failed']],
|
|
['Cancelled jobs', $payload['counts']['cancelled']],
|
|
['Expired jobs', $payload['counts']['expired']],
|
|
['Stuck queued jobs', $payload['health']['stuck_queued']],
|
|
['Stuck processing jobs', $payload['health']['stuck_processing']],
|
|
['Jobs created today', $payload['today']['created']],
|
|
['Jobs completed today', $payload['today']['completed']],
|
|
['Jobs failed today', $payload['today']['failed']],
|
|
['Average processing time today', $payload['today']['average_processing_seconds'] ?? '—'],
|
|
]);
|
|
|
|
return self::SUCCESS;
|
|
}
|
|
|
|
private function payload(): array
|
|
{
|
|
$todayStart = now()->startOfDay();
|
|
$todayEnd = now()->endOfDay();
|
|
$stuckQueuedCutoff = now()->subMinutes((int) config('enhance.health.stuck_queued_after_minutes', 60));
|
|
$stuckProcessingCutoff = now()->subMinutes((int) config('enhance.health.stuck_processing_after_minutes', 30));
|
|
|
|
$counts = [
|
|
'total' => EnhanceJob::query()->count(),
|
|
'pending' => EnhanceJob::query()->where('status', EnhanceJob::STATUS_PENDING)->count(),
|
|
'queued' => EnhanceJob::query()->where('status', EnhanceJob::STATUS_QUEUED)->count(),
|
|
'processing' => EnhanceJob::query()->where('status', EnhanceJob::STATUS_PROCESSING)->count(),
|
|
'completed' => EnhanceJob::query()->where('status', EnhanceJob::STATUS_COMPLETED)->count(),
|
|
'failed' => EnhanceJob::query()->where('status', EnhanceJob::STATUS_FAILED)->count(),
|
|
'cancelled' => EnhanceJob::query()->where('status', EnhanceJob::STATUS_CANCELLED)->count(),
|
|
'expired' => EnhanceJob::query()->where('status', EnhanceJob::STATUS_EXPIRED)->count(),
|
|
];
|
|
|
|
return [
|
|
'engine' => (string) config('enhance.default_engine', EnhanceJob::ENGINE_STUB),
|
|
'queue' => (string) config('enhance.queue', 'default'),
|
|
'worker_configured' => trim((string) config('enhance.external_worker.url', '')) !== '',
|
|
'storage_disk' => (string) config('enhance.disk', 'public'),
|
|
'counts' => $counts,
|
|
'health' => [
|
|
'stuck_queued' => EnhanceJob::query()
|
|
->where('status', EnhanceJob::STATUS_QUEUED)
|
|
->whereNotNull('queued_at')
|
|
->where('queued_at', '<=', $stuckQueuedCutoff)
|
|
->count(),
|
|
'stuck_processing' => EnhanceJob::query()
|
|
->where('status', EnhanceJob::STATUS_PROCESSING)
|
|
->whereNotNull('started_at')
|
|
->where('started_at', '<=', $stuckProcessingCutoff)
|
|
->count(),
|
|
],
|
|
'today' => [
|
|
'created' => EnhanceJob::query()->whereBetween('created_at', [$todayStart, $todayEnd])->count(),
|
|
'completed' => EnhanceJob::query()
|
|
->where('status', EnhanceJob::STATUS_COMPLETED)
|
|
->whereBetween('finished_at', [$todayStart, $todayEnd])
|
|
->count(),
|
|
'failed' => EnhanceJob::query()
|
|
->where('status', EnhanceJob::STATUS_FAILED)
|
|
->whereBetween('finished_at', [$todayStart, $todayEnd])
|
|
->count(),
|
|
'average_processing_seconds' => ($average = EnhanceJob::query()
|
|
->whereNotNull('processing_seconds')
|
|
->whereBetween('finished_at', [$todayStart, $todayEnd])
|
|
->avg('processing_seconds')) !== null
|
|
? round((float) $average, 2)
|
|
: null,
|
|
],
|
|
];
|
|
}
|
|
} |