Come sappiamo, un'applicazione web può subire uno shutdown per i motivi più disparati: magari stiamo facendo uno scale down dei nostri server sul cloud perché il traffico è diminuito, oppure abbiamo effettuato il deploy di una nuova versione del codice, o ancora IIS ha magari bisogno di effettuare il restart dell'application pool (e abbiamo utilizzato l'hosting model InProcess).
In generale non dobbiamo preoccuparci più di tanto di queste evenienze, nel caso peggiore un eventuale utente collegato riceverà un errore sulla richiesta. Tuttavia possono esserci casistiche che vanno gestite in maniela puntuale, soprattutto nel caso di procesi in esecuzione in background, per evitare pericolosi bug o perdite di dati.
Prendiamo come esempio il sistema di audit che abbiamo visto nel precedente script (https://www.aspitalia.com/script/1367/Effettuare-Tracing-Asincrono-Chiamate-Applicazione-ASP.NET-Core.aspx). Come sappiamo, questo sistema si basa sul mantenimento di una coda locale, che viene poi salvata su database al raggiungimento di una certa soglia.
Ovviamente, se l'applicazione dovesse terminare prima del raggiungimento di questo limite, il batch corrente verrebbe irrimediabilmente perso. Come possiamo ovviare a questo problema?
La soluzione è quella di aggiungere alla nostra classe AuditQueue una dipendenza da IHostApplicationLifetime:
internal class AuditQueue { private BatchBlock<AuditTrace> _queue; public AuditQueue(IAuditRepository auditRepository, IHostApplicationLifetime application) { var batchSize = 50; _queue = new BatchBlock<AuditTrace>(batchSize); var actionBlock = new ActionBlock<AuditTrace[]>(async traces => { await auditRepository.SaveTracesAsync(traces); }); _queue.LinkTo(actionBlock); // in caso di shutdown forziamo il salvataggio di quanto accumulato application.ApplicationStopping.Register(() => _queue.Complete()); } public Task SendAsync(AuditTrace trace) { return _queue.SendAsync(trace); } }
Come si vede la modifica è tutto sommato piuttosto semplice: questa interfaccia espone una serie di eventi del ciclo di vita dell'applicazione, e quello a cui siamo interessati è denominato ApplicationStopping. Non dobbiamo far altro che registrare un handler che, alla richiesta di shutdown, forzi il completamento della coda, come nel codice in alto.
Tipicamente l'applicazione deve terminare entro 5 secondi dalla richiesta di chiusura, quindi dobbiamo essere sicuri che il nostro codice possa essere completato in questo intervallo temporale.
In casi particolari, possiamo incrementare questo timeout in fase di startup, tramite il metodo UseShutdownTimeout:
public static IHostBuilder CreateHostBuilder(string[] args) => Host.CreateDefaultBuilder(args) .ConfigureWebHostDefaults(webBuilder => { webBuilder.UseShutdownTimeout(TimeSpan.FromSeconds(10)); webBuilder.UseStartup<Startup>(); });
Commenti
Per inserire un commento, devi avere un account.
Fai il login e torna a questa pagina, oppure registrati alla nostra community.
Approfondimenti
Effettuare il binding di date in Blazor
Utilizzare gli snapshot con Azure File shares
Inizializzare i container in Azure Container Apps
Eseguire operazioni con timeout in React
Supporto ai tipi DateOnly e TimeOnly in Entity Framework Core
Le novità di Angular: i miglioramenti alla CLI
Visualizzare le change sul plan di Terraform tramite le GitHub Actions
Evitare la command injection in un workflow di GitHub
Generare file per il download da Blazor WebAssembly
Miglioramenti agli screen reader e al contrasto in Angular
Utilizzare le collection expression in C#
Verificare la provenienza di un commit tramite le GitHub Actions
I più letti di oggi
- Microsoft annuncia Windows "TrustBridge"
- ecco tutte le novità pubblicate sui nostri siti questa settimana: https://aspit.co/wkly buon week-end!
- Estrarre dati randomici da una lista di oggetti in C#
- Usare il colore CSS per migliorare lo stile della pagina
- Una attached property per accedere a NavigationService da ViewModel in Silverlight 4.0