Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP

Loading…

Better shutdown #1

Merged
merged 6 commits into from

2 participants

@bobrik

Added ability to pass killer funtion to init.simple and init.stop.

  • init.hardKiller(delay = 2000) — old and default behaviour, TERM, INT, QUIT and KILL signals with 2s delay.
  • init.softKiller(delay = 2000) — new behaviour, TERM signal with 2s delay until process die.

It helps me to shutdown heavy server with no waste in databases.

There maybe stupid english in docs, comment with your corrections, I'll fix that.

p.s.: This is REALLY helpful, trust me.

@frodwith
Owner

Just say "killer defaults to hardKiller(2000)" in the docs for stop, and document hardKiller and
softKiller in a new section called "shutdown functions".

@bobrik

now ready to merge

@frodwith frodwith merged commit 4ed9859 into frodwith:master
@bobrik

Please leave a comment here, when it will go to npm, I'll remove symlinks from my version. Thanks for approving.

@frodwith
Owner

it's on npm now, thanks for the contribution :)

Glad you're finding it useful!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Commits on Oct 3, 2011
  1. @bobrik
  2. @bobrik

    Added killers to readme

    bobrik authored
Commits on Oct 4, 2011
  1. @bobrik

    only one SIGTERM in softKiller

    bobrik authored
  2. @bobrik
  3. @bobrik

    done with docs

    bobrik authored
  4. @bobrik

    fixed typo

    bobrik authored
This page is out of date. Refresh to see the latest.
Showing with 62 additions and 19 deletions.
  1. +26 −4 README.markdown
  2. +36 −15 init.coffee
View
30 README.markdown
@@ -57,11 +57,10 @@ about to daemonize). 'pid' will be the id of the running process, and
A function to be called if the start action cannot be performed. Error will be
some sort of stringifiable error object. Defaults to init.startFailed.
-### init.stop(pidfile, cb)
+### init.stop(pidfile, cb, killer)
-Sends your service TERM, INT, QUIT, in that order (with 2 second delays) and
-then KILL until the process is no longer running, then calls cb (defaults to
-init.stopped). If the process was running, cb's first argument will be true.
+Stops your service with one of shutdown functions. Default is
+`init.hardKiller(2000)`, but you may pass your own.
### init.status(pidfile, cb)
@@ -80,11 +79,34 @@ following keyword arguments:
#### logfile
As in init.start()
+### killer
+As in init.stop()
+
#### command
A string on which to dispatch. Defaults to your program's first argument
(process.argv[2]). Recognized actions are "start", "stop", "restart",
"try-restart", "force-reload", and "status".
+#### killer
+As in init.stop()
+
+Shutdown functions
+-----------------
+
+### init.hardKiller(delay = 2000)
+
+Sends your service TERM, INT, QUIT, in that order (with 2000 ms delays) and
+then KILL until the process is no longer running, then calls cb (defaults to
+init.stopped). If the process was running, cb's first argument will be true.
+This is default shutdown function.
+
+### init.softKiller(delay = 2000)
+
+Sends your service TERM and wait until it die with 2000 ms delays. This is
+preferred choice for graceful shutdown. Your service may decide when it want
+to stop with no data loss.
+
+
Default Actions
---------------
These functions are the defaults for various callbacks, but you can call them
View
51 init.coffee
@@ -68,32 +68,53 @@ exports.stopped = (killed) ->
console.log 'Not running.'
process.exit 0
-exports.stop = (pidfile, cb = exports.stopped) ->
+exports.hardKiller = (timeout = 2000) ->
+ (pid, cb) ->
+ signals = ['TERM', 'INT', 'QUIT', 'KILL']
+ tryKill = ->
+ sig = "SIG#{ signals[0] }"
+ try
+ # throws when the process no longer exists
+ process.kill pid, sig
+ signals.shift() if signals.length > 1
+ setTimeout (-> tryKill sig), timeout
+ catch e
+ cb(signals.length < 4)
+ tryKill()
+
+exports.softKiller = (timeout = 2000) ->
+ (pid, cb) ->
+ sig = "SIGTERM"
+ tryKill = ->
+ try
+ # throws when the process no longer exists
+ process.kill pid, sig
+ console.log "Waiting for pid " + pid
+ sig = 0 if sig != 0
+ first = false
+ setTimeout tryKill, timeout
+ catch e
+ cb(sig == 0)
+ tryKill()
+
+exports.stop = (pidfile, cb = exports.stopped, killer = exports.hardKiller(2000)) ->
exports.status pidfile, ({pid}) ->
if pid
- signals = ['TERM', 'INT', 'QUIT', 'KILL']
- tryKill = ->
- sig = "SIG#{ signals[0] }"
- try
- # throws when the process no longer exists
- process.kill pid, sig
- signals.shift() if signals.length > 1
- setTimeout (-> tryKill sig), 2000
- catch e
- fs.unlink pidfile, -> cb(signals.length < 4)
- tryKill()
+ killer pid, (killed) ->
+ fs.unlink pidfile, -> cb(killed)
else
cb false
-exports.simple = ({pidfile, logfile, command, run}) ->
+exports.simple = ({pidfile, logfile, command, run, killer}) ->
command or= process.argv[2]
+ killer or= null
start = -> exports.start { pidfile, logfile, run }
switch command
when 'start' then start()
- when 'stop' then exports.stop pidfile
+ when 'stop' then exports.stop pidfile, null, killer
when 'status' then exports.status pidfile
when 'restart', 'force-reload'
- exports.stop pidfile, start
+ exports.stop pidfile, start, killer
when 'try-restart'
exports.stop pidfile, (killed) ->
if killed
Something went wrong with that request. Please try again.