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
| Method | Parameter | Return | Beschreibung |
|---|---|---|---|
runAllChecks() | -- | array | Alle Health-Checks ausführen und aggregieren |
checkDatabase() | -- | array | PostgreSQL-Konnektivität prüfen |
checkCache() | -- | array | Redis Read/Write/Delete Zyklus prüfen |
checkReverb() | -- | array | WebSocket-Server Erreichbarkeit prüfen |
checkQueueWorkers() | -- | array | Queue-Worker-Status prüfen |
checkFailedJobs() | -- | array | Failed-Jobs-Anzahl gegen Schwellwert prüfen |
checkInstagramDailyScrape() | -- | array | Instagram Collector Pipeline prüfen |
checkYouTubeDailySync() | -- | array | YouTube Daily Sync Status prüfen |
checkCollectorHeartbeat() | -- | array | Collector-API Heartbeat prüfen |
checkGoogleApiQuota() | -- | array | API-Quota-Verbrauch prüfen |
checkCronHeartbeats() | -- | array | Cron-Job Heartbeats prüfen |
checkPipelineStatus() | -- | array | Pipeline-Gesamtstatus prüfen |
checkExploreMetricsFreshness() | -- | array | Explore-Metriken-Aktualität prüfen |
checkYouTubeVideoSync() | -- | array | YouTube Video Sync Status prüfen |
buildPipelineAscii() | -- | string | ASCII-Pipeline-Übersicht generieren |
getJobThroughput() | -- | array | Job-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)
| Szenario | Ergebnis |
|---|---|
| ≤5000 pending ODER recent processing (Queue oder Collector) | RUNNING |
| >5000 pending + keine recent processing + ausserhalb Quiet Hours | WARNING |
| >5000 pending + keine recent processing + innerhalb Quiet Hours | WAITING |
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:
| Phase | Label | Schritte |
|---|---|---|
collection | Datensammlung | YouTube Scrape, Instagram Scrape, Video Sync |
processing | Verarbeitung | Scores, Explore Metrics, Leaderboards |
maintenance | Wartung (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):
| Metrik | Quelle | Zählung |
|---|---|---|
collector_1m/5m/15m | collector_jobs | status=completed + updated_at >= now()-Xmin |
queue_1m/5m/15m | jobs (Standard-Queue) | reserved_at >= now()-Xmin |
Wird auf /up_system direkt unter der Pipeline-ASCII-Übersicht angezeigt.
Abhängigkeiten
- DB, Cache,
CollectorJobModel,DailySyncRunModel,PipelineStatusService, Config (postbox.health.*,reverb.*,services.youtube.*)
Verwendet von
/up_systemHealth-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
| Method | Parameter | Return | Beschreibung |
|---|---|---|---|
check() | array $metrics | void | Alle Metriken gegen Schwellwerte prüfen |
Alert-Level
| Level | Beschreibung |
|---|---|
ok | Alle Werte im Normalbereich |
warning | Schwellwert überschritten, Aufmerksamkeit erforderlich |
critical | Kritischer 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
| Metrik | Warning | Critical | Richtung |
|---|---|---|---|
cpu_percent | >= 75 | >= 90 | Höher = schlimmer |
ram_percent | >= 80 | >= 90 | Höher = schlimmer |
swap_percent | >= 25 | >= 50 | Höher = schlimmer |
disk_percent | >= 80 | >= 90 | Höher = schlimmer |
pg_connections | >= 80 | >= 150 | Höher = schlimmer |
pg_cache_hit | < 95 | < 90 | Niedriger = schlimmer (inverted) |
Alert-Aktionen
- Log-Eintrag (
Log::warning()) ServerAlertTriggered-Event dispatchen- Announcement in der Nachrichtenzentrale für alle Admins (via
NotificationService)
Abhängigkeiten
- Cache,
NotificationService,ServerAlertTriggeredEvent, 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
| Method | Parameter | Return | Beschreibung |
|---|---|---|---|
checkAll() | -- | array{ok, failed, not_required, muted, results} | Alle Heartbeats prüfen und Status aggregieren |
checkSingle() | string $key, array $config | array | Einzelnen Heartbeat prüfen |
mute() | string $key | void | Heartbeat stumm schalten (Admin-Aktion) |
unmute() | string $key | void | Stummschaltung aufheben |
isMuted() | string $key | bool | Prüfen ob Heartbeat stumm geschaltet ist |
getMutedHeartbeats() | -- | array<string, string> | Alle stumm geschalteten Heartbeats mit Zeitstempel |
calculateUptimeSla() | string $key, int $days | array | Uptime-SLA für einen Heartbeat berechnen |
calculateOverallSla() | int $days | array | System-weites Uptime-SLA berechnen |
getRecentEvents() | int $limit | Collection | Letzte Events für Historie-Tabelle |
sendMuteReminder() | -- | void | Wö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 (
ok→failed): Sofortige Notification + Event-Log - Anhaltender Ausfall: Cooldown (Default: 60 Min) verhindert Spam
- Eskalation (E3): Nach 60 Min ohne Recovery zusätzliche Eskalations-Notification
- Recovery (
failed→ok): Recovery-Notification + Cache-Cleanup - Muted: Keine Alerts, Status wird als
mutedgemeldet
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:
| Event | Beschreibung |
|---|---|
failed | Heartbeat erstmalig überfällig |
recovered | Heartbeat wiederhergestellt |
escalated | Eskalation nach >60 Min Ausfall |
muted | Von Admin stumm geschaltet |
unmuted | Stummschaltung aufgehoben |
Retention: 30 Tage (konfigurierbar, via MassPrunable).
Abhängigkeiten
- Cache,
NotificationService,CronHeartbeatEventModel, Config (postbox.cron_monitoring.*,postbox.health.cron_heartbeats)
Verwendet von
CheckCronHeartbeats-Command (alle 5 Min),AppMonitoring/IndexLivewire 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
| Method | Parameter | Return | Beschreibung |
|---|---|---|---|
fetchQuotaUsage() | string $projectId, string $service | array | Aktuellen Quota-Verbrauch abfragen |
fetchQuotaLimits() | string $projectId, string $service | array | Quota-Limits abfragen |
Authentifizierung
- Service Account Credentials aus JSON-Datei laden (Pfad via
GOOGLE_APPLICATION_CREDENTIALS) - JWT mit RS256 erstellen (Header + Payload + Signatur)
- JWT gegen Google OAuth2 Token-Endpoint tauschen
- 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
| Method | Parameter | Return | Beschreibung |
|---|---|---|---|
upsertLimits() | string $projectId, string $service, array $limits | int | Quota-Limits upserten |
upsertUsages() | string $projectId, string $service, array $usages, CarbonInterface $date, CarbonInterface $collectedAt | int | Quota-Verbrauch upserten |
Upsert-Strategie
- Limits: Unique auf
(project_id, service, quota_metric, display_name, unit)--unitist 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
GoogleApiQuotaLimitModel,GoogleApiQuotaUsageModel
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
| Method | Parameter | Return | Beschreibung |
|---|---|---|---|
detect() | SocialProfile $profile | ?array | Sprache/Land erkennen (vereinfacht) |
detectWithLogging() | SocialProfile $profile | ?array | Erkennung mit vollständigem Logging |
detectAndSave() | SocialProfile $profile | bool | Erkennen und Profil aktualisieren |
Erkannte Felder
| Feld | Format | Confidence |
|---|---|---|
language | ISO 639-1 (z.B. de, en) | high/medium/low |
country | ISO 3166-1 alpha-2 (z.B. DE, US) | high/medium/low |
category | Aus 25 vordefinierten Kategorien | high/medium/low |
keywords | Bis zu 10 englische Keywords | high/medium/low |
description | 200-500 Zeichen, Englisch | high/medium/low |
social_links | Cross-Platform Handles | high/medium/low |
AI-Modell
- Modell:
gemini-2.5-flash-lite(konfigurierbar viaservices.gemini.model) - Temperature: 0.1 (deterministisch)
- Timeout: 30 Sekunden
- Rate-Limit: Queue
ai-detectionmit 15 RPM
Logging
Jede Detection wird in ai_detection_logs protokolliert:
input_data: Handle, Title, Description, Platformoutput_data: Parsed AI-Ergebnisraw_response: Vollständige Gemini-Responsestatus:successodererrortokens_used: Token-Verbrauchduration_ms: Verarbeitungsdauer
Social-Link-Erkennung
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,
AiDetectionLogModel,SocialProfileModel
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
| Method | Parameter | Return | Beschreibung |
|---|---|---|---|
findInstagramForYouTube() | SocialProfile $youtubeProfile | array{found, stored} | Instagram-Profile für YouTube-Channel finden |
findYouTubeForInstagram() | SocialProfile $instagramProfile | array{found, stored} | YouTube-Channels für Instagram-Profil finden |
Score-Komponenten (Max. 100 Punkte)
| Faktor | Max. Punkte | Beschreibung |
|---|---|---|
| Parsed Link Match | 50 | Direkter Link/Handle in Bio gefunden |
| Keyword Match | 30 | Gemeinsame Keywords (6 Punkte pro Match) |
| Category Match | 25 | Gleiche Kategorie |
| Favorites Bonus | 15 | Viele Favoriten = relevanter |
| Follower Tier | 10 | Gleicher oder benachbarter Tier |
| Name Similarity | 10 | Ähnliche Profile-Namen |
| Language/Country | 10 | Gleiche Sprache (5) + gleiches Land (5) |
Parsed-Link-Scoring
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,CrossPlatformRelatedProfileModel,InstagramProfileKeywordModel,SocialProfileLinkModel
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
| Method | Parameter | Return | Beschreibung |
|---|---|---|---|
consolidateChunk() | array $tags | array | Tag-Chunk via Gemini analysieren |
TagMerger
| Method | Parameter | Return | Beschreibung |
|---|---|---|---|
executeMerge() | string $source, string $target | void | Tags zusammenführen |
executeSuppress() | string $tag | void | Tag unterdrücken |
executeBatch() | array $actions | array | Batch von Merge/Suppress-Aktionen |
invalidateCaches() | -- | void | Tag-Caches invalidieren |
Verwendet von
ConsolidateTags-Command (wöchentlich)