Blog

Hangfire-Job-Historie länger als 24 Stunden aufbewahren

Zwei der meistgelesenen Threads im Hangfire-Forum drehen sich um Job-Retention: „Wie konfiguriere ich die Aufbewahrungszeit von Jobs?" und „Fehlgeschlagene Jobs werden nie gelöscht." Zusammen wurden sie über 60.000 Mal aufgerufen. Wenn eines dieser Probleme bei dir aufgetaucht ist — erfolgreiche Jobs, die über Nacht verschwinden, oder 60.000 fehlgeschlagene Jobs, die sich über ein Jahr ansammeln — ist hier, was tatsächlich passiert und was man dagegen tun kann.

Was Hangfire standardmässig macht

Hangfire führt regelmässig einen Background-Sweep aus, der Job-Einträge aus dem Storage entfernt, sobald sie ein bestimmtes Alter überschreiten. Das genaue Standardverhalten hängt vom Storage-Provider und der Version ab, aber auf den meisten Setups ist es dasselbe: erfolgreiche Jobs werden nach etwa einem Tag entfernt, und der Sweep läuft kontinuierlich im Hintergrund. Das ist ein sinnvoller Default — ohne ihn wachsen die Job- und State-Tabellen unbegrenzt, und auf SQL Server wird das schnell zu Lock-Contention und langsamen Dashboard-Queries.

Der Haken: derselbe Sweep entfernt fehlgeschlagene Jobs nicht zuverlässig. Ein fehlgeschlagener Job bleibt im Storage, bis du ihn manuell im Dashboard löschst oder selbst einen Cleanup-Job schreibst. Über Monate hinweg heisst das eine stetige Ansammlung — wir haben Produktionsdatenbanken gesehen, in denen die Hangfire.State-Tabelle allein durch fehlgeschlagene Jobs mehrere Gigabyte gross geworden war.

Das Retention-Fenster erhöhen — und warum das nicht skaliert

Für erfolgreiche Jobs bietet dein Storage-Provider möglicherweise eine Konfigurationsoption (manchmal JobStorageOptions.JobExpirationTimeout, manchmal die eigenen Settings des Storages), um das Aufbewahrungsfenster zu erhöhen. Auf 30 Tage gesetzt bekommt man einen Monat Historie statt eines Tages. Soweit, so gut.

Der Haken ist, was mit den Storage-Tabellen passiert. Das Hangfire-Schema wurde nicht für langfristige Archivierung entworfen — die State-Tabelle ist besonders schreib- und indexlastig, und auf SQL Server dauern die SELECT TOP-Queries des Dashboards gegen eine Tabelle mit mehreren Millionen Zeilen irgendwann Sekunden statt Millisekunden. Ein GitHub-Issue (#1962) beschreibt, wie das Dashboard nach einigen Monaten mit erhöhter Retention unbrauchbar langsam wird. Man tauscht Historie gegen Dashboard-Performance — und innerhalb von Hangfire selbst gibt es keinen Mittelweg.

Für fehlgeschlagene Jobs ist es noch schlimmer — es gibt überhaupt keine eingebaute „auto-delete fehlgeschlagene Jobs älter als X"-Option. Man kann einen wiederkehrenden Job schreiben, der den Cleanup übernimmt, aber das ist eine selbst gebaute Lösung, keine Konfiguration.

Warum „alles in Hangfires eigenem Storage behalten" die falsche Antwort ist

Der Impuls ist, Retention so hoch wie möglich zu setzen und es dabei zu belassen. Das Problem ist, dass Hangfires Storage operational ist — es existiert, um die Queue, die Worker und das Dashboard anzutreiben, nicht als permanentes Audit-Log. In dem Moment, in dem man es als Historie-of-Record behandelt, gehen drei Dinge kaputt:

Wie ein separates Historie-Storage aussieht

Die saubere Antwort ist, aufzuhören, Hangfires Storage zu bitten, gleichzeitig die Live-Queue und das langfristige Archiv zu sein. Hangfire macht, was es gut kann — Jobs orchestrieren — und ein separates System streamt State-Changes heraus, sobald sie passieren, und bewahrt sie irgendwo auf, das nicht Hangfires Lifecycle teilt.

Genau diese Lücke schliesst QueueHawk: QueueHawk.Agent hängt sich in Hangfires OnStateElection / OnStateApplied-Filter ein und streamt jeden State-Change per HTTPS heraus, sobald er passiert. Die Historie lebt in einer eigenen Datenbank, mit 7 bis 365 Tagen Aufbewahrung je nach Plan, und übersteht Redeploys, Storage-Migrationen und vollständige Hangfire-Neustarts — weil sie nie in derselben Datenbank lag, die Hangfire sweeped. Das Hangfire-Schema bleibt schlank und das Dashboard schnell; der Audit-Trail lebt woanders.

Zusammenfassung

Siehe auch: warum Job-Historie auch nach jedem Deploy verschwindet (ein verwandtes, aber anderes Problem — dort geht's um Schema-Resets, hier um Retention), und wie QueueHawk mit dem eingebauten Hangfire-Dashboard vergleicht.

← Alle Artikel QueueHawk kostenlos testen →

Job-Historie behalten, die nicht abläuft

Kostenlos für eine Application, keine Kreditkarte nötig.

Kostenlos starten