# Geplante Aufgaben

Letzte Aktualisierung: 2026-03-06

## Ziel

Scheduler-Betrieb und Job-Erweiterung kurz und verlässlich dokumentieren.

## Kernkomponenten

- Runner: `bin/console scheduler:run`
- Orchestrierung: `lib/Service/Scheduler/SchedulerRunService.php`
- Job-Konfiguration: `lib/Service/Scheduler/ScheduledJobService.php`
- Registry: `lib/Service/Scheduler/ScheduledJobRegistry.php`
- Schedule-Berechnung: `lib/Service/Scheduler/ScheduleCalculator.php`

## Datenmodell

- `scheduled_jobs`: Job-Konfiguration und letzter Laufstatus
- `scheduled_job_runs`: Historie einzelner Läufe
- `scheduler_runtime_status`: Heartbeat/Runner-Status (eine Zeile, `id = 1`)

Retention:

- Run-Logs: 90 Tage (`ScheduledJobService::purgeRunsExpired`)
- System-Audit: konfigurierbar über `system_audit_retention_days` (Job `system_audit_purge`)

## Laufmodell

1. Runner startet und synchronisiert Registry-Jobs (`ensureSystemJobs`).
2. Globaler MySQL-Lock wird gesetzt (`scheduled_jobs_runner`).
3. Fällige Jobs (`enabled=1`, `next_run_at <= now`) werden abgearbeitet.
4. Jeder Lauf wird als `success`/`failed`/`skipped` protokolliert.
5. `next_run_at` wird neu berechnet.
6. Heartbeat wird aktualisiert, Lock wird freigegeben.

## Scheduling

Unterstützt:

- `hourly` (1..24)
- `daily` (1..365 + Uhrzeit)
- `weekly` (1..52 + Uhrzeit + Wochentage)

`catchup_once` steuert Nachholverhalten:

- `1`: genau ein Nachhol-Lauf, danach normal weiter
- `0`: kein Backlog-Sturm, verpasste Fenster werden übersprungen

## Rechte

- `jobs.view`: Übersicht sehen
- `jobs.manage`: Job speichern
- `jobs.run_now`: Job manuell starten
- Purge der Run-Logs: `settings.update`

Registrierte Core-Jobs:

- `user_lifecycle_run`
- `system_audit_purge`

## Neuen Job registrieren (kurz)

1. Handler in `lib/Service/Scheduler/Handler/*` erstellen (`ScheduledJobHandlerInterface`).
2. Job-Key + Handler in `ScheduledJobRegistry` ergänzen.
3. Abhängigkeiten in `SchedulerServicesFactory` verdrahten.

Danach wird der Job bei `ensureSystemJobs()` automatisch in `scheduled_jobs` angelegt.

## Troubleshooting

- `lock_not_acquired`: ein anderer Runner läuft.
- Job bleibt `running`: stale-running Schutz greift nach 2 Stunden.
- Job läuft nicht: `enabled`, `next_run_at`, `timezone`, Schedule prüfen.
