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, ], ]; } }