|  |  | @ -5,6 +5,9 @@ from __future__ import (absolute_import, division, print_function, | 
			
		
	
		
		
			
				
					
					|  |  |  | import os |  |  |  | import os | 
			
		
	
		
		
			
				
					
					|  |  |  | from datetime import timedelta |  |  |  | from datetime import timedelta | 
			
		
	
		
		
			
				
					
					|  |  |  | from time import sleep |  |  |  | from time import sleep | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | import signal | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | import time | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | from multiprocessing import Process | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  | from tests import RQTestCase, slow |  |  |  | from tests import RQTestCase, slow | 
			
		
	
		
		
			
				
					
					|  |  |  | from tests.fixtures import (create_file, create_file_after_timeout, |  |  |  | from tests.fixtures import (create_file, create_file_after_timeout, | 
			
		
	
	
		
		
			
				
					|  |  | @ -468,3 +471,74 @@ class TestWorker(RQTestCase): | 
			
		
	
		
		
			
				
					
					|  |  |  |         worker = Worker(queue, connection=self.testconn) |  |  |  |         worker = Worker(queue, connection=self.testconn) | 
			
		
	
		
		
			
				
					
					|  |  |  |         worker.work(burst=True) |  |  |  |         worker.work(burst=True) | 
			
		
	
		
		
			
				
					
					|  |  |  |         self.assertEqual(self.testconn.zcard(registry.key), 0) |  |  |  |         self.assertEqual(self.testconn.zcard(registry.key), 0) | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | def kill_worker(pid, double_kill): | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |     # wait for the worker to be started over on the main process | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |     time.sleep(0.5) | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |     os.kill(pid, signal.SIGTERM) | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |     if double_kill: | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |         # give the worker time to switch signal handler | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |         time.sleep(0.5) | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |         os.kill(pid, signal.SIGTERM) | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | class TestWorkerShutdown(RQTestCase): | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |     def setUp(self): | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |         # we want tests to fail if signal are ignored and the work remain running, | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |         # so set a signal to kill them after 5 seconds | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |         signal.signal(signal.SIGALRM, self._timeout) | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |         signal.alarm(5) | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |     def _timeout(self, signal, frame): | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |         raise AssertionError("test still running after 5 seconds, " | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |                              "likely the worker wasn't shutdown correctly") | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |     @slow | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |     def test_idle_worker_warm_shutdown(self): | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |         """worker with no ongoing job receiving single SIGTERM signal and shutting down""" | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |         w = Worker('foo') | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |         self.assertFalse(w._stop_requested) | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |         p = Process(target=kill_worker, args=(os.getpid(), False)) | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |         p.start() | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |         w.work() | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |         p.join(1) | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |         self.assertFalse(w._stop_requested) | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |     @slow | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |     def test_working_worker_warm_shutdown(self): | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |         """worker with an ongoing job receiving single SIGTERM signal, allowing job to finish then shutting down""" | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |         fooq = Queue('foo') | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |         w = Worker(fooq) | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |         sentinel_file = '/tmp/.rq_sentinel_warm' | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |         fooq.enqueue(create_file_after_timeout, sentinel_file, 2) | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |         self.assertFalse(w._stop_requested) | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |         p = Process(target=kill_worker, args=(os.getpid(), False)) | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |         p.start() | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |         w.work() | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |         p.join(2) | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |         self.assertTrue(w._stop_requested) | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |         self.assertTrue(os.path.exists(sentinel_file)) | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |     @slow | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |     def test_working_worker_cold_shutdown(self): | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |         """worker with an ongoing job receiving double SIGTERM signal and shutting down immediately""" | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |         fooq = Queue('foo') | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |         w = Worker(fooq) | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |         sentinel_file = '/tmp/.rq_sentinel_cold' | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |         fooq.enqueue(create_file_after_timeout, sentinel_file, 2) | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |         self.assertFalse(w._stop_requested) | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |         p = Process(target=kill_worker, args=(os.getpid(), True)) | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |         p.start() | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |         self.assertRaises(SystemExit, w.work) | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |         p.join(1) | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |         self.assertTrue(w._stop_requested) | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |         self.assertFalse(os.path.exists(sentinel_file)) | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | 
 | 
			
		
	
	
		
		
			
				
					|  |  | 
 |