Zum Hauptinhalt springen

Infrastruktur Services

Die Infrastruktur-Services umfassen Health-Checks, Server-Monitoring, Google API Quota-Tracking, AI-Integration und Cross-Platform-Matching. Sie bilden das operative Rückgrat des Systems.

SystemHealthService

Location: app/Services/Health/SystemHealthService.php

Führt umfassende Health-Checks für alle Systemkomponenten durch. Wird vom /up_system-Endpoint für UptimeRobot-Monitoring und von der Admin-Seite verwendet.

Public Methods

MethodParameterReturnBeschreibung
runAllChecks()--arrayAlle Health-Checks ausführen und aggregieren
checkDatabase()--arrayPostgreSQL-Konnektivität prüfen
checkCache()--arrayRedis Read/Write/Delete Zyklus prüfen
checkReverb()--arrayWebSocket-Server Erreichbarkeit prüfen
checkQueueWorkers()--arrayQueue-Worker-Status prüfen
checkFailedJobs()--arrayFailed-Jobs-Anzahl gegen Schwellwert prüfen
checkInstagramDailyScrape()--arrayInstagram Collector Pipeline prüfen
checkYouTubeDailySync()--arrayYouTube Daily Sync Status prüfen
checkCollectorHeartbeat()--arrayCollector-API Heartbeat prüfen
checkGoogleApiQuota()--arrayAPI-Quota-Verbrauch prüfen
checkCronHeartbeats()--arrayCron-Job Heartbeats prüfen
checkPipelineStatus()--arrayPipeline-Gesamtstatus prüfen
checkExploreMetricsFreshness()--arrayExplore-Metriken-Aktualität prüfen
checkYouTubeVideoSync()--arrayYouTube Video Sync Status prüfen
buildPipelineAscii()--stringASCII-Pipeline-Übersicht generieren
getJobThroughput()--arrayJob-Durchsatz (1/5/15 Min) berechnen

Status-Werte

Jeder Check gibt ein standardisiertes Array zurück:

[
'status' => 'RUNNING', // RUNNING | FAILED | WARNING | WAITING
'message' => '...', // Human-readable Status
'error' => '...', // Nur bei FAILED/WARNING
]

Overall-Status-Aggregation

'ALL_SYSTEMS_OPERATIONAL'  // Kein Check FAILED
'SYSTEM_DEGRADED' // Mindestens ein FAILED

Hinweis: WARNING-Checks sind rein informativ und degradieren den Overall-Status nicht. Nur FAILED-Checks erzeugen SYSTEM_DEGRADED. WARNINGs sind im Response-Body sichtbar, beeinflussen aber nicht den UptimeRobot-Keyword-Check auf ALL_SYSTEMS_OPERATIONAL.

Cron-Heartbeat-System

Scheduled Commands schreiben nach erfolgreicher Ausführung einen Heartbeat in den Cache:

Cache::put("cron:heartbeat:{$key}", now()->timestamp, $ttl);

checkCronHeartbeats() liest diese Werte und vergleicht sie gegen konfigurierte max_minutes-Schwellwerte in config('postbox.health.cron_heartbeats').

Deploy-Schutz: Nach einem Deployment (Cache-Clear) wird ein deploy:timestamp Marker gesetzt. Checks melden WAITING statt FAILED für die Dauer von max_minutes nach dem Deploy.

Queue-Worker-Check

Der ai-detection Queue wird von der Pending-Count-Prüfung ausgenommen, da dieser Queue rate-limited ist (15 RPM via Gemini API) und legitimerweise einen großen Backlog haben kann.

Neben der Standard-Queue-Prüfung wird ein sekundärer Check über kürzlich abgeschlossene collector_jobs durchgeführt — wenn innerhalb des Activity-Fensters Jobs abgeschlossen wurden, gilt der Worker als aktiv (auch bei hohem Pending-Count).

Queue Quiet Hours

Konfigurierbare Quiet Hours fuer Queue-Health-Checks (Default: 04:00–06:00 UTC). Waehrend Quiet Hours wird WAITING statt WARNING zurueckgegeben, um falsche UptimeRobot-Alarme bei geplanter Wartung zu vermeiden.

HEALTH_QUEUE_QUIET_START=4   # 04:00 UTC (Default)
HEALTH_QUEUE_QUIET_END=6 # 06:00 UTC (Default)
HEALTH_QUEUE_PENDING_THRESHOLD=5000 # Pending-Jobs-Schwellwert (Default: 5000)
HEALTH_QUEUE_ACTIVITY_WINDOW_MINUTES=30 # Activity-Fenster in Minuten (Default: 30)
SzenarioErgebnis
≤5000 pending ODER recent processing (Queue oder Collector)RUNNING
>5000 pending + keine recent processing + ausserhalb Quiet HoursWARNING
>5000 pending + keine recent processing + innerhalb Quiet HoursWAITING

Die isDuringQuietHours()-Methode unterstuetzt Wrap-Around (z.B. start=23, end=5 fuer 23:00–04:59 UTC). Wenn start === end, sind Quiet Hours deaktiviert.

Pipeline-ASCII-Übersicht

buildPipelineAscii() generiert eine ASCII-Tabelle aller Tages-Pipeline-Schritte für den /up_system-Endpoint. Schritte werden aus PipelineStatusService::getStatusForDate() geladen und nach Phase gruppiert:

PhaseLabelSchritte
collectionDatensammlungYouTube Scrape, Instagram Scrape, Video Sync
processingVerarbeitungScores, Explore Metrics, Leaderboards
maintenanceWartung (Nacht)Sanitizer, Retry, Prune

Status-Icons: OK (completed), >> (running), .. (scheduled), !! (overdue), XX (failed), -- (waiting).

Laufende Schritte zeigen einen ASCII-Fortschrittsbalken: [########............] 40%.

Job-Durchsatz

getJobThroughput() zählt abgeschlossene Jobs in drei Zeitfenstern (1 Min, 5 Min, 15 Min):

MetrikQuelleZählung
collector_1m/5m/15mcollector_jobsstatus=completed + updated_at >= now()-Xmin
queue_1m/5m/15mjobs (Standard-Queue)reserved_at >= now()-Xmin

Wird auf /up_system direkt unter der Pipeline-ASCII-Übersicht angezeigt.

Abhängigkeiten

  • DB, Cache, CollectorJob Model, DailySyncRun Model, PipelineStatusService, Config (postbox.health.*, reverb.*, services.youtube.*)

Verwendet von

  • /up_system Health-Endpoint (SystemHealthController), Admin Dashboard

ServerAlertService

Location: app/Services/ServerAlertService.php

Prüft Server-Metriken (CPU, RAM, Swap, Disk, PostgreSQL) gegen konfigurierte Schwellwerte und feuert Alerts bei Überschreitung. Nutzt ein State-Change-Modell zur Vermeidung von Alert-Fatigue.

Public Methods

MethodParameterReturnBeschreibung
check()array $metricsvoidAlle Metriken gegen Schwellwerte prüfen

Alert-Level

LevelBeschreibung
okAlle Werte im Normalbereich
warningSchwellwert überschritten, Aufmerksamkeit erforderlich
criticalKritischer Schwellwert überschritten, sofortige Aktion nötig

State-Change-Modell

Alerts werden nicht bei jedem Check gefeuert, sondern nur bei Zustandsänderungen:

  • Erstmaliges Auftreten: Alert sofort
  • Gleicher Level bleibt: Sustained Cooldown (Default: 4 Stunden)
  • Level ändert sich (z.B. ok -> warning, warning -> critical): Alert sofort

Konfigurierte Metriken

MetrikWarningCriticalRichtung
cpu_percent>= 75>= 90Höher = schlimmer
ram_percent>= 80>= 90Höher = schlimmer
swap_percent>= 25>= 50Höher = schlimmer
disk_percent>= 80>= 90Höher = schlimmer
pg_connections>= 80>= 150Höher = schlimmer
pg_cache_hit< 95< 90Niedriger = schlimmer (inverted)

Alert-Aktionen

  1. Log-Eintrag (Log::warning())
  2. ServerAlertTriggered-Event dispatchen
  3. Announcement in der Nachrichtenzentrale für alle Admins (via NotificationService)

Abhängigkeiten

  • Cache, NotificationService, ServerAlertTriggered Event, Config (server-monitoring.*)

Verwendet von

  • CheckServerAlerts-Command, Pulse Recorders

CronHeartbeatMonitorService

Location: app/Services/CronMonitoring/CronHeartbeatMonitorService.php

Zentraler Service für Cron-Heartbeat-Monitoring, Alerting, Eskalation und Muting. Vollständige In-App-Lösung für Cron-Überwachung.

Public Methods

MethodParameterReturnBeschreibung
checkAll()--array{ok, failed, not_required, muted, results}Alle Heartbeats prüfen und Status aggregieren
checkSingle()string $key, array $configarrayEinzelnen Heartbeat prüfen
mute()string $keyvoidHeartbeat stumm schalten (Admin-Aktion)
unmute()string $keyvoidStummschaltung aufheben
isMuted()string $keyboolPrüfen ob Heartbeat stumm geschaltet ist
getMutedHeartbeats()--array<string, string>Alle stumm geschalteten Heartbeats mit Zeitstempel
calculateUptimeSla()string $key, int $daysarrayUptime-SLA für einen Heartbeat berechnen
calculateOverallSla()int $daysarraySystem-weites Uptime-SLA berechnen
getRecentEvents()int $limitCollectionLetzte Events für Historie-Tabelle
sendMuteReminder()--voidWöchentliche Erinnerung an Admins für stumme Heartbeats

State-Change-Modell

Alerts werden nicht bei jedem Check gefeuert, sondern nur bei Zustandsänderungen:

  • Erstmaliger Ausfall (okfailed): Sofortige Notification + Event-Log
  • Anhaltender Ausfall: Cooldown (Default: 60 Min) verhindert Spam
  • Eskalation (E3): Nach 60 Min ohne Recovery zusätzliche Eskalations-Notification
  • Recovery (failedok): Recovery-Notification + Cache-Cleanup
  • Muted: Keine Alerts, Status wird als muted gemeldet

Dependency-Analyse (E4)

Bei Heartbeat-Ausfall werden konfigurierte Abhängigkeiten geprüft:

// config/postbox.php → cron_monitoring.dependencies
'dependencies' => [
'sitemap_generate' => ['public_explorer_refresh'],
'scores_calculate' => ['pipeline_run'],
],

Wenn eine Abhängigkeit ebenfalls ausgefallen ist, wird die Root-Cause-Information in die Alert-Notification aufgenommen.

Uptime-SLA (E2)

Berechnet Uptime-Prozentsatz aus failed/recovered Events in der cron_heartbeat_events Tabelle:

  • Pro Heartbeat: Downtime-Minuten aus Event-Paaren summieren
  • System-weit: Schlechtester Heartbeat bestimmt Gesamt-SLA
  • Top-5 schlechteste Heartbeats werden angezeigt

Event-Historie (Phase 3)

Alle Statuswechsel werden in cron_heartbeat_events protokolliert:

EventBeschreibung
failedHeartbeat erstmalig überfällig
recoveredHeartbeat wiederhergestellt
escalatedEskalation nach >60 Min Ausfall
mutedVon Admin stumm geschaltet
unmutedStummschaltung aufgehoben

Retention: 30 Tage (konfigurierbar, via MassPrunable).

Abhängigkeiten

  • Cache, NotificationService, CronHeartbeatEvent Model, Config (postbox.cron_monitoring.*, postbox.health.cron_heartbeats)

Verwendet von

  • CheckCronHeartbeats-Command (alle 5 Min), AppMonitoring/Index Livewire Component

GoogleApiUsageService

Location: app/Services/GoogleApiUsage/GoogleApiUsageService.php

Fragt die Google Cloud Monitoring API und Service Usage API ab, um YouTube API Quota-Verbrauch und -Limits zu synchronisieren. Authentifiziert sich via manueller JWT-Token-Exchange (kein gcloud CLI nötig).

Public Methods

MethodParameterReturnBeschreibung
fetchQuotaUsage()string $projectId, string $servicearrayAktuellen Quota-Verbrauch abfragen
fetchQuotaLimits()string $projectId, string $servicearrayQuota-Limits abfragen

Authentifizierung

  1. Service Account Credentials aus JSON-Datei laden (Pfad via GOOGLE_APPLICATION_CREDENTIALS)
  2. JWT mit RS256 erstellen (Header + Payload + Signatur)
  3. JWT gegen Google OAuth2 Token-Endpoint tauschen
  4. Access Token für nachfolgende API-Calls verwenden

Quota-Usage-Abfrage

Nutzt die serviceruntime.googleapis.com/quota/rate/net_usage Metrik aus der Cloud Monitoring API. Ergebnisse werden pro quota_metric und optional pro credential_id aggregiert.

Quota-Limits-Abfrage

Nutzt den Service Usage API v1beta1-Endpoint consumerQuotaMetrics. Liefert tägliche, pro-Minute und pro-Minute-pro-User Limits mit effektiven Werten aus quotaBuckets.

Abhängigkeiten

  • Guzzle HTTP Client, Config (google_api_usage.*)

Verwendet von

  • SyncGoogleApiUsage-Command (stündlich)

GoogleApiUsageRepository

Location: app/Services/GoogleApiUsage/GoogleApiUsageRepository.php

Persistiert Quota-Limits und -Verbrauch in der Datenbank via Upsert-Operationen. Garantiert Idempotenz durch stabile Unique-Constraints.

Public Methods

MethodParameterReturnBeschreibung
upsertLimits()string $projectId, string $service, array $limitsintQuota-Limits upserten
upsertUsages()string $projectId, string $service, array $usages, CarbonInterface $date, CarbonInterface $collectedAtintQuota-Verbrauch upserten

Upsert-Strategie

  • Limits: Unique auf (project_id, service, quota_metric, display_name, unit) -- unit ist Teil des Keys, da eine Metrik verschiedene Limit-Einheiten haben kann (pro Tag, pro Minute)
  • Usages: Unique auf (project_id, service, quota_metric, date) -- pro Tag und Metrik ein Eintrag

Abhängigkeiten

  • GoogleApiQuotaLimit Model, GoogleApiQuotaUsage Model

Verwendet von

  • SyncGoogleApiUsage-Command

ChannelLanguageDetector

Location: app/Services/AI/ChannelLanguageDetector.php

Erkennt Sprache, Land, Kategorie, Keywords und erstellt Beschreibungen für Social Profiles via Google Gemini API. Alle AI-generierten Inhalte sind auf Englisch.

Public Methods

MethodParameterReturnBeschreibung
detect()SocialProfile $profile?arraySprache/Land erkennen (vereinfacht)
detectWithLogging()SocialProfile $profile?arrayErkennung mit vollständigem Logging
detectAndSave()SocialProfile $profileboolErkennen und Profil aktualisieren

Erkannte Felder

FeldFormatConfidence
languageISO 639-1 (z.B. de, en)high/medium/low
countryISO 3166-1 alpha-2 (z.B. DE, US)high/medium/low
categoryAus 25 vordefinierten Kategorienhigh/medium/low
keywordsBis zu 10 englische Keywordshigh/medium/low
description200-500 Zeichen, Englischhigh/medium/low
social_linksCross-Platform Handleshigh/medium/low

AI-Modell

  • Modell: gemini-2.5-flash-lite (konfigurierbar via services.gemini.model)
  • Temperature: 0.1 (deterministisch)
  • Timeout: 30 Sekunden
  • Rate-Limit: Queue ai-detection mit 15 RPM

Logging

Jede Detection wird in ai_detection_logs protokolliert:

  • input_data: Handle, Title, Description, Platform
  • output_data: Parsed AI-Ergebnis
  • raw_response: Vollständige Gemini-Response
  • status: success oder error
  • tokens_used: Token-Verbrauch
  • duration_ms: Verarbeitungsdauer

Die AI erkennt Cross-Platform-Links in der Profil-Beschreibung:

  • Unterstützte Plattformen: Instagram, TikTok, Twitter, Twitch, Facebook, Spotify, Website
  • Handles werden normalisiert (ohne @-Prefix)
  • Die Quell-Plattform wird ausgeschlossen (z.B. kein YouTube-Link bei YouTube-Profilen)

Abhängigkeiten

  • Google Gemini API, AiDetectionLog Model, SocialProfile Model

Verwendet von

  • DetectChannelLanguages-Command (alle 15 Min), ExploreCategoryDetector

CrossPlatformRelatedCalculator

Location: app/Services/Related/CrossPlatformRelatedCalculator.php

Berechnet Cross-Platform Related Profiles (YouTube <-> Instagram) ohne API-Calls. Nutzt ausschließlich lokale Daten für das Matching.

Public Methods

MethodParameterReturnBeschreibung
findInstagramForYouTube()SocialProfile $youtubeProfilearray{found, stored}Instagram-Profile für YouTube-Channel finden
findYouTubeForInstagram()SocialProfile $instagramProfilearray{found, stored}YouTube-Channels für Instagram-Profil finden

Score-Komponenten (Max. 100 Punkte)

FaktorMax. PunkteBeschreibung
Parsed Link Match50Direkter Link/Handle in Bio gefunden
Keyword Match30Gemeinsame Keywords (6 Punkte pro Match)
Category Match25Gleiche Kategorie
Favorites Bonus15Viele Favoriten = relevanter
Follower Tier10Gleicher oder benachbarter Tier
Name Similarity10Ähnliche Profile-Namen
Language/Country10Gleiche Sprache (5) + gleiches Land (5)

Approved Links (via Admin in social_profile_links) erhalten volle 50 Punkte. Unapproved Auto-Parsed Links erhalten 70% (35 Punkte). Generische @handle-Matches erhalten 50% (25 Punkte).

Constraints

  • Max. 25 Related Profiles pro Source-Profil
  • Max. 500 Kandidaten werden evaluiert
  • Minimum-Score: 10 Punkte
  • Min. 1 Keyword-Match für Kandidatur

Abhängigkeiten

  • ProfileKeywordExtractor, CrossPlatformRelatedProfile Model, InstagramProfileKeyword Model, SocialProfileLink Model

Verwendet von

  • QueueCrossPlatformRelated-Command (täglich 06:00 UTC), AutoFillCrossPlatformRelated-Command

TagConsolidator & TagMerger

Location: app/Services/AI/TagConsolidator.php, app/Services/AI/TagMerger.php

AI-gesteuerte Konsolidierung von Tags via Gemini. Der TagConsolidator analysiert Tag-Gruppen und schlägt Merges/Suppressions vor. Der TagMerger führt die Aktionen aus.

TagConsolidator

MethodParameterReturnBeschreibung
consolidateChunk()array $tagsarrayTag-Chunk via Gemini analysieren

TagMerger

MethodParameterReturnBeschreibung
executeMerge()string $source, string $targetvoidTags zusammenführen
executeSuppress()string $tagvoidTag unterdrücken
executeBatch()array $actionsarrayBatch von Merge/Suppress-Aktionen
invalidateCaches()--voidTag-Caches invalidieren

Verwendet von

  • ConsolidateTags-Command (wöchentlich)