Zum Hauptinhalt springen

Scheduler Übersicht

Alle Scheduled Tasks werden in routes/console.php definiert und laufen in UTC. Der Laravel Scheduler wird per Cron jede Minute aufgerufen:

php8.4 /home/forge/app.postbox.so/current/artisan schedule:run

Location: routes/console.php

Vollständige Task-Tabelle

Data Collection (Scraping)

CommandZeitpunkt(e)IntervallOverlapHeartbeatBeschreibung
social:queue-daily-instagram00:00, 02:00, 04:00, ...alle 2h (gerade)30 mininstagram_scrapeInstagram-Scrapes in Collector-Queue einreihen
social:scrape-daily-followers09:00, 11:00, 13:00, ...alle 2h (ungerade, ab 09)30 minyoutube_scrapeYouTube Daily Scrape via Data API
health:check-daily-scrapes09:30, 11:30, 13:30, ...alle 2h (:30 ungerade, ab 09:30)NeinAlert-E-Mail wenn Instagram/YouTube Jobs fehlen

Nightly Pipeline (pipeline:run)

CommandZeitpunkt(e)IntervallOverlapHeartbeatBeschreibung
notifications:rollup-stats00:30taeglichJaMail-Log in taegliche Statistiken aggregieren
pipeline:run03:00taeglichJapipeline_runZentraler Orchestrator: Scores+Explore → Rollups → Leaderboards → Trending → Tags, Cross-Platform, Explorer Refresh

Die Pipeline ersetzt 8 einzeln geschedulte Commands (dashboard:rollup-*, scores:calculate, explore:calculate, tags:refresh-cache, cross-platform:queue-related, public-explorer:refresh). Alle laufen jetzt als parallele Bus::batch-Jobs innerhalb von pipeline:run. Phase 3 (Trending Flags, Videos, Categories, Tag Cache, Explorer Refresh) wird als eigenstaendiger RunPipelinePhase3-Job dispatcht, damit er einen frischen Worker-Prozess mit vollem Memory erhaelt. Manueller Re-Run: php artisan pipeline:run --phase=phase3.

SEO Pipeline (nach pipeline:run)

CommandZeitpunkt(e)IntervallOverlapHeartbeatBeschreibung
sitemap:generate07:30taeglichJasitemap_generateXML-Sitemaps generieren (nach Pipeline)
og-images:generate --missing-only08:00täglich60 minOG-Images für neue Profile generieren (Browsershot)
seo:sync-search-console08:00täglich30 minseo_sync_search_consoleGoogle Search Console Metriken synchronisieren
api-tokens:check-alerts08:00täglichJaInaktive/ablaufende API Tokens prüfen

Abend-Jobs (Retry + Sanitizer)

CommandZeitpunkt(e)IntervallOverlapBeschreibung
profiles:sanitize03:30taeglichJaLow-Value Profile auto-deaktivieren (Queue-basiert via SanitizeProfileBatch)
profiles:retry-inactive22:00taeglichJaDeaktivierte Profile erneut versuchen, archivieren nach 6 Monaten

YouTube

CommandZeitpunkt(e)IntervallOverlapHeartbeatBeschreibung
youtube:manage-websub --all06:00täglich30 minwebsub_manageWebSub-Subscriptions erneuern, erstellen, bereinigen
youtube:poll-rss-feeds:00, :30alle 30 Min25 minyoutube_rss_pollRSS-Feeds pollen (WebSub-Fallback für neue Videos)
youtube:sync-video-stats14:00täglichJayoutube_video_syncVideo-Statistiken für Auto-Sync-Profile
youtube:calculate-video-scores21:00täglichJaVideo-Performance-Scores berechnen

AI & Spracherkennung

CommandZeitpunkt(e)IntervallOverlapBeschreibung
social:detect-languages --queuealle 15 Minalle 15 MinJaAI-Spracherkennung (Hourly Limit / 4 pro Chunk)

Hochfrequent (5-Minuten-Intervall)

CommandIntervallOverlapHeartbeatBeschreibung
watchers:resume-importsalle 5 MinJaPausierte Watcher-Imports fortsetzen
youtube:auto-fill-related-channelsalle 5 MinJaRelated Channels bei freiem Quota füllen
ProcessPendingYouTubeImports (Job)alle 5 MinJaQuota-blockierte YouTube-Imports verarbeiten
cross-platform:auto-fill-relatedalle 5 MinJacross_platform_auto_fillCross-Platform Related in kleinen Batches (5/Run)
instagram:auto-fill-relatedalle 5 MinJaInstagram Related in kleinen Batches (5/Run)
server:check-alertsalle 5 MinJaserver_alertsServer-Metriken gegen Alert-Schwellwerte prüfen
server:snapshotalle 5 MinJaserver_snapshotPulse-Metriken → server_metrics Tabelle
collector:requeue-expired-leasesalle 5 Min5 mincollector_requeueVerwaiste Collector-Jobs mit abgelaufenen Leases requeuen

Monitoring & Snapshots (15 Min / Stündlich)

CommandZeitpunkt(e)IntervallOverlapHeartbeatBeschreibung
queue:metrics-snapshot:00, :15, :30, :45alle 15 Min5 minqueue_metricsQueue-Metriken für Admin-Charts
db:snapshot:00, :15, :30, :45alle 15 MinJadb_monitoringPostgreSQL-Metriken Snapshot
db:snapshot --slow-queries --top=50:00 jede StundestündlichJaTop 50 Slow Queries erfassen
google:sync-api-usage:00 jede Stundestündlich10 mingoogle_api_syncGoogle API Quota synchronisieren
watchers:backfill-admin-workspace:00 jede StundestündlichJaProfile ins Admin-Workspace spiegeln
error-monitor:check-anomalies:00 jede StundestündlichJaError-Anomalie-Schwellwerte prüfen (max 1 Mail/h)
cloudflare:fetch-r2-metrics:00 jede Stundestündlich5 minr2_metricsR2-Metriken via Cloudflare GraphQL/REST API

Alle 30 Minuten

CommandIntervallOverlapHeartbeatBeschreibung
youtube:poll-rss-feedsalle 30 Min25 minyoutube_rss_pollRSS-Feeds für Auto-Sync-Profile pollen (WebSub-Fallback)

Data Retention & Pruning

CommandZeitpunkt(e)IntervallOverlapBeschreibung
db:prune-historical-data01:00täglichJaMetriken (400d), Rollups (400d), Snapshots (90d)
error-monitor:rollup-daily01:10täglichJaError-Logs → Daily Stats aggregieren
queue:prune-failed --hours=16801:15täglichJaLaravel failed_jobs > 7 Tage löschen
pulse:purge01:20täglichJaPulse-Daten > 7 Tage löschen (PULSE_STORAGE_KEEP)
vantage:cleanup-stuck --timeout=201:22täglichJaStuck "processing" Jobs > 2h als failed markieren
vantage:prune --status=completed --hours=2401:25täglichJaCompleted Vantage-Jobs > 24h löschen
vantage:prune --status=failed --days=401:30täglichJaFailed Vantage-Jobs > 4 Tage löschen
youtube-research:prune01:30täglichJaResearch-Queries > 30 Tage löschen
server:snapshot --prune02:20täglichJaServer-Metriken > 90 Tage prunen
db:snapshot --prune02:15täglichJaDB-Monitoring-Snapshots > Retention löschen
server:rollup-hourly --prune03:00täglichJa5-Min-Snapshots → stündliche Rollups (365d Retention)
ai:prune-logs --days=710:30, 22:302x täglichJaAI-Detection-Logs > 7 Tage löschen
notifications:cleanup03:00täglichJaAbgelaufene Benachrichtigungen + alte Reads löschen
collector:prune-logs --days=711:15, 17:15, 23:15, 05:15alle 6hJaCollector-Logs > 7 Tage löschen

Wöchentlich

CommandTag / UhrzeitOverlapBeschreibung
keywords:update-stopwordsSonntag 02:00JaDynamische Stopword-Liste aktualisieren
error-monitor:pruneSonntag 03:00JaError-Daten > 365 Tage bereinigen
seo:prune-metricsSonntag 03:30JaSearch Console + Web Vitals Daten > 90 Tage löschen
og-images:cleanupSonntag 04:0030 minVerwaiste OG-Images löschen
storage:sync-r2-backupSonntag 04:30120 minPrimary R2 Bucket in Backup Bucket syncen
ai:retry-failed --limit=50Sonntag 08:00JaFailed AI-Detection Retry (max 50)
tags:consolidateSonntag 18:00JaAI-Tags via Gemini konsolidieren
social:discover-from-links --retry-failed --limit=200Sonntag 09:00JaFehlgeschlagene Profil-Discovery erneut versuchen
error-monitor:weekly-reportMontag 08:00JaWöchentlicher Error-Report
seo:web-vitals-reportMontag 09:00JaWöchentlicher Web Vitals Report mit Degradation-Warnungen
pulse:check-sizeSonntag 03:3030 minPulse-Tabellen Größen-Check (> 2 GB Alert)

Overlap-Schutz

Die meisten Tasks verwenden .withoutOverlapping(minutes) mit einem Mutex-Lock, um parallele Ausführungen zu verhindern. Die Lock-Dauer variiert je nach erwarteter Laufzeit:

Task-TypLock-DauerBegründung
Instagram/YouTube Scrape30 minKann bei vielen Profilen lange dauern
Google API Sync10 minRelativ kurz, häufige Ausführung
Queue Metrics5 minSehr kurz, alle 15 Min
Standard (ohne Angabe)1440 min (24h)Laravel Default

Cron Heartbeats

Kritische Tasks schreiben nach erfolgreicher Ausführung einen Heartbeat in den database Cache-Store. Der Health-Endpoint /up_system liest diese Heartbeats und meldet den Status an UptimeRobot:

// Scheduler-Callback fuer eigenstaendige Commands:
$cronHeartbeat = function (string $key): \Closure {
return function () use ($key) {
CronStatusService::recordHeartbeat($key);
};
};

Schedule::command('pipeline:run')
->dailyAt('03:00')
->after($cronHeartbeat('pipeline_run'));

Pipeline-interne Heartbeats: Die Pipeline sendet Heartbeats direkt aus den Queue-Jobs/Callbacks:

  • scores_calculatethen()-Callback nach Batch 1A (Score+Explore)
  • dashboard_rollupBuildDailyRollupsBatch
  • leaderboard_rollupBuildLeaderboardsJob
  • global_leaderboard_rollupBuildGlobalLeaderboardsJob
  • explore_metricsRunPipelinePhase3
  • pipeline_completeRunPipelinePhase3

Registrierte Heartbeats: instagram_scrape, youtube_scrape, youtube_video_sync, youtube_rss_poll, websub_manage, google_api_sync, queue_metrics, cross_platform_auto_fill, server_alerts, server_snapshot, db_monitoring, collector_requeue, pipeline_run, scores_calculate, dashboard_rollup, leaderboard_rollup, global_leaderboard_rollup, explore_metrics, pipeline_complete, tag_cache, public_explorer_refresh, sitemap_generate, seo_sync_search_console, seo_prune_metrics, api_token_alerts, profile_retry, profile_sanitize, youtube_publishing_stats, youtube_video_scores, pulse_purge, indexnow_submit, r2_metrics

Wöchentliche Jobs: Heartbeats für wöchentliche Jobs (z.B. youtube_publishing_stats, seo_prune_metrics) brauchen ein max_minutes von mindestens 8 Tagen (8 * 24 * 60 = 11520), nicht die Standard-26h. Sonst werden ab Tag 2 nach dem letzten Lauf False-Positive-Alarme ausgelöst.

Quiet Hours: Heartbeats können quiet_hours konfigurieren (z.B. 'quiet_hours' => [2, 8] für 02:00-08:00 UTC). Während der Quiet Hours gibt der Heartbeat-Check not_required statt failed zurück — keine Alerts, kein Rauschen im Dashboard. Nützlich für Tasks die planmäßig nur zu bestimmten Zeiten laufen (z.B. YouTube Scrape: 09:00-23:00 UTC).

Pipeline-Job-Heartbeats: Die Pipeline-Jobs (BuildDailyRollupsBatch, BuildLeaderboardsJob, BuildGlobalLeaderboardsJob) schreiben eigene Heartbeats direkt am Ende ihrer handle()-Methode: dashboard_rollup, leaderboard_rollup, global_leaderboard_rollup. So erkennt PipelineStatusService ob diese Steps erfolgreich liefen — fehlende Heartbeats zeigen den Step als "Ueberfaellig" an. BuildDailyRollupsBatch hat einen Timeout von 3600s (1 Stunde) da die Rollup-Berechnung bei vielen Profilen laenger als 10 Minuten dauern kann.

Konfiguration

Relevante ENV-Variablen für den Scheduler:

POSTBOX_INSTAGRAM_ROTATION_DAYS=3    # Tage zwischen Instagram-Scrapes
POSTBOX_YOUTUBE_ROTATION_DAYS=3 # Tage zwischen YouTube-Scrapes
POSTBOX_LEADERBOARD_CANDIDATE_LIMIT=200 # Kandidaten für Priority-Scraping
AI_ENHANCER_HOURLY_LIMIT=100 # Gemini AI Limit pro Stunde
AI_ENHANCER_RATE_PER_MINUTE=15 # Gemini AI Rate Limit
VANTAGE_RETENTION_DAYS=3 # Vantage Job-History Retention
PULSE_STORAGE_KEEP="7 days" # Pulse Daten-Retention
DB_MONITORING_RETENTION_DAYS=30 # DB-Monitoring-Snapshot Retention
DB_SLOW_QUERY_RETENTION_DAYS=14 # Slow-Query-Log Retention