Hangfire in produzione: dashboard, retry e job ricorrenti spiegati bene
Hangfire in produzione: dashboard, retry e job ricorrenti spiegati bene
La dashboard di Hangfire non va mai esposta senza autenticazione in produzione: di default è raggiungibile da chiunque conosca l'URL, e mostra job, parametri, stack trace degli errori — informazioni interne che non dovrebbero essere pubbliche. La protezione minima corretta è un filtro di autorizzazione che verifichi una chiave/credenziale prima di servire la pagina, non l'omissione del link sperando che nessuno lo trovi (la dashboard è quasi sempre su /hangfire, un percorso prevedibile).
Perché la dashboard richiede attenzione dedicata
Hangfire registra i suoi endpoint (inclusa la dashboard) fuori dal normale flusso di autenticazione/autorizzazione dei controller applicativi — va configurata esplicitamente con un IDashboardAuthorizationFilter personalizzato, altrimenti il default permette l'accesso a chiunque in locale e, a seconda della configurazione, anche in produzione. Un pattern semplice ed efficace è un filtro che controlla un parametro di query o un header contro un valore segreto configurato lato server (lo stesso genere di "gate perimetrale" spesso già in uso per altri endpoint amministrativi):
public class HangfireAuthFilter : IDashboardAuthorizationFilter
{
private readonly string _expectedKey;
public HangfireAuthFilter(string expectedKey) => _expectedKey = expectedKey;
public bool Authorize(DashboardContext context)
{
var httpContext = context.GetHttpContext();
var providedKey = httpContext.Request.Query["key"].ToString();
return !string.IsNullOrEmpty(_expectedKey) && providedKey == _expectedKey;
}
}
Registrato con:
app.UseHangfireDashboard("/hangfire", new DashboardOptions
{
Authorization = new[] { new HangfireAuthFilter(hangfireKey) }
});
Un dettaglio da verificare sempre dopo il deploy: che la dashboard risponda 401 senza la chiave o con una chiave errata, e 200 solo con quella corretta — non basta che la pagina "sembri protetta", va testato esplicitamente con entrambi i casi.
Job ricorrenti: cosa verificare dopo ogni deploy
Un errore comune è dare per scontato che i job ricorrenti configurati nel codice sopravvivano automaticamente a un deploy. In realtà RecurringJob.AddOrUpdate(...) va eseguito ad ogni avvio dell'applicazione (tipicamente in startup) — se la registrazione viene rimossa o rinominata per errore in una nuova versione, il job scompare silenziosamente dalla dashboard senza nessun errore visibile, semplicemente non c'è più.
Checklist minima post-deploy:
Come funziona il retry, e perché non basta sempre
Hangfire ritenta automaticamente un job fallito con un numero di tentativi configurabile e un backoff crescente tra un tentativo e l'altro — di base è già attivo senza configurazione esplicita. Il punto da capire bene: il retry automatico protegge da fallimenti transitori (un timeout di rete momentaneo, un database temporaneamente occupato), non da errori di logica applicativa. Un job che fallisce per un bug nel codice fallirà allo stesso modo ad ogni tentativo, consumando solo tempo prima di finire nello stato "Failed" definitivo.
Per operazioni non critiche al successo immediato dell'azione principale (l'esempio più comune: invio email dopo un'operazione applicativa riuscita), un pattern più robusto del solo retry automatico è non far fallire l'operazione principale se l'invio fallisce — loggare l'errore e lasciare che sia il retry di Hangfire (per i job schedulati) o un controllo manuale successivo (per notifiche puntuali) a gestire il recupero, invece di far dipendere il successo dell'azione dell'utente dalla disponibilità di un servizio esterno come quello email.
Job ricorrenti vs job puntuali (fire-and-forget): quando usare quale
Due modelli diversi, spesso confusi:
Confonderli porta a un antipattern comune: registrare come job ricorrente qualcosa che dovrebbe essere un job puntuale (risultato: esecuzioni ripetute non necessarie), oppure il contrario (un controllo periodico implementato come tanti job puntuali sparsi, più difficile da monitorare in un unico posto nella dashboard).
Domande frequenti
La dashboard di Hangfire va esposta pubblicamente dietro la stessa autenticazione dell'app principale? Non necessariamente allo stesso modo — un filtro dedicato più semplice (una chiave condivisa, un IP allowlist) è spesso sufficiente e più facile da verificare rispetto a integrarla nel sistema di ruoli applicativo completo, soprattutto se la dashboard è usata solo da personale tecnico/amministrativo, non dagli utenti finali.
Cosa succede ai job in coda se il servizio si riavvia (es. dopo un deploy)? Con uno storage persistente (es. su database, non in-memory), i job in coda sopravvivono al riavvio: Hangfire li riprende alla ripartenza del worker. È un motivo in più per non usare lo storage in-memory in produzione, solo in sviluppo/test.
Come faccio a sapere se un job ricorrente ha smesso di essere eseguito per un bug silenzioso? Controllare regolarmente (o impostare un controllo automatico) il numero di esecuzioni completate con successo nel tempo atteso — se un job dovrebbe girare ogni ora e l'ultima esecuzione registrata è di giorni fa, il problema non è nella dashboard ma nel worker o nella registrazione del job stesso.
Serve preoccuparsi delle performance del database se Hangfire usa lo storage MySQL/MariaDB? Con volumi di job moderati (poche decine/centinaia al giorno) l'impatto è trascurabile. Con volumi molto più alti vale la pena monitorare la crescita delle tabelle di storage Hangfire e valutare una policy di retention per i job completati più vecchi, per evitare una crescita indefinita dello spazio occupato.