[Checkins] SVN: zc.resumelb/trunk/src/zc/resumelb/ Added graceful worker shutdown on SIGTERM.
jim
cvs-admin at zope.org
Wed Mar 21 19:56:52 UTC 2012
Log message for revision 124661:
Added graceful worker shutdown on SIGTERM.
Changed:
U zc.resumelb/trunk/src/zc/resumelb/README.txt
U zc.resumelb/trunk/src/zc/resumelb/tests.py
U zc.resumelb/trunk/src/zc/resumelb/worker.py
U zc.resumelb/trunk/src/zc/resumelb/worker.test
U zc.resumelb/trunk/src/zc/resumelb/zk.py
U zc.resumelb/trunk/src/zc/resumelb/zk.test
-=-
Modified: zc.resumelb/trunk/src/zc/resumelb/README.txt
===================================================================
--- zc.resumelb/trunk/src/zc/resumelb/README.txt 2012-03-21 19:55:46 UTC (rev 124660)
+++ zc.resumelb/trunk/src/zc/resumelb/README.txt 2012-03-21 19:56:48 UTC (rev 124661)
@@ -243,6 +243,8 @@
0.1.1 (2012-03-??)
------------------
+- Added graceful worker shutdown on SIGTERM.
+
- Updated the API for application trace logging to match that of
zc.zservertracelog, mainly to get database logging for ZTK
applications.
Modified: zc.resumelb/trunk/src/zc/resumelb/tests.py
===================================================================
--- zc.resumelb/trunk/src/zc/resumelb/tests.py 2012-03-21 19:55:46 UTC (rev 124660)
+++ zc.resumelb/trunk/src/zc/resumelb/tests.py 2012-03-21 19:56:48 UTC (rev 124661)
@@ -18,6 +18,7 @@
import manuel.capture
import manuel.doctest
import manuel.testing
+import mock
import os
import re
import time
@@ -77,6 +78,7 @@
def setUp(test):
zope.testing.setupstack.setUpDirectory(test)
+ zope.testing.setupstack.context_manager(test, mock.patch('gevent.signal'))
global pid
pid = 6115
test.globs['wait'] = zope.testing.wait.Wait(getsleep=lambda : gevent.sleep)
Modified: zc.resumelb/trunk/src/zc/resumelb/worker.py
===================================================================
--- zc.resumelb/trunk/src/zc/resumelb/worker.py 2012-03-21 19:55:46 UTC (rev 124660)
+++ zc.resumelb/trunk/src/zc/resumelb/worker.py 2012-03-21 19:56:48 UTC (rev 124661)
@@ -136,6 +136,15 @@
if hasattr(self, 'threadpool'):
self.threadpool.kill()
+ def shutdown(self):
+ self.server.close()
+ while 1:
+ if [conn for conn in self.connections if conn.readers]:
+ gevent.sleep(.01)
+ else:
+ break
+ self.stop()
+
def handle_connection(self, sock, addr):
try:
conn = zc.resumelb.util.Worker()
Modified: zc.resumelb/trunk/src/zc/resumelb/worker.test
===================================================================
--- zc.resumelb/trunk/src/zc/resumelb/worker.test 2012-03-21 19:55:46 UTC (rev 124660)
+++ zc.resumelb/trunk/src/zc/resumelb/worker.test 2012-03-21 19:56:48 UTC (rev 124661)
@@ -341,6 +341,30 @@
True
+Gracefull shutdown
+==================
+
+Workers have a shutdown method that stops accepting and waits for
+outstanding requests to be completed. We have some outstating
+requests. Let's shutdown:
+
+ >>> shutdown_greenlet = gevent.spawn(worker.shutdown)
+
+We used a greenlet, because we don't want to block.
+
+ >>> shutdown_greenlet.join(.01)
+ >>> shutdown_greenlet.ready()
+ False
+
+We can't connect to the worker anymore:
+
+ >>> gevent.socket.create_connection(worker.addr)
+ Traceback (most recent call last):
+ ...
+ error: [Errno 111] Connection refused
+
+If we finish the outstanding requests, the worker will finish shutting down:
+
>>> write_message(worker_socket, 3, '')
>>> print_response(worker_socket, 3) # doctest: +NORMALIZE_WHITESPACE
3 200 OK
@@ -365,10 +389,10 @@
6115 10000 44bd0dbf8208fea52dc6180376d14798b86847bd
<BLANKLINE>
-Cleanup:
+ >>> shutdown_greenlet.join(.1)
+ >>> shutdown_greenlet.ready()
+ True
- >>> worker.stop()
-
Saving and loading resumes
--------------------------
Modified: zc.resumelb/trunk/src/zc/resumelb/zk.py
===================================================================
--- zc.resumelb/trunk/src/zc/resumelb/zk.py 2012-03-21 19:55:46 UTC (rev 124660)
+++ zc.resumelb/trunk/src/zc/resumelb/zk.py 2012-03-21 19:56:48 UTC (rev 124661)
@@ -18,6 +18,7 @@
import logging
import os
import re
+import signal
import sys
import zc.parse_addr
import zc.zk
@@ -60,6 +61,12 @@
worker.zk = zk
worker.__zksettings = settings
+ def shutdown():
+ zk.close()
+ worker.shutdown()
+
+ gevent.signal(signal.SIGTERM, shutdown)
+
if run:
try:
worker.server.serve_forever()
Modified: zc.resumelb/trunk/src/zc/resumelb/zk.test
===================================================================
--- zc.resumelb/trunk/src/zc/resumelb/zk.test 2012-03-21 19:55:46 UTC (rev 124660)
+++ zc.resumelb/trunk/src/zc/resumelb/zk.test 2012-03-21 19:56:48 UTC (rev 124661)
@@ -101,9 +101,32 @@
>>> bool(meta['ephemeralOwner'])
True
+Shutdown
+--------
+
+A shutdown signal handler is registered. We can call it to shut the
+worker down:
+
+ >>> import gevent, signal
+ >>> gevent.signal.call_args[0][0] == signal.SIGTERM
+ True
+
+ >>> gevent.signal.call_args[0][1]()
+ >>> gevent.sleep(.01)
+ >>> zk.get_children('/test/lb/workers/providers')
+ []
+
+ >>> gevent.socket.create_connection(worker.addr)
+ Traceback (most recent call last):
+ ...
+ error: [Errno 111] Connection refused
+
+
+Variations
+----------
+
Let's try again, but this time, don't set up logging:
- >>> worker.stop(); worker.zk.close()
>>> with mock.patch('ZConfig.configureLoggers') as configureLoggers:
... with mock.patch('logging.basicConfig') as basicConfig:
... worker = zc.resumelb.zk.worker(
More information about the checkins
mailing list