How to correct shutdown Yesod application?
Calling in route handler liftIO (exitSuccess) just raises Internal Server erorr and server continue working.
I found liftIO (raiseSignal sigTERM) that terminates process itself, but this method is not crossplatform and I not sure about correct releasing used resources by such method.
Is any correct method gracefully shutdown Yesod application?
Best regards,
Dmitriy.
So the question is how do we signal Warp to shutdown. One possibility
would be to kill its containing thread, though that will interrupt any
active requests. I think a better idea is to have an (optional) IORef
passed to Warp that will indicate whether it should accept new
connections. Once the IORef gets switched to False, Warp will exit its
loop, returning control to your program. You'll then be responsible
for any cleanup you want to do, and should probably also threadDelay
for about 5 seconds to give active connections a chance to finish.
We could then use this IORef to handle signals (as is necessary on Heroku).
Any thoughts on this?
Michael
2011/7/21 Dmitriy Nikitinskiy <nikit...@gmail.com>:
It's ok for me, using IORef running flag.
Maybe add such IORef into Wai.Handler.Warp.Settings ?
Or as variant maybe add ResponseControl to Response data to controlling Wai handler (not only running-flag)?
For example:
data Response = ... | ResponseControl WaiControlCommand (Maybe ResponseType {- for contnent with control -})
data WaiControlCommand = ...
Best regards,
Dmitriy
Warp *could* handle this on its own, but:
1) Signals only exist on POSIX systems.
2) It wouldn't be appropriate for Warp to install a process-wide
signal handler on its own by default. I have no problem providing
helper functions to make this easier on people, but the default should
be to not affect the rest of the process.
> I am wondering what Dmitry's use case is, because it doesn't seem like a
> frequent case that one would want to shutdown the application from inside
> it! It seems more appropriate to send a signal from the outside, at which
> point Warp will stop accepting new connections and will return from its
> loop. Could it return thread ids to wait on or is there too much overhead
> associated with that?
I can imagine having a "shutdown" button on an admin panel. Especially
if you have your application process being monitored, it could be
convenient for some use cases.
What do you mean by return thread ids?
> One ideal usage case for Warp and Signal handlers would be to send a signal
> that would allow one to switch to a new version of the application with zero
> downtime. But there might be other approaches to achieving this that work
> better.
I think the approach just mentioned would achieve this: sending the
signal will shut down the main thread and release the main listening
thread, still letting active connections finish. So starting up a new
process right after sending the signal should work.
It's possible to do this, but I'd rather not. I don't want to start
confusing the request/response setup of WAI with control messages.
Michael
I think we should defer true zero downtime to a load balancer outside
of our process. As you're pointing out, there's no way to have true
zero downtime with a single process like this.
> I am still confused about the recommended approach- someone has to catch
> signals- is Yesod going to do that by default now?
The idea is that we'd set up a signal handler before calling Warp. In
pseudo-code:
flag <- newIORef True
installHandler sigINT (writeIORef flag False)
runWarpWithFlag flag
cleanup
Warp will continue running until that flag is set to False, which will
happen when the signal gets sent.
Michael
There could also be an exception
data StopWarp = StopWarp deriving (Show, Typeable)
instance Exception StopWarp where
that could be thrown by the application. This would be only exception
to the exception handler (pun intended) and would be equivalent to
setting the flag to False, just more convenient.
However, I don't know if it would be possible to do away with the flag
and use throwTo. It may be simpler to keep it.
Cheers,
--
Felipe.
Interesting idea, it could be a very good approach. The only possible
problem is that Yesod intercepts all exceptions and returns 500 pages,
so Yesod would need to have special handling[1] for this as well. In
any event, we'd need to have the flag at the Warp level, if only
internally, so that a child thread could notify the main thread, so I
don't see any performance reason for this change.
Michael
[1] Exceptional exceptions.
OK, scratch everything I said before, it was a bad approach. An IORef
flag would indeed work, but only *after* one more connection was made,
since the main Warp loop is still stuck in an accept call. Instead, it
seems that we have a much simpler approach available that works with
Warp as it is right now. Here's an example of interrupt signal
handling[1], though it should work well for Dmitriy's case as well I
think.
Michael