In .NET-Systemen beginnen viele Produktionsausfälle nicht mit einem plötzlichen Absturz, sondern mit einem schleichenden Stillstand: Anfragen bleiben hängen, Ressourcen werden blockiert und die Kapazität schwindet, ohne dass CPU oder Speicher auffällig sind.
Am 12. Oktober um 09:12 Uhr verlangsamte sich plötzlich ein zentraler Dienst. Die ASP.NET-Instanzen zeigten keine erhöhte Last, keine Speicherprobleme – doch die Anzahl der aktiven Anfragen stieg kontinuierlich an. Connection-Pools drehten sich nicht mehr, Skalierungsmaßnahmen brachten keine Besserung, da neue Instanzen ebenfalls in der Warteschleife landeten. Der einzige „Lösungsansatz“: Prozess-Recycling, das die blockierten Anfragen kurzzeitig beendete – bis das Problem eine Woche später erneut auftrat.
Dieser Leitfaden zeigt, wie Sie in .NET-Systemen stabile Zeitlimits einführen, CancellationToken konsequent propagieren und Fallback-Strategien implementieren – ohne neue Ausfälle zu riskieren.
Warum unbegrenzte Wartezeiten Kapazität rauben
Fehlende Zeitlimits sind kein reines Performance-Problem, sondern ein Kapazitätsproblem. Wenn eine Anfrage theoretisch unendlich warten darf, nutzt sie irgendwann alle verfügbaren Ressourcen auf: Worker-Slots, Threads, Datenbankverbindungen oder Locks. Das System verhält sich nicht mehr wie ein Dienst, sondern wie eine ungesteuerte Warteschlange.
Extern wirkt alles „langsam“, intern sammelt sich jedoch Arbeit an, die nie abgeschlossen wird. Zeitlimits sind daher keine technische Stellschraube, sondern eine Produktentscheidung: Wie lange ist das System bereit, auf eine Antwort zu warten, bevor es einen alternativen Pfad wählt?
Diagnose: Was wartet tatsächlich endlos?
Bevor Zeitlimits erzwungen werden, muss die Ursache der Verzögerungen systematisch analysiert werden. Sonst riskieren Sie, dass strikte Zeitvorgaben bestehende Probleme verschärfen.
Führen Sie diese Schritte in dieser Reihenfolge aus:
- Identifizieren Sie die drei wichtigsten Abhängigkeiten auf dem langsamen Pfad (HTTP-Aufrufe, SQL-Abfragen, SDK-Integrationen).
- Ermitteln Sie die längsten beobachteten Wartezeiten – nicht die Durchschnittswerte.
- Prüfen Sie, ob nach einem Zeitlimit tatsächlich keine weitere Arbeit mehr ausgeführt wird (Cancellation wird respektiert) oder ob „Zombie-Arbeit“ weiterläuft.
- Analysieren Sie die Zusammenhänge zwischen Zeitlimits und Wiederholungsversuchen: Zeitlimits + Retries bilden eine gemeinsame Budgetregel.
- Achten Sie auf Rückstausignale: Warteschlangenlänge, Alter der ältesten Anfrage, steigende Anzahl aktiver Anfragen bei sinkender Abschlussrate.
Schnellcheck-Tabelle für die ersten Maßnahmen
| Symptom | Wahrscheinliche Ursache | Schnelle Bestätigung | Erste sichere Maßnahme | |---------|------------------------|----------------------|-----------------------| | Aktive Anfragen steigen, Abschlussrate sinkt, CPU normal | Endlose Wartezeiten blockieren Ressourcen | Warteschlangenlänge und Alter der ältesten Anfrage steigen | Explizite Zeitlimits pro Versuch + Gesamtbudget einführen; CancellationToken durchgängig propagieren | | Anfragen „timten“ ab, aber nachgelagerte Dienste arbeiten weiter | Keine Cancellation-Propagation („Zombie-Arbeit“) | Arbeit wird nach Client-Zeitlimit fortgesetzt; späte Seiteneffekte sichtbar | CancellationToken in jeden asynchronen Aufruf integrieren; mit CancelAfter verknüpfte CTS nutzen | | Wiederholungsversuche verschärfen den Vorfall | Zeitlimits und Retries nicht als gemeinsames Budget betrachtet | Anzahl der Versuche steigt parallel zur Latenz; Gesamtzeit wächst unkontrolliert | Versuche begrenzen + Gesamtbudget einführen; keine Zeitlimits in langsame Abhängigkeiten wiederholen | | Eine Abhängigkeit dominiert die Verzögerung | Budgetverletzung durch eine einzelne Ressource (z. B. SQL-Abfrage) | Protokolle zeigen einen Dienst wiederholt mit Überschreitung der timeoutMs | Bulkhead-Muster + konservatives Zeitlimit; Fallback oder Warteschlangenpfad einführen |
Der erste Schritt zur Stabilität ist immer die klare Definition: Was ist unser Gesamtzeitbudget pro Anfrage oder Job?
Häufige Muster in .NET, die zu endlosen Wartezeiten führen
Wiederkehrende Ausfälle entstehen selten durch einen einzelnen Fehler, sondern durch ein System, das unbegrenzte Wartezeiten zulässt. Diese Muster sind typisch:
- HTTP-Aufrufe ohne Versuchszähler oder ohne propagated CancellationToken
- SQL-Befehle, die hinter Locks warten oder lange Abfragen ohne klare Operatorgrenzen
- Hintergrundaufgaben ohne maximale Laufzeit und ohne Heartbeat-Mechanismus
- Integrationen, die bei externen Ausfällen blockieren, während das System weiterhin versucht, die Anfrage zu wiederholen
Genau deshalb wirkt Prozess-Recycling kurzfristig: Es beendet die blockierten Anfragen und setzt Ressourcen frei – behebt aber nicht die eigentliche Ursache.
Stabilität durch Zeitbudgets, Fallbacks und Abbruchregeln
Viele Teams scheuen Zeitlimits, weil sie zunächst mehr Fehler melden. Doch diese Beobachtung führt oft in die falsche Richtung.
Zeitlimits schaffen keine Fragilität – sie offenbaren bestehende Schwächen: fehlende Cancellation-Propagation, unkontrollierte Retry-Kaskaden und das Fehlen alternativer Pfade, wenn das Budget ausgeschöpft ist.
Definieren Sie für jede Anfrage oder jeden Job-Typ drei zentrale Parameter:
- Gesamtbudget: Die maximale Zeit, die das System auf eine Antwort warten darf, bevor es einen alternativen Pfad wählt
- Versuchsbudget: Die maximale Zeit, die ein einzelner Abhängigkeitsaufruf Ressourcen halten darf
- Abbruchregeln: Klare Handlungsanweisungen, wann die Arbeit eingestellt, isoliert oder eskaliert wird – statt blind zu wiederholen
Praktische Fallback-Strategien, die in der Produktion funktionieren:
- Klare Fehlerantworten senden und die Arbeit einstellen
- Gecachte oder veraltete Daten ausliefern
- Arbeit in eine Warteschlange stellen und sofort antworten
- Teilantworten liefern, wo dies sicher ist
Wenn Ihre einzige Reaktion auf ein Zeitlimit „weiter warten“ ist, garantieren Sie damit, dass Abhängigkeitsprobleme zu Ausfällen werden.
Ein praxistaugliches Zeitlimit-Matrix-Beispiel
Perfekte Zahlen sind nicht das Ziel – konsistente Budgetregeln und klare Operatoranweisungen schon. Beginnen Sie mit einem Gesamtbudget und verteilen Sie kleinere Budgets innerhalb dieses Rahmens.
Konkrete Ausgangswerte für den Einstieg:
Benutzeranfragen (Web)
- Gesamtbudget: 500 Millisekunden
- HTTP-Aufruf-Versuch: 200 Millisekunden
- SQL-Abfrage-Versuch: 150 Millisekunden
- Versuchsanzahl: 2
- Fallback bei Budgetüberschreitung: Gecachte Antwort oder sofortige Fehlermeldung
Hintergrundjobs
- Maximale Laufzeit: 30 Sekunden
- Heartbeat-Intervall: Alle 5 Sekunden
- Abbruch bei Heartbeat-Verlust: Job als fehlgeschlagen markieren
Externe APIs
- Zeitlimit pro Aufruf: 1 Sekunde
- Retries: Maximal 3 Versuche
- Fallback bei allen Zeitüberschreitungen: Warteschlangenpfad aktivieren
Wichtig: Dokumentieren Sie diese Regeln zentral und stellen Sie sicher, dass alle Teammitglieder die Logik verstehen und anwenden. Nur so vermeiden Sie, dass Zeitlimits zu neuen, unerwarteten Problemen führen.
Die Einführung von Zeitlimits ist kein einmaliger Akt, sondern ein kontinuierlicher Prozess. Beginnen Sie mit konservativen Werten, überwachen Sie die Auswirkungen und passen Sie die Budgets schrittweise an die tatsächlichen Anforderungen an. So schaffen Sie stabile .NET-Systeme, die auch unter Last zuverlässig funktionieren.
KI-Zusammenfassung
ASP.NET projelerinizde sonsuz askı sorunlarını ve kapasite tıkanıklıklarını önlemek için zaman limitleri belirleme, iptal sinyallerini yayma ve yedek yollar oluşturma yöntemlerini keşfedin.