title: Shutting down Orleans
This document explains how to gracefully shutdown an Orleans silo before application exit, first as a Console app, and then as a Docker container app.
Graceful shutdown - Console app
The following code shows how to gracefully shutdown an Orleans silo console app in response to the user pressing Ctrl+C, which generates the Console.CancelkeyPress event.
Normally when that event handler returns, the application will exit immediately, causing a catastrophic Orleans silo crash and loss of in-memory state.
But in the sample code below, we set a.Cancel = true; to prevent the application closing before the Orleans silo has completed its graceful shutdown.
using Microsoft.Extensions.Logging;using Orleans.Configuration;using Orleans.Hosting;using System;using System.Net;using System.Threading;using System.Threading.Tasks;namespace MySiloHost {class Program {static readonly ManualResetEvent _siloStopped = new ManualResetEvent(false);static ISiloHost silo;static bool siloStopping = false;static readonly object syncLock = new object();static void Main(string[] args) {SetupApplicationShutdown();silo = CreateSilo();silo.StartAsync().Wait();/// Wait for the silo to completely shutdown before exiting._siloStopped.WaitOne();}static void SetupApplicationShutdown() {/// Capture the user pressing Ctrl+CConsole.CancelKeyPress += (s, a) => {/// Prevent the application from crashing ungracefully.a.Cancel = true;/// Don't allow the following code to repeat if the user presses Ctrl+C repeatedly.lock (syncLock) {if (!siloStopping) {siloStopping = true;Task.Run(StopSilo).Ignore();}}/// Event handler execution exits immediately, leaving the silo shutdown running on a background thread,/// but the app doesn't crash because a.Cancel has been set = true};}static ISiloHost CreateSilo() {return new SiloHostBuilder().Configure(options => options.ClusterId = "MyTestCluster").UseDevelopmentClustering(options => options.PrimarySiloEndpoint = new IPEndPoint(IPAddress.Loopback, 11111)).ConfigureLogging(b => b.SetMinimumLevel(LogLevel.Debug).AddConsole()).Build();}static async Task StopSilo() {await silo.StopAsync();_siloStopped.Set();}}}
Of course, there are many other ways of achieving the same goal.
Below is shown a way, popular online, and misleading, that DOES NOT work properly. It does not work because it sets up a race condition between two methods trying to exit first: the Console.CancelKeyPress event handler method, and the static void Main(string[] args) method.
When the event handler method finishes first, which happens at least half the time, the application will hang instead of exiting smoothly.
class Program {static readonly ManualResetEvent _siloStopped = new ManualResetEvent(false);static ISiloHost silo;static bool siloStopping = false;static readonly object syncLock = new object();static void Main(string[] args) {Console.CancelKeyPress += (s, a) => {Task.Run(StopSilo);/// Wait for the silo to completely shutdown before exiting._siloStopped.WaitOne();/// Now race to finish ... who will finish first?/// If I finish first, the application will hang! :(};silo = CreateSilo();silo.StartAsync().Wait();/// Wait for the silo to completely shutdown before exiting._siloStopped.WaitOne();/// Now race to finish ... who will finish first?}static async Task StopSilo() {await silo.StopAsync();_siloStopped.Set();}}
Graceful shutdown - Docker app
To be completed.
