Zum Inhalt

Exponential Backoff & Retry für Auth Claims Trigger

Übersicht

Die Funktion onShopUserPermissionChanged wurde mit Exponential Backoff + Retry ausgestattet, um Race Conditions bei Batch-Approvals zu verhindern.

Problem

Firebase Auth hat Rate Limits bei setCustomUserClaims(). Wenn ein ERP-Admin viele Shop-User gleichzeitig freigibt, feuert der Trigger für jeden customerUsers-Doc und macht parallele Auth-Claim-Updates. Dies kann zu auth/quota-exceeded Fehlern führen.

Lösung

1. Retry-Helper mit Exponential Backoff

Eine neue Hilfsfunktion retryWithExponentialBackoff() wurde implementiert:

Retry-Strategie (Equal Jitter): - Versuch 1: sofort - Versuch 2: nach 50–100ms (zufällig) - Versuch 3: nach 100–200ms (zufällig) - Versuch 4: nach 200–400ms (zufällig) - Versuch 5: nach 400–800ms (zufällig) - Max. 5 Versuche (default)

Jitter-Strategie: Equal Jitter

Um das Thundering-Herd-Problem bei parallelen Batch-Operationen zu vermeiden, wird Equal Jitter verwendet. Math.ceil(maxDelay/2 + random * maxDelay/2) stellt sicher, dass ein Mindest-Backoff von 50% gewahrt bleibt, während die zufällige Komponente die Retry-Zeitpunkte paralleler Requests auseinanderstreut.

Retryable Fehler: - auth/quota-exceeded - auth/internal-error - Error-Message enthält "quota", "rate" oder "resource_exhausted" (case-insensitive)

Nicht-retryable Fehler: - auth/user-not-found - Alle anderen Fehler → sofortiger Abbruch ohne Retry

2. Angepasste Trigger

Beide Trigger verwenden jetzt Retry:

onErpUserDocumentCreated

await retryWithExponentialBackoff(async () => {
  await admin.auth().setCustomUserClaims(uid, { appType: "erp" });
});

onShopUserPermissionChanged

await retryWithExponentialBackoff(async () => {
  await admin.auth().setCustomUserClaims(uid, {
    ...existingClaims,
    appType,
    customerIds,
    adminCustomerIds,
  });
});

3. Tests

13 Unit-Tests validieren die Retry-Logik:

✅ Erfolgreicher erster Versuch
✅ Retry bei auth/quota-exceeded
✅ Retry bei RESOURCE_EXHAUSTED
✅ Retry bei "quota" in Error-Message
✅ Retry bei "rate" in Error-Message
✅ Aufgeben nach maxRetries
✅ Sofortiger Abbruch bei nicht-retryable Errors
✅ Korrekte Exponential Backoff Delays mit Jitter
✅ Custom maxRetries funktioniert
✅ Custom baseDelayMs funktioniert
✅ auth/internal-error wird als retryable behandelt
✅ Reguläre Errors werfen sofort
✅ Jitter-Berechnung (Delay zwischen maxDelay/2 und maxDelay)

Alle Tests bestehen:

Änderungen

Geänderte Dateien

  1. core/functions/src/triggers/auth-claims.triggers.js
  2. Neue retryWithExponentialBackoff() Funktion
  3. Beide setCustomUserClaims() Aufrufe nutzen Retry

  4. core/functions/test/unit/triggers/auth-claims.test.js (neu)

  5. 13 Unit-Tests für Retry-Logik inkl. Jitter-Validierung
  6. Fake timers für deterministische Tests

Vorteile

Robustheit: Batch-Approvals schlagen nicht mehr bei Rate-Limits fehl
Automatisch: Keine manuelle Intervention bei temporären Fehlern
Intelligent: Nur retryable Fehler werden wiederholt
Performant: Exponential Backoff mit Jitter verhindert Überlastung und Thundering Herd
Testbar: Vollständige Unit-Test-Abdeckung

Monitoring

Die Retry-Funktion loggt jeden Retry-Versuch:

secureLogger.info("Retry mit Exponential Backoff", {
  attempt: attempt + 1,
  maxRetries,
  delayMs,
  error: err.message,
});

Monitoring in Cloud Logging:

severity = INFO
jsonPayload.message = "Retry mit Exponential Backoff"
jsonPayload.attempt = 2
jsonPayload.delayMs = 100

Best Practices

  1. Batch-Approvals: Können jetzt sicher ausgeführt werden
  2. Fehlerbehandlung: Nicht-retryable Errors schlagen sofort fehl (z.B. User nicht gefunden)
  3. Rate Limits: Exponential Backoff mit Equal Jitter verhindert API-Überlastung und Thundering Herd
  4. Monitoring: Überwachen Sie Retry-Logs für Capacity-Planning

Zukünftige Erweiterungen

Optional könnten folgende Verbesserungen erwogen werden:

  • ~~Jitter: Zufälliger Offset zu Delays, um "Thundering Herd" zu vermeiden~~ ✅ Implementiert (Equal Jitter)
  • Max Backoff: Cap bei z.B. 5 Sekunden für sehr viele Retries
  • Konfigurierbar: maxRetries und baseDelayMs über Firestore Settings

Audit-Empfehlung

Vollständig umgesetzt

"Exponential Backoff + Retry in onShopUserPermissionChanged einbauen" wurde implementiert und getestet.