changeset 2170:4652a112a4ba

mod_graceful_shutdown: Experiment in improving the shutdown experience
author Kim Alvefur <zash@zash.se>
date Thu, 05 May 2016 15:43:01 +0200
parents 9fa588babbba
children 85d88ed4f2a2
files mod_graceful_shutdown/README.markdown mod_graceful_shutdown/mod_graceful_shutdown.lua
diffstat 2 files changed, 68 insertions(+), 0 deletions(-) [+]
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mod_graceful_shutdown/README.markdown	Thu May 05 15:43:01 2016 +0200
@@ -0,0 +1,26 @@
+This module is an experiment about a more graceful shutdown process.
+
+Why
+===
+
+When shutting down, a number of sessions, connections and other things
+are teared down. Due to all these things happening very quickly,
+sometimes eg client unavailable notifications don't make it to all
+remote contacts because the server-to-server connections are teared down
+just after.
+
+How
+===
+
+This module works by breaking the shutdown process into separate steps
+with a brief pause between them.
+
+It goes something like this
+
+1.  Stop accepting new client connections.
+2.  Close all client connections.
+3.  Fire event for everything else.
+4.  Tell `net.server` to quit the main loop.
+5.  ???
+6.  Still here? Kill itself.
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mod_graceful_shutdown/mod_graceful_shutdown.lua	Thu May 05 15:43:01 2016 +0200
@@ -0,0 +1,42 @@
+-- luacheck: ignore 122/prosody 113/prosody
+
+local timer = require "util.timer";
+local portman = require "core.portmanager";
+local server = require "net.server";
+
+module:set_global();
+local orig_shutdown = prosody.shutdown;
+
+local pause = module:get_option_number("shutdown_pause", 1);
+
+function module.unload()
+	prosody.shutdown = orig_shutdown;
+end
+
+prosody.shutdown = coroutine.wrap(function (reason, code)
+	prosody.shutdown_reason = reason;
+	prosody.shutdown_code = code;
+	timer.add_task(pause, prosody.shutdown);
+	coroutine.yield(true, "shutdown initiated");
+	-- Close c2s ports, stop accepting new connections
+	portman.deactivate("c2s");
+	-- Close all c2s sessions
+	for _, sess in pairs(prosody.full_sessions) do
+		sess:close{ condition = "system-shutdown", text = reason }
+	end
+	-- Wait for notifications to be sent
+	coroutine.yield(pause);
+	-- Event for everything else to shut down
+	prosody.events.fire_event("server-stopping", {
+		reason = reason;
+		code = code;
+	});
+	-- And wait
+	coroutine.yield(pause);
+	-- And stop main event loop
+	server.setquitting(true);
+	-- And wait for death
+	coroutine.yield(pause * 3);
+	-- you came back? die zombie!
+	os.exit(1);
+end);