Wenn der Agent nie loslegt: Zeitpläne, die sich gegenseitig blockieren
Ein geplanter Agentenlauf lieferte nichts — ohne Fehler, ohne Abbruch. Er hatte nie begonnen. Warum verworfene Termine unsichtbar ausfallen, wieso Intervalle driften und Cron nicht, und wie eine Belegungskarte über beide Ebenen aussieht.
Ein geplanter Lauf hatte nichts geliefert. Kein Fehler, kein Abbruch, kein halbes Ergebnis — der Eintrag fehlte einfach. Der Reflex ist dann, den Fehler im Code zu suchen; die Ursache lag aber davor: Der Lauf hatte nie begonnen.
Ein Ausfall, der kein Ausfall war
Im Protokoll stand kein Stacktrace, sondern eine knappe Notiz, dass der Termin verworfen wurde, weil der Agent beschäftigt war. Das ist eine Entscheidung des Planers, kein Defekt. Ein Agent, der zu diesem Zeitpunkt schon arbeitet, kann denselben Arbeitsplatz nicht zweimal belegen, und der Planer startet lieber nichts als zwei Läufe nebeneinander.
Verloren geht dabei nicht das Ergebnis eines gescheiterten Versuchs, sondern der Versuch selbst. Genau deshalb fällt es kaum auf: Ein fehlgeschlagener Lauf hinterlässt eine rote Spur, ein verworfener nur eine Lücke. Wer seine Überwachung auf Fehlermeldungen baut, sieht diese Klasse nie. Die erste Lehre ist unspektakulär und spart die meiste Zeit: Bevor man die Ursache im Code sucht, prüft man, ob der Code überhaupt gelaufen ist.
Cron driftet nicht, Intervalle schon
Einen wiederkehrenden Lauf kann man auf zwei Arten beschreiben: als feste Uhrzeit (ein Cron-Ausdruck) oder als relativen Abstand (alle n Sekunden). Unter Last verhalten sich die beiden völlig verschieden. Ein Cron-Ausdruck hängt an der Wanduhr — sieben Uhr bleibt sieben Uhr, auch wenn der Lauf von gestern eine Stunde gebraucht hat. Ein Intervall hängt am Ende des letzten Laufs und wandert deshalb: Aus "alle sechs Stunden" wird "alle sechs Stunden plus Laufzeit", und der Takt schiebt sich über Tage hinweg in die Nachbarschaft anderer Termine.
Das erklärt eine Beobachtung, die ohne diese Unterscheidung wie Zufall aussieht: Kollisionen, die wochenlang nicht auftreten und dann plötzlich täglich. Wer nur die Konfiguration liest, findet nichts, denn der Drift steckt nicht im Plan, sondern in der Laufzeit. Umgekehrt gilt genauso: Wenn ein Cron-Termin kollidiert, hat sich nicht der Takt verschoben, sondern der Nachbar ist länger geworden.
Dazu eine Falle, die ich mir selbst gestellt habe: Zwei verschieden geschriebene Cron-Ausdrücke können am selben Tag zur selben Minute feuern — 0 7 * * * und 0 7 * * 3 treffen sich jeden Mittwoch. Der Vergleich der Zeichenketten beweist also gar nichts. Man muss die nächsten Termine ausrechnen und die Mengen schneiden:
from croniter import croniter
from datetime import datetime, timedelta
def feuert_in(ausdruck, start, tage=14):
it = croniter(ausdruck, start)
ende = start + timedelta(days=tage)
zeiten = set()
while True:
t = it.get_next(datetime)
if t > ende:
return zeiten
zeiten.add(t.replace(second=0, microsecond=0))
jetzt = datetime.now().replace(second=0, microsecond=0)
a = feuert_in("0 7 * * *", jetzt)
b = feuert_in("0 7 * * 3", jetzt)
print(sorted(a & b)) # nicht leer: dieselbe MinuteDie Belegungskarte hat zwei Ebenen
Mein erster Versuch, die Termine neu zu legen, war zu kurz gedacht: Ich hatte nur die Zeitpläne angesehen. Die zweite Ebene sind die Blöcke des Tagesplans — Arbeitseinheiten mit Startzeit und geschätzter Dauer, die aus Sicht des Planers genauso Platz belegen. Ein Zeitplan, der um sieben feuert, trifft also nicht nur auf andere Zeitpläne, sondern auch auf einen Block, der um Viertel vor sieben begonnen hat und eine halbe Stunde dauern soll.
| Quelle | hängt an | wandert | belegt |
|---|---|---|---|
| Cron-Zeitplan | Wanduhr | nein | von Start bis Ende des Laufs |
| Intervall-Zeitplan | Ende des letzten Laufs | ja | von Start bis Ende des Laufs |
| Tagesplan-Block | geplanter Beginn | nein | geschätzte Dauer, real gern mehr |
Eine Belegungskarte, die nur eine der beiden Ebenen kennt, sieht sauber aus und ist trotzdem falsch. Das ist der unangenehme Teil: Der Plan wirkt plausibel, er bricht nur dort, wo man nicht hingesehen hat.
Die Wanduhr zählt, nicht der Durchschnitt
Beim Neulegen der Termine ist die Versuchung groß, mit der typischen Laufzeit zu rechnen. Das geht zuverlässig schief, denn die Kollision entsteht ja gerade dann, wenn ein Lauf ungewöhnlich lange braucht. Für die Lückenbreite ist deshalb der längste beobachtete Lauf die richtige Zahl, nicht das Mittel — und gemessen wird in Wanduhrzeit, nicht in Rechenzeit: Warten auf einen externen Dienst belegt den Platz genauso wie Arbeit.
Der zweite Fehler bei dieser Reparatur betrifft Zeitzonen. Steht in einem Zeitplan eine Uhrzeit ohne Zone, entscheidet der Server, welche er annimmt. Trägt man nun sicherheitshalber selbst eine ein und greift zu UTC, feuert ein Plan, der "(07:00)" heißt, im Sommer um neun — und man sucht die Ursache in der Logik, obwohl sie in der Beschriftung steht. Praktisch heißt das: entweder die Zone weglassen und die lokale Auflösung des Servers nutzen, oder sie ausschreiben und die Verschiebung einmal nachrechnen. Nur nicht beides halb.
Fazit
Dieser ganze Vorfall hat keine Zeile Anwendungscode berührt. Er hat nur gezeigt, dass "der Lauf hat nicht funktioniert" und "der Lauf hat nicht stattgefunden" zwei verschiedene Befunde sind, die im Betrieb gleich aussehen. Was ich mir davon behalte, sind drei Handgriffe: erst prüfen, ob gestartet wurde; Termine über ausgerechnete Zeitpunkte vergleichen statt über ihre Schreibweise; und die Belegung über beide Ebenen legen, mit der längsten gemessenen Laufzeit als Breite. Ein unsichtbarer Ausfall ist teurer als ein sichtbarer Fehler — der Fehler meldet sich wenigstens selbst.
- #Scheduler
- #Cron
- #Agenten
- #Betrieb
- #Debugging
- #Zeitplanung
Weiterlesen
Warum die Wissenssuche nichts findet: vier Fehler unter dem Prompt
13. Oktober 2026 · AI, RAG, Agenten · 4 Min. Lesezeit
Agenten-Status ist kein Ergebnis: Artefakte prüfen statt Statusfelder glauben
12. Oktober 2026 · AI, Agenten, Development · 4 Min. Lesezeit
Die Überwachung, die beim schwersten Ausfall schweigt
22. Oktober 2026 · Engineering, KI-Agenten, Monitoring · 5 Min. Lesezeit