# Job Dashboard - Real-time Queue & Scheduler Monitoring
Umfassendes Dashboard-System für Echtzeit-Überwachung von Background Jobs, Worker Health und Scheduler-Tasks mit composable LiveComponents.
## Übersicht
Das Job Dashboard bietet:
- **Real-time Queue Statistics** - Aktuelle Queue-Metriken mit 5s Polling
- **Worker Health Monitoring** - Live Worker-Status und Gesundheitsüberwachung
- **Failed Jobs Management** - Interaktive Verwaltung fehlgeschlagener Jobs mit Retry/Delete
- **Scheduler Timeline** - Visualisierung anstehender Tasks mit Next-Execution-Vorhersage
**Route**: `/admin/jobs/dashboard`
## Architektur
### Composable Components Pattern
Das Dashboard verwendet **4 unabhängige LiveComponents** statt eines monolithischen Components:
```
JobDashboardController
├── QueueStatsComponent (5s polling)
│ └── QueueStatsState
├── WorkerHealthComponent (5s polling)
│ └── WorkerHealthState
├── FailedJobsListComponent (10s polling)
│ └── FailedJobsState
└── SchedulerTimelineComponent (30s polling)
└── SchedulerState
```
**Vorteile**:
- Wiederverwendbarkeit über verschiedene Dashboards hinweg
- Granulare Polling-Intervalle pro Component
- Bessere Performance durch kleinere Payloads
- Einfacheres Testing
- SOLID-Principles (Single Responsibility)
## Components
### 1. QueueStatsComponent
**Purpose**: Echtzeit-Überwachung der Queue-Performance
**Polling Interval**: 5000ms (5 Sekunden)
**Data**:
- `currentQueueSize`: Aktuelle Anzahl Jobs in Queue
- `totalJobs`: Gesamt-Jobs (letzte Stunde)
- `successfulJobs`: Erfolgreich abgeschlossene Jobs
- `failedJobs`: Fehlgeschlagene Jobs
- `successRate`: Erfolgsrate in Prozent
- `avgExecutionTimeMs`: Durchschnittliche Ausführungszeit in Millisekunden
**Services**:
- `Queue` - Für aktuelle Queue-Größe
- `JobMetricsManagerInterface` - Für aggregierte Metriken
**Template Features**:
- 6 Statistik-Karten mit Gradient-Backgrounds
- Icon-basierte Visualisierung
- Farbcodierung (Primary, Success, Danger, Info)
- Auto-Update Indicator im Footer
**Usage**:
```php
$queueStats = new QueueStatsComponent(
id: ComponentId::create('queue-stats', 'main'),
state: QueueStatsState::empty(),
queue: $this->queue,
metricsManager: $this->metricsManager
);
// In template
{liveComponent.queueStats}
```
### 2. WorkerHealthComponent
**Purpose**: Überwachung der Worker-Gesundheit und -Auslastung
**Polling Interval**: 5000ms (5 Sekunden)
**Data**:
- `activeWorkers`: Anzahl aktiver Worker
- `totalWorkers`: Gesamt-Worker
- `jobsInProgress`: Aktuell laufende Jobs
- `workerDetails`: Array mit Worker-Informationen
- `hostname`: Server-Name
- `process_id`: Prozess-ID
- `healthy`: Health-Status (true/false)
- `jobs`: Aktuelle Job-Anzahl
- `max_jobs`: Maximale Kapazität
- `cpu_usage`: CPU-Auslastung in Prozent
- `memory_usage_mb`: Speicherverbrauch in MB
- `last_heartbeat`: Zeitpunkt letzter Heartbeat
**Health Detection Logic**:
```php
private function isWorkerHealthy(Worker $worker): bool
{
$lastHeartbeat = $worker->lastHeartbeat;
$heartbeatAge = Timestamp::now()->diff($lastHeartbeat);
// Heartbeat muss innerhalb der letzten 2 Minuten sein
if ($heartbeatAge->i >= 2) {
return false;
}
// CPU-Auslastung darf 95% nicht überschreiten
if ($worker->cpuUsage >= 95.0) {
return false;
}
return true;
}
```
**Template Features**:
- Worker-Karten mit Health-Badges (✓ Healthy / ⚠ Unhealthy)
- Detaillierte Metriken: Jobs, CPU, Memory, Heartbeat
- Responsive Grid-Layout
- Empty State für keine Worker
**Usage**:
```php
$workerHealth = new WorkerHealthComponent(
id: ComponentId::create('worker-health', 'main'),
state: WorkerHealthState::empty(),
workerRegistry: $this->workerRegistry
);
```
### 3. FailedJobsListComponent
**Purpose**: Interaktive Verwaltung fehlgeschlagener Jobs
**Polling Interval**: 10000ms (10 Sekunden)
**Data**:
- `totalFailedJobs`: Gesamt-Anzahl fehlgeschlagener Jobs
- `failedJobs`: Array mit Job-Details
- `id`: Job-ID
- `queue`: Queue-Name
- `job_type`: Job-Klasse
- `error`: Fehlermeldung
- `payload_preview`: Gekürzte Payload-Vorschau (max 100 chars)
- `failed_at`: Zeitpunkt des Fehlers
- `attempts`: Anzahl Wiederholungsversuche
- `statistics`: Zusätzliche Statistiken
**Actions**:
**Retry Job**:
```php
#[Action]
public function retryJob(
string $jobId,
?ComponentEventDispatcher $events = null
): FailedJobsState {
$success = $this->deadLetterManager->retryJob($jobId);
if ($success && $events) {
$events->dispatch('failed-jobs:retry-success', ['jobId' => $jobId]);
}
return $this->poll(); // Refresh state
}
```
**Delete Job**:
```php
#[Action]
public function deleteJob(
string $jobId,
?ComponentEventDispatcher $events = null
): FailedJobsState {
$success = $this->deadLetterManager->deleteJob($jobId);
if ($success && $events) {
$events->dispatch('failed-jobs:delete-success', ['jobId' => $jobId]);
}
return $this->poll();
}
```
**Template Features**:
- Interaktive Tabelle mit Action-Buttons
- Retry-Button (🔄) für erneute Ausführung
- Delete-Button (🗑️) für permanentes Entfernen
- Hover-Effekte und Transitions
- Empty State ("✨ No failed jobs - everything running smoothly!")
**Frontend Integration**:
```html
```
### 4. SchedulerTimelineComponent
**Purpose**: Visualisierung anstehender Scheduled Tasks
**Polling Interval**: 30000ms (30 Sekunden)
**Data**:
- `totalScheduledTasks`: Gesamt-Anzahl geplanter Tasks
- `dueTasks`: Tasks, die jetzt fällig sind
- `upcomingTasks`: Nächste 10 anstehende Tasks
- `id`: Task-ID
- `schedule_type`: Typ (cron, interval, onetime)
- `next_run`: Geplante Ausführungszeit (absolute)
- `next_run_relative`: Relative Zeitangabe (z.B. "5 hours, 30 min")
- `is_due`: Boolean ob Task fällig ist
- `nextExecution`: Zeitpunkt der nächsten Ausführung (global)
- `statistics`: Ausführungsstatistiken
**Time Formatting Logic**:
```php
private function formatTimeUntil(Timestamp $now, Timestamp $nextRun): string
{
$diff = $now->diff($nextRun);
// Weniger als 1 Minute
if ($diff->days === 0 && $diff->h === 0 && $diff->i === 0) {
return 'Less than 1 minute';
}
// Tage und Stunden
if ($diff->days > 0) {
$hours = $diff->h;
return "{$diff->days} days, {$hours} hours";
}
// Nur Stunden und Minuten
if ($diff->h > 0) {
return "{$diff->h} hours, {$diff->i} min";
}
// Nur Minuten
return "{$diff->i} min";
}
```
**Schedule Type Detection**:
```php
private function getScheduleType($schedule): string
{
return match (true) {
$schedule instanceof CronSchedule => 'cron',
$schedule instanceof IntervalSchedule => 'interval',
$schedule instanceof OneTimeSchedule => 'onetime',
default => 'manual'
};
}
```
**Template Features**:
- Summary-Header mit Total Tasks, Due Tasks, Next Execution
- Timeline-Visualisierung mit Timeline-Items
- Due-Badge mit Pulse-Animation für fällige Tasks
- Relative Zeitangaben ("in 5 hours, 30 min")
- Schedule-Type-Badges (CRON, INTERVAL, ONETIME)
- Empty State ("📅 No scheduled tasks")
## Dashboard Controller
**File**: `src/Application/Admin/JobDashboardController.php`
**Route**: `#[Route(path: '/admin/jobs/dashboard', method: Method::GET)]`
**Implementation**:
```php
final readonly class JobDashboardController
{
public function __construct(
private Queue $queue,
private JobMetricsManagerInterface $metricsManager,
private WorkerRegistry $workerRegistry,
private DeadLetterManager $deadLetterManager,
private SchedulerService $scheduler
) {}
#[Route(path: '/admin/jobs/dashboard', method: Method::GET)]
public function dashboard(): ViewResult
{
// Queue Statistics Component
$queueStats = new QueueStatsComponent(
id: ComponentId::create('queue-stats', 'main'),
state: QueueStatsState::empty(),
queue: $this->queue,
metricsManager: $this->metricsManager
);
// Worker Health Component
$workerHealth = new WorkerHealthComponent(
id: ComponentId::create('worker-health', 'main'),
state: WorkerHealthState::empty(),
workerRegistry: $this->workerRegistry
);
// Failed Jobs Component
$failedJobs = new FailedJobsListComponent(
id: ComponentId::create('failed-jobs', 'main'),
state: FailedJobsState::empty(),
deadLetterManager: $this->deadLetterManager
);
// Scheduler Timeline Component
$schedulerTimeline = new SchedulerTimelineComponent(
id: ComponentId::create('scheduler-timeline', 'main'),
state: SchedulerState::empty(),
scheduler: $this->scheduler
);
return new ViewResult(
template: 'admin/job-dashboard',
data: [
'queueStats' => $queueStats,
'workerHealth' => $workerHealth,
'failedJobs' => $failedJobs,
'schedulerTimeline' => $schedulerTimeline,
]
);
}
}
```
## Template Structure
**Main Dashboard Template**: `src/Application/Admin/templates/job-dashboard.view.php`
```html
Real-time monitoring of queue, workers, and scheduler