|
|
@ -8,13 +8,16 @@ from time import sleep
|
|
|
|
import signal
|
|
|
|
import signal
|
|
|
|
import time
|
|
|
|
import time
|
|
|
|
from multiprocessing import Process
|
|
|
|
from multiprocessing import Process
|
|
|
|
|
|
|
|
import subprocess
|
|
|
|
|
|
|
|
|
|
|
|
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,
|
|
|
|
div_by_zero, do_nothing, say_hello, say_pid)
|
|
|
|
div_by_zero, do_nothing, say_hello, say_pid,
|
|
|
|
|
|
|
|
access_self)
|
|
|
|
from tests.helpers import strip_microseconds
|
|
|
|
from tests.helpers import strip_microseconds
|
|
|
|
|
|
|
|
|
|
|
|
from rq import get_failed_queue, Queue, SimpleWorker, Worker
|
|
|
|
from rq import (get_failed_queue, Queue, SimpleWorker, Worker,
|
|
|
|
|
|
|
|
get_current_connection)
|
|
|
|
from rq.compat import as_text, PY2
|
|
|
|
from rq.compat import as_text, PY2
|
|
|
|
from rq.job import Job, JobStatus
|
|
|
|
from rq.job import Job, JobStatus
|
|
|
|
from rq.registry import StartedJobRegistry
|
|
|
|
from rq.registry import StartedJobRegistry
|
|
|
@ -82,12 +85,16 @@ class TestWorker(RQTestCase):
|
|
|
|
"""Worker processes work, then quits."""
|
|
|
|
"""Worker processes work, then quits."""
|
|
|
|
fooq, barq = Queue('foo'), Queue('bar')
|
|
|
|
fooq, barq = Queue('foo'), Queue('bar')
|
|
|
|
w = Worker([fooq, barq])
|
|
|
|
w = Worker([fooq, barq])
|
|
|
|
self.assertEqual(w.work(burst=True), False,
|
|
|
|
self.assertEqual(
|
|
|
|
'Did not expect any work on the queue.')
|
|
|
|
w.work(burst=True), False,
|
|
|
|
|
|
|
|
'Did not expect any work on the queue.'
|
|
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
fooq.enqueue(say_hello, name='Frank')
|
|
|
|
fooq.enqueue(say_hello, name='Frank')
|
|
|
|
self.assertEqual(w.work(burst=True), True,
|
|
|
|
self.assertEqual(
|
|
|
|
'Expected at least some work done.')
|
|
|
|
w.work(burst=True), True,
|
|
|
|
|
|
|
|
'Expected at least some work done.'
|
|
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
def test_worker_ttl(self):
|
|
|
|
def test_worker_ttl(self):
|
|
|
|
"""Worker ttl."""
|
|
|
|
"""Worker ttl."""
|
|
|
@ -102,8 +109,10 @@ class TestWorker(RQTestCase):
|
|
|
|
q = Queue('foo')
|
|
|
|
q = Queue('foo')
|
|
|
|
w = Worker([q])
|
|
|
|
w = Worker([q])
|
|
|
|
job = q.enqueue('tests.fixtures.say_hello', name='Frank')
|
|
|
|
job = q.enqueue('tests.fixtures.say_hello', name='Frank')
|
|
|
|
self.assertEqual(w.work(burst=True), True,
|
|
|
|
self.assertEqual(
|
|
|
|
'Expected at least some work done.')
|
|
|
|
w.work(burst=True), True,
|
|
|
|
|
|
|
|
'Expected at least some work done.'
|
|
|
|
|
|
|
|
)
|
|
|
|
self.assertEqual(job.result, 'Hi there, Frank!')
|
|
|
|
self.assertEqual(job.result, 'Hi there, Frank!')
|
|
|
|
|
|
|
|
|
|
|
|
def test_job_times(self):
|
|
|
|
def test_job_times(self):
|
|
|
@ -116,14 +125,25 @@ class TestWorker(RQTestCase):
|
|
|
|
self.assertIsNotNone(job.enqueued_at)
|
|
|
|
self.assertIsNotNone(job.enqueued_at)
|
|
|
|
self.assertIsNone(job.started_at)
|
|
|
|
self.assertIsNone(job.started_at)
|
|
|
|
self.assertIsNone(job.ended_at)
|
|
|
|
self.assertIsNone(job.ended_at)
|
|
|
|
self.assertEqual(w.work(burst=True), True,
|
|
|
|
self.assertEqual(
|
|
|
|
'Expected at least some work done.')
|
|
|
|
w.work(burst=True), True,
|
|
|
|
|
|
|
|
'Expected at least some work done.'
|
|
|
|
|
|
|
|
)
|
|
|
|
self.assertEqual(job.result, 'Hi there, Stranger!')
|
|
|
|
self.assertEqual(job.result, 'Hi there, Stranger!')
|
|
|
|
after = utcnow()
|
|
|
|
after = utcnow()
|
|
|
|
job.refresh()
|
|
|
|
job.refresh()
|
|
|
|
self.assertTrue(before <= job.enqueued_at <= after, 'Not %s <= %s <= %s' % (before, job.enqueued_at, after))
|
|
|
|
self.assertTrue(
|
|
|
|
self.assertTrue(before <= job.started_at <= after, 'Not %s <= %s <= %s' % (before, job.started_at, after))
|
|
|
|
before <= job.enqueued_at <= after,
|
|
|
|
self.assertTrue(before <= job.ended_at <= after, 'Not %s <= %s <= %s' % (before, job.ended_at, after))
|
|
|
|
'Not %s <= %s <= %s' % (before, job.enqueued_at, after)
|
|
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
self.assertTrue(
|
|
|
|
|
|
|
|
before <= job.started_at <= after,
|
|
|
|
|
|
|
|
'Not %s <= %s <= %s' % (before, job.started_at, after)
|
|
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
self.assertTrue(
|
|
|
|
|
|
|
|
before <= job.ended_at <= after,
|
|
|
|
|
|
|
|
'Not %s <= %s <= %s' % (before, job.ended_at, after)
|
|
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
def test_work_is_unreadable(self):
|
|
|
|
def test_work_is_unreadable(self):
|
|
|
|
"""Unreadable jobs are put on the failed queue."""
|
|
|
|
"""Unreadable jobs are put on the failed queue."""
|
|
|
@ -557,14 +577,16 @@ def kill_worker(pid, double_kill):
|
|
|
|
|
|
|
|
|
|
|
|
class TestWorkerShutdown(RQTestCase):
|
|
|
|
class TestWorkerShutdown(RQTestCase):
|
|
|
|
def setUp(self):
|
|
|
|
def setUp(self):
|
|
|
|
# we want tests to fail if signal are ignored and the work remain running,
|
|
|
|
# we want tests to fail if signal are ignored and the work remain
|
|
|
|
# so set a signal to kill them after 5 seconds
|
|
|
|
# running, so set a signal to kill them after X seconds
|
|
|
|
|
|
|
|
self.killtimeout = 10
|
|
|
|
signal.signal(signal.SIGALRM, self._timeout)
|
|
|
|
signal.signal(signal.SIGALRM, self._timeout)
|
|
|
|
signal.alarm(5)
|
|
|
|
signal.alarm(self.killtimeout)
|
|
|
|
|
|
|
|
|
|
|
|
def _timeout(self, signal, frame):
|
|
|
|
def _timeout(self, signal, frame):
|
|
|
|
raise AssertionError("test still running after 5 seconds, "
|
|
|
|
raise AssertionError(
|
|
|
|
"likely the worker wasn't shutdown correctly")
|
|
|
|
"test still running after %i seconds, likely the worker wasn't shutdown correctly" % self.killtimeout
|
|
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
@slow
|
|
|
|
@slow
|
|
|
|
def test_idle_worker_warm_shutdown(self):
|
|
|
|
def test_idle_worker_warm_shutdown(self):
|
|
|
@ -621,3 +643,35 @@ class TestWorkerShutdown(RQTestCase):
|
|
|
|
shutdown_requested_date = w.shutdown_requested_date
|
|
|
|
shutdown_requested_date = w.shutdown_requested_date
|
|
|
|
self.assertIsNotNone(shutdown_requested_date)
|
|
|
|
self.assertIsNotNone(shutdown_requested_date)
|
|
|
|
self.assertEqual(type(shutdown_requested_date).__name__, 'datetime')
|
|
|
|
self.assertEqual(type(shutdown_requested_date).__name__, 'datetime')
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def schedule_access_self():
|
|
|
|
|
|
|
|
q = Queue('default', connection=get_current_connection())
|
|
|
|
|
|
|
|
q.enqueue(access_self)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class TestWorkerSubprocess(RQTestCase):
|
|
|
|
|
|
|
|
def setUp(self):
|
|
|
|
|
|
|
|
super(TestWorkerSubprocess, self).setUp()
|
|
|
|
|
|
|
|
db_num = self.testconn.connection_pool.connection_kwargs['db']
|
|
|
|
|
|
|
|
self.redis_url = 'redis://127.0.0.1:6379/%d' % db_num
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def test_run_empty_queue(self):
|
|
|
|
|
|
|
|
"""Run the worker in its own process with an empty queue"""
|
|
|
|
|
|
|
|
subprocess.check_call(['rqworker', '-u', self.redis_url, '-b'])
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def test_run_access_self(self):
|
|
|
|
|
|
|
|
"""Schedule a job, then run the worker as subprocess"""
|
|
|
|
|
|
|
|
q = Queue()
|
|
|
|
|
|
|
|
q.enqueue(access_self)
|
|
|
|
|
|
|
|
subprocess.check_call(['rqworker', '-u', self.redis_url, '-b'])
|
|
|
|
|
|
|
|
assert get_failed_queue().count == 0
|
|
|
|
|
|
|
|
assert q.count == 0
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def test_run_scheduled_access_self(self):
|
|
|
|
|
|
|
|
"""Schedule a job that schedules a job, then run the worker as subprocess"""
|
|
|
|
|
|
|
|
q = Queue()
|
|
|
|
|
|
|
|
q.enqueue(schedule_access_self)
|
|
|
|
|
|
|
|
subprocess.check_call(['rqworker', '-u', self.redis_url, '-b'])
|
|
|
|
|
|
|
|
assert get_failed_queue().count == 0
|
|
|
|
|
|
|
|
assert q.count == 0
|
|
|
|