[Zope-Checkins] CVS: Zope/lib/python - Lifetime.py:1.1.2.1
Toby Dickenson
tdickenson@geminidataloggers.com
Thu, 10 Oct 2002 13:27:54 -0400
Update of /cvs-repository/Zope/lib/python
In directory cvs.zope.org:/tmp/cvs-serv27621/lib/python
Added Files:
Tag: toby-clean-shutdown-branch
Lifetime.py
Log Message:
draft implementation of the clean shutdown proposal. ZMI and signal-based shutdown methods now set a global variable. This global variable causes the main medusa loop to return. We then gradually shut down sockets, in the right order. If any clients are slowly downloading a file then they can cause the shutdown to be delayed by up to 30 seconds, to allow them time to complete. This is true unless the shutdown was triggered by SIGTERM - we need to shutdown fast because the whole machine might be going down, and we can expect a SIGKILL within a few seconds.
=== Added File Zope/lib/python/Lifetime.py ===
import sys, asyncore, time
_shutdown_phase = 0
_shutdown_timeout = 30 # seconds per phase
# The shutdown phase counts up from 0 to 4.
#
# 0 Not yet terminating. running in main loop
#
# 1 Loss of service is imminent. Prepare any front-end proxies for this happening
# by stopping any ICP servers, so that they can choose to send requests to other
# Zope servers in the cluster.
#
# 2 Stop accepting any new requests.
#
# 3 Wait for all old requests to have been processed
#
# 4 Already terminated
#
# It is up to individual socket handlers to implement these actions, by providing the
# 'clean_shutdown_control' method. This is called intermittantly during shutdown with
# two parameters; the current phase number, and the amount of time that it has currently
# been in that phase. This method should return true if it does not yet want shutdown to
# proceed to the next phase.
def shutdown(exit_code,fast = 0):
global _shutdown_phase
global _shutdown_timeout
if _shutdown_phase == 0:
# Thread safety? proably no need to care
sys.ZServerExitCode = exit_code
_shutdown_phase = 1
if fast:
# Someone wants us to shutdown fast. This is hooked into SIGTERM - so possibly
# the system is going down and we can expect a SIGKILL within a few seconds.
# Limit each shutdown phase to one second. This is fast enough, but still clean.
_shutdown_timeout = 1.0
def loop():
# Run the main loop until someone calls shutdown()
lifetime_loop()
# Gradually close sockets in the right order, while running a select
# loop to allow remaining requests to trickle away.
graceful_shutdown_loop()
def lifetime_loop():
# The main loop. Stay in here until we need to shutdown
map = asyncore.socket_map
timeout = 30.0
while map and _shutdown_phase == 0:
asyncore.poll(timeout, map)
def graceful_shutdown_loop():
# The shutdown loop. Allow various services to shutdown gradually.
global _shutdown_phase
timestamp = time.time()
timeout = 1.0
map = asyncore.socket_map
while map and _shutdown_phase < 4:
time_in_this_phase = time.time()-timestamp
veto = 0
for fd,obj in map.items():
try:
fn = getattr(obj,'clean_shutdown_control')
except AttributeError:
pass
else:
try:
veto = veto or fn(_shutdown_phase,time_in_this_phase)
except:
obj.handle_error()
if veto and time_in_this_phase<_shutdown_timeout:
# Any open socket handler can veto moving on to the next shutdown phase.
# (but not forever)
asyncore.poll(timeout, map)
else:
# No vetos? That is one step closer to shutting down
_shutdown_phase += 1
timestamp = time.time()