Ziel: MVP-Spezifikation für “Automatisierte Backups per Zeitplan (pro Tenant)” als Grundlage für die Implementierung (Spec-first). Scope (MVP): Tenant-scoped backup_schedules + backup_schedule_runs Dispatcher erstellt idempotente Runs (Unique Slot) + Queue-Job führt Run aus “Run now” / “Retry”, Run-History, Retention (keep last N) No catch-up für verpasste Slots Wichtige Klarstellungen (aus Constitution abgeleitet): Jede Operation ist tenant-scoped und schreibt Audit Logs (Dispatcher/Run/Retention; keine Secrets/Tokens) Graph-Aufrufe laufen über die bestehende Abstraktion (keine Hardcodings) Retry/Backoff: Throttling → Backoff; 401/403 → kein Retry Authorization (MVP): TenantRole-Matrix (readonly/operator/manager/owner) statt neuer Permission-Registry Nicht im MVP: Kein Restore-Scheduling Kein Cross-Tenant Bulk Scheduling / Templates Kein Catch-up von missed runs Review-Fokus: Semantik “1 Run = 1 BackupSet” Concurrency/Lock-Verhalten (bei laufendem Run → skipped) DST/Timezone-Regeln + Slot-Minutenpräzision Artefakte: spec.md plan.md tasks.md requirements.md Co-authored-by: Ahmed Darrazi <ahmeddarrazi@adsmac.local> Reviewed-on: #33
2.9 KiB
2.9 KiB
Plan: Backup Scheduling MVP (032)
Date: 2026-01-05 Input: spec.md
Architecture / Reuse
- Reuse existing services:
PolicySyncService::syncPoliciesWithReport()for selected policy typesBackupService::createBackupSet()to create immutable snapshots + items (include_foundations supported)
- Store selection as
policy_types(config keys), not free-form categories. - Use tenant scoping (
tenant_id) consistent with existing tables (backup_sets,backup_items).
Scheduling Mechanism
- Add Artisan command:
tenantpilot:schedules:dispatch. - Scheduler integration (Laravel 12): schedule the command every minute via
routes/console.php+ ops configuration (Dokploy cronschedule:runor long-runningschedule:work). - Dispatcher algorithm:
- load enabled schedules
- compute whether due for the current minute in schedule timezone
- create run with
scheduled_forslot (minute precision) using DB unique constraint - dispatch
RunBackupScheduleJob(schedule_id, run_id)
- Concurrency:
- Cache lock per schedule (
lock:backup_schedule:{id}) plus DB unique slot constraint for idempotency. - If lock is held: mark run as
skippedwith a clear error_code (no parallel execution).
- Cache lock per schedule (
Run Execution
RunBackupScheduleJob:- load schedule + tenant
- preflight: tenant active; Graph/auth errors mapped to error_code
- sync policies for selected types (collect report)
- select policy IDs from local DB for those types (exclude ignored)
- create backup set:
- name:
{schedule_name} - {Y-m-d H:i} - includeFoundations: schedule flag
- name:
- set run status:
- success if backup_set.status == completed
- partial if backup_set.status == partial OR sync had failures but backup succeeded
- failed if nothing backed up / hard error
- update schedule last_run_* and compute/persist next_run_at
- dispatch retention job
- audit logs:
- log run start + completion (status, counts, error_code; no secrets)
Retry / Backoff
- Configure job retry behavior based on error classification:
- Throttling/transient (e.g. 429/503): backoff + retry
- Auth/permission (401/403): no retry
- Unknown: limited retries
Retention
ApplyBackupScheduleRetentionJob(schedule_id):- identify runs ordered newest→oldest
- keep last N runs that created a backup_set_id
- for older ones: soft-delete referenced BackupSets (and cascade soft-delete items)
- audit log: number of deleted BackupSets
Filament UX
- Tenant-scoped resources:
BackupScheduleResource- Runs UI via RelationManager under schedule (or a dedicated resource if needed)
- Actions: enable/disable, run now, retry
- Notifications: persist via
->sendToDatabase($user)for the DB info panel.- MVP notification scope: only interactive actions notify the acting user; scheduled runs rely on Run history.
Ops / Deployment Notes
- Requires queue worker.
- Requires scheduler running.
- Missed runs policy (MVP): no catch-up.