mirror of
https://github.com/django-q2/django-q2.git
synced 2026-10-07 12:28:12 +08:00
Merge pull request #64 from Koed00/dev
Adds configuration output and other enhancements
This commit is contained in:
@@ -98,6 +98,9 @@ class QueueAdmin(admin.ModelAdmin):
|
|||||||
list_display = (
|
list_display = (
|
||||||
'id',
|
'id',
|
||||||
'key',
|
'key',
|
||||||
|
'task_id',
|
||||||
|
'name',
|
||||||
|
'func',
|
||||||
'lock'
|
'lock'
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -7,7 +7,6 @@ class Broker(object):
|
|||||||
self.connection = self.get_connection(list_key)
|
self.connection = self.get_connection(list_key)
|
||||||
self.list_key = list_key
|
self.list_key = list_key
|
||||||
self.cache = self.get_cache()
|
self.cache = self.get_cache()
|
||||||
self.task_cache = []
|
|
||||||
|
|
||||||
def enqueue(self, task):
|
def enqueue(self, task):
|
||||||
"""
|
"""
|
||||||
|
|||||||
@@ -17,17 +17,9 @@ class Sqs(Broker):
|
|||||||
# sqs supports max 10 messages in bulk
|
# sqs supports max 10 messages in bulk
|
||||||
if Conf.BULK > 10:
|
if Conf.BULK > 10:
|
||||||
Conf.BULK = 10
|
Conf.BULK = 10
|
||||||
t = None
|
tasks = self.queue.receive_messages(MaxNumberOfMessages=Conf.BULK, VisibilityTimeout=Conf.RETRY)
|
||||||
if len(self.task_cache) > 0:
|
if tasks:
|
||||||
t = self.task_cache.pop()
|
return [(t.receipt_handle, t.body) for t in tasks]
|
||||||
else:
|
|
||||||
tasks = self.queue.receive_messages(MaxNumberOfMessages=Conf.BULK, VisibilityTimeout=Conf.RETRY)
|
|
||||||
if tasks:
|
|
||||||
t = tasks.pop()
|
|
||||||
if tasks:
|
|
||||||
self.task_cache = tasks
|
|
||||||
if t:
|
|
||||||
return t.receipt_handle, t.body
|
|
||||||
|
|
||||||
def acknowledge(self, task_id):
|
def acknowledge(self, task_id):
|
||||||
return self.delete(task_id)
|
return self.delete(task_id)
|
||||||
|
|||||||
@@ -11,18 +11,10 @@ class Disque(Broker):
|
|||||||
'ADDJOB {} {} 500 RETRY {}'.format(self.list_key, task, retry)).decode()
|
'ADDJOB {} {} 500 RETRY {}'.format(self.list_key, task, retry)).decode()
|
||||||
|
|
||||||
def dequeue(self):
|
def dequeue(self):
|
||||||
t = None
|
|
||||||
if len(self.task_cache) > 0:
|
|
||||||
t = self.task_cache.pop()
|
|
||||||
else:
|
|
||||||
tasks = self.connection.execute_command(
|
tasks = self.connection.execute_command(
|
||||||
'GETJOB COUNT {} TIMEOUT 1000 FROM {}'.format(Conf.BULK, self.list_key))
|
'GETJOB COUNT {} TIMEOUT 1000 FROM {}'.format(Conf.BULK, self.list_key))
|
||||||
if tasks:
|
if tasks:
|
||||||
t = tasks.pop()
|
return [(t[1].decode(), t[2].decode()) for t in tasks]
|
||||||
if tasks:
|
|
||||||
self.task_cache = tasks
|
|
||||||
if t:
|
|
||||||
return t[1].decode(), t[2].decode()
|
|
||||||
|
|
||||||
def queue_size(self):
|
def queue_size(self):
|
||||||
return self.connection.execute_command('QLEN {}'.format(self.list_key))
|
return self.connection.execute_command('QLEN {}'.format(self.list_key))
|
||||||
|
|||||||
@@ -9,18 +9,10 @@ class IronMQBroker(Broker):
|
|||||||
return self.connection.post(task)['ids'][0]
|
return self.connection.post(task)['ids'][0]
|
||||||
|
|
||||||
def dequeue(self):
|
def dequeue(self):
|
||||||
t = None
|
|
||||||
if len(self.task_cache) > 0:
|
|
||||||
t = self.task_cache.pop()
|
|
||||||
else:
|
|
||||||
timeout = Conf.RETRY or None
|
timeout = Conf.RETRY or None
|
||||||
tasks = self.connection.get(timeout=timeout, wait=1, max=Conf.BULK)['messages']
|
tasks = self.connection.get(timeout=timeout, wait=1, max=Conf.BULK)['messages']
|
||||||
if tasks:
|
if tasks:
|
||||||
t = tasks.pop()
|
return [(t['id'], t['body']) for t in tasks]
|
||||||
if tasks:
|
|
||||||
self.task_cache = tasks
|
|
||||||
if t:
|
|
||||||
return t['id'], t['body']
|
|
||||||
|
|
||||||
def ping(self):
|
def ping(self):
|
||||||
return self.connection.name == self.list_key
|
return self.connection.name == self.list_key
|
||||||
|
|||||||
+1
-12
@@ -31,24 +31,13 @@ class ORM(Broker):
|
|||||||
return package.pk
|
return package.pk
|
||||||
|
|
||||||
def dequeue(self):
|
def dequeue(self):
|
||||||
if len(self.task_cache) > 0:
|
|
||||||
t = self.task_cache.pop()
|
|
||||||
return t.pk, t.payload
|
|
||||||
else:
|
|
||||||
# Get new and timed out tasks
|
|
||||||
tasks = OrmQ.objects.using(Conf.ORM).filter(
|
tasks = OrmQ.objects.using(Conf.ORM).filter(
|
||||||
Q(key=self.list_key, lock__isnull=True) |
|
Q(key=self.list_key, lock__isnull=True) |
|
||||||
Q(key=self.list_key, lock__lte=timezone.now() - timedelta(seconds=Conf.RETRY)))[:Conf.BULK]
|
Q(key=self.list_key, lock__lte=timezone.now() - timedelta(seconds=Conf.RETRY)))[:Conf.BULK]
|
||||||
if tasks:
|
if tasks:
|
||||||
# lock them
|
# lock them
|
||||||
OrmQ.objects.using(Conf.ORM).filter(pk__in=tasks).update(lock=timezone.now())
|
OrmQ.objects.using(Conf.ORM).filter(pk__in=tasks).update(lock=timezone.now())
|
||||||
tasks = [t for t in tasks]
|
return [(t.pk, t.payload) for t in tasks]
|
||||||
# pop one task
|
|
||||||
t = tasks.pop()
|
|
||||||
if tasks:
|
|
||||||
# add remainder to cache
|
|
||||||
self.task_cache = [t for t in tasks]
|
|
||||||
return t.pk, t.payload
|
|
||||||
# empty queue, spare the cpu
|
# empty queue, spare the cpu
|
||||||
sleep(0.2)
|
sleep(0.2)
|
||||||
|
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ class Redis(Broker):
|
|||||||
def dequeue(self):
|
def dequeue(self):
|
||||||
task = self.connection.blpop(self.list_key, 1)
|
task = self.connection.blpop(self.list_key, 1)
|
||||||
if task:
|
if task:
|
||||||
return None, task[1]
|
return [(None, task[1])]
|
||||||
|
|
||||||
def queue_size(self):
|
def queue_size(self):
|
||||||
return self.connection.llen(self.list_key)
|
return self.connection.llen(self.list_key)
|
||||||
|
|||||||
+15
-14
@@ -171,7 +171,7 @@ class Sentinel(object):
|
|||||||
self.spawn_process(worker, self.task_queue, self.result_queue, Value('f', -1), self.timeout)
|
self.spawn_process(worker, self.task_queue, self.result_queue, Value('f', -1), self.timeout)
|
||||||
|
|
||||||
def spawn_monitor(self):
|
def spawn_monitor(self):
|
||||||
return self.spawn_process(monitor, self.result_queue)
|
return self.spawn_process(monitor, self.result_queue, self.broker)
|
||||||
|
|
||||||
def reincarnate(self, process):
|
def reincarnate(self, process):
|
||||||
"""
|
"""
|
||||||
@@ -298,23 +298,24 @@ def pusher(task_queue, event, broker=None):
|
|||||||
logger.info(_('{} pushing tasks at {}').format(current_process().name, current_process().pid))
|
logger.info(_('{} pushing tasks at {}').format(current_process().name, current_process().pid))
|
||||||
while True:
|
while True:
|
||||||
try:
|
try:
|
||||||
task = broker.dequeue()
|
task_set = broker.dequeue()
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(e)
|
logger.error(e)
|
||||||
# broker probably crashed. Let the sentinel handle it.
|
# broker probably crashed. Let the sentinel handle it.
|
||||||
sleep(10)
|
sleep(10)
|
||||||
break
|
break
|
||||||
if task:
|
if task_set:
|
||||||
ack_id = task[0]
|
for task in task_set:
|
||||||
# unpack the task
|
ack_id = task[0]
|
||||||
try:
|
# unpack the task
|
||||||
task = signing.SignedPackage.loads(task[1])
|
try:
|
||||||
except (TypeError, signing.BadSignature) as e:
|
task = signing.SignedPackage.loads(task[1])
|
||||||
logger.error(e)
|
except (TypeError, signing.BadSignature) as e:
|
||||||
broker.fail(ack_id)
|
logger.error(e)
|
||||||
continue
|
broker.fail(ack_id)
|
||||||
task['ack_id'] = ack_id
|
continue
|
||||||
task_queue.put(task)
|
task['ack_id'] = ack_id
|
||||||
|
task_queue.put(task)
|
||||||
logger.debug(_('queueing from {}').format(broker.list_key))
|
logger.debug(_('queueing from {}').format(broker.list_key))
|
||||||
if event.is_set():
|
if event.is_set():
|
||||||
break
|
break
|
||||||
@@ -493,7 +494,7 @@ def set_cpu_affinity(n, process_ids, actual=not Conf.TESTING):
|
|||||||
"""
|
"""
|
||||||
Sets the cpu affinity for the supplied processes.
|
Sets the cpu affinity for the supplied processes.
|
||||||
Requires the optional psutil module.
|
Requires the optional psutil module.
|
||||||
:param int n:
|
:param int n: affinity
|
||||||
:param list process_ids: a list of pids
|
:param list process_ids: a list of pids
|
||||||
:param bool actual: Test workaround for Travis not supporting cpu affinity
|
:param bool actual: Test workaround for Travis not supporting cpu affinity
|
||||||
"""
|
"""
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
|
from optparse import make_option
|
||||||
from django.core.management.base import BaseCommand
|
from django.core.management.base import BaseCommand
|
||||||
from django.utils.translation import ugettext as _
|
from django.utils.translation import ugettext as _
|
||||||
|
|
||||||
|
from django_q.conf import Conf
|
||||||
from django_q.monitor import info
|
from django_q.monitor import info
|
||||||
|
|
||||||
|
|
||||||
@@ -8,5 +10,19 @@ class Command(BaseCommand):
|
|||||||
# Translators: help text for qinfo management command
|
# Translators: help text for qinfo management command
|
||||||
help = _('General information over all clusters.')
|
help = _('General information over all clusters.')
|
||||||
|
|
||||||
|
option_list = BaseCommand.option_list + (
|
||||||
|
make_option('--config',
|
||||||
|
action='store_true',
|
||||||
|
dest='config',
|
||||||
|
default=False,
|
||||||
|
help='Print current configuration.'),
|
||||||
|
)
|
||||||
|
|
||||||
def handle(self, *args, **options):
|
def handle(self, *args, **options):
|
||||||
info()
|
if options.get('config', False):
|
||||||
|
hide = ['conf', 'IDLE', 'STOPPING', 'STARTING', 'WORKING', 'SIGNAL_NAMES', 'STOPPED']
|
||||||
|
settings = [a for a in dir(Conf) if not a.startswith('__') and a not in hide]
|
||||||
|
for setting in settings:
|
||||||
|
self.stdout.write('{}: {}'.format(setting, getattr(Conf, setting)))
|
||||||
|
else:
|
||||||
|
info()
|
||||||
|
|||||||
+34
-10
@@ -1,6 +1,6 @@
|
|||||||
import logging
|
import logging
|
||||||
from django import get_version
|
|
||||||
|
|
||||||
|
from django import get_version
|
||||||
import importlib
|
import importlib
|
||||||
from django.core.urlresolvers import reverse
|
from django.core.urlresolvers import reverse
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
@@ -11,6 +11,8 @@ from django.utils import timezone
|
|||||||
from picklefield import PickledObjectField
|
from picklefield import PickledObjectField
|
||||||
from picklefield.fields import dbsafe_decode
|
from picklefield.fields import dbsafe_decode
|
||||||
|
|
||||||
|
from django_q.signing import SignedPackage
|
||||||
|
|
||||||
|
|
||||||
class Task(models.Model):
|
class Task(models.Model):
|
||||||
id = models.CharField(max_length=32, primary_key=True, editable=False)
|
id = models.CharField(max_length=32, primary_key=True, editable=False)
|
||||||
@@ -40,12 +42,20 @@ class Task(models.Model):
|
|||||||
values = Task.objects.filter(group=group_id).exclude(success=False).values_list('result', flat=True)
|
values = Task.objects.filter(group=group_id).exclude(success=False).values_list('result', flat=True)
|
||||||
return decode_results(values)
|
return decode_results(values)
|
||||||
|
|
||||||
|
def group_result(self, failures=False):
|
||||||
|
if self.group:
|
||||||
|
return self.get_result_group(self.group, failures)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_group_count(group_id, failures=False):
|
def get_group_count(group_id, failures=False):
|
||||||
if failures:
|
if failures:
|
||||||
return Failure.objects.filter(group=group_id).count()
|
return Failure.objects.filter(group=group_id).count()
|
||||||
return Task.objects.filter(group=group_id).count()
|
return Task.objects.filter(group=group_id).count()
|
||||||
|
|
||||||
|
def group_count(self, failures=False):
|
||||||
|
if self.group:
|
||||||
|
return self.get_group_count(self.group, failures)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def delete_group(group_id, objects=False):
|
def delete_group(group_id, objects=False):
|
||||||
group = Task.objects.filter(group=group_id)
|
group = Task.objects.filter(group=group_id)
|
||||||
@@ -53,6 +63,10 @@ class Task(models.Model):
|
|||||||
return group.delete()
|
return group.delete()
|
||||||
return group.update(group=None)
|
return group.update(group=None)
|
||||||
|
|
||||||
|
def group_delete(self, tasks=False):
|
||||||
|
if self.group:
|
||||||
|
return self.delete_group(self.group, tasks)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_task(task_id):
|
def get_task(task_id):
|
||||||
if len(task_id) == 32 and Task.objects.filter(id=task_id).exists():
|
if len(task_id) == 32 and Task.objects.filter(id=task_id).exists():
|
||||||
@@ -189,14 +203,26 @@ class Schedule(models.Model):
|
|||||||
|
|
||||||
|
|
||||||
class OrmQ(models.Model):
|
class OrmQ(models.Model):
|
||||||
key = models.CharField(max_length=100)
|
key = models.CharField(max_length=100)
|
||||||
payload = models.TextField()
|
payload = models.TextField()
|
||||||
lock = models.DateTimeField(null=True)
|
lock = models.DateTimeField(null=True)
|
||||||
|
|
||||||
class Meta:
|
def task(self):
|
||||||
app_label = 'django_q'
|
return SignedPackage.loads(self.payload)
|
||||||
verbose_name = _('Queued task')
|
|
||||||
verbose_name_plural = _('Queued tasks')
|
def func(self):
|
||||||
|
return self.task()['func']
|
||||||
|
|
||||||
|
def task_id(self):
|
||||||
|
return self.task()['id']
|
||||||
|
|
||||||
|
def name(self):
|
||||||
|
return self.task()['name']
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
app_label = 'django_q'
|
||||||
|
verbose_name = _('Queued task')
|
||||||
|
verbose_name_plural = _('Queued tasks')
|
||||||
|
|
||||||
|
|
||||||
# Backwards compatibility for Django 1.7
|
# Backwards compatibility for Django 1.7
|
||||||
@@ -205,5 +231,3 @@ def decode_results(values):
|
|||||||
# decode values in 1.7
|
# decode values in 1.7
|
||||||
return [dbsafe_decode(v) for v in values]
|
return [dbsafe_decode(v) for v in values]
|
||||||
return values
|
return values
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -7,12 +7,13 @@ from django_q.tasks import schedule
|
|||||||
from django_q.models import Task, Failure, OrmQ
|
from django_q.models import Task, Failure, OrmQ
|
||||||
from django_q.humanhash import uuid
|
from django_q.humanhash import uuid
|
||||||
from django_q.conf import Conf
|
from django_q.conf import Conf
|
||||||
|
from django_q.signing import SignedPackage
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.django_db
|
@pytest.mark.django_db
|
||||||
def test_admin_views(admin_client):
|
def test_admin_views(admin_client):
|
||||||
Conf.ORM='default'
|
Conf.ORM = 'default'
|
||||||
s = schedule('sched.test')
|
s = schedule('schedule.test')
|
||||||
tag = uuid()
|
tag = uuid()
|
||||||
f = Task.objects.create(
|
f = Task.objects.create(
|
||||||
id=tag[1],
|
id=tag[1],
|
||||||
@@ -25,13 +26,13 @@ def test_admin_views(admin_client):
|
|||||||
t = Task.objects.create(
|
t = Task.objects.create(
|
||||||
id=tag[1],
|
id=tag[1],
|
||||||
name=tag[0],
|
name=tag[0],
|
||||||
func='test.succes',
|
func='test.success',
|
||||||
started=timezone.now(),
|
started=timezone.now(),
|
||||||
stopped=timezone.now(),
|
stopped=timezone.now(),
|
||||||
success=True)
|
success=True)
|
||||||
q = OrmQ.objects.create(
|
q = OrmQ.objects.create(
|
||||||
key='test',
|
key='test',
|
||||||
payload='test')
|
payload=SignedPackage.dumps({'id': 1, 'func': 'test', 'name': 'test'}))
|
||||||
admin_urls = (
|
admin_urls = (
|
||||||
# schedule
|
# schedule
|
||||||
reverse('admin:django_q_schedule_changelist'),
|
reverse('admin:django_q_schedule_changelist'),
|
||||||
|
|||||||
@@ -18,10 +18,19 @@ def test_broker():
|
|||||||
broker.acknowledge('test')
|
broker.acknowledge('test')
|
||||||
broker.ping()
|
broker.ping()
|
||||||
broker.info()
|
broker.info()
|
||||||
|
# stats
|
||||||
assert broker.get_stat('test_1') is None
|
assert broker.get_stat('test_1') is None
|
||||||
broker.set_stat('test_1', 'test', 3)
|
broker.set_stat('test_1', 'test', 3)
|
||||||
assert broker.get_stat('test_1') == 'test'
|
assert broker.get_stat('test_1') == 'test'
|
||||||
assert broker.get_stats('test:*')[0] == 'test'
|
assert broker.get_stats('test:*')[0] == 'test'
|
||||||
|
# stats with no cache
|
||||||
|
Conf.CACHE = 'not_configured'
|
||||||
|
broker.cache = broker.get_cache()
|
||||||
|
assert broker.get_stat('test_1') is None
|
||||||
|
broker.set_stat('test_1', 'test', 3)
|
||||||
|
assert broker.get_stat('test_1') is None
|
||||||
|
assert broker.get_stats('test:*') is None
|
||||||
|
Conf.CACHE = 'default'
|
||||||
|
|
||||||
|
|
||||||
def test_redis():
|
def test_redis():
|
||||||
@@ -49,7 +58,7 @@ def test_disque():
|
|||||||
broker.enqueue('test')
|
broker.enqueue('test')
|
||||||
assert broker.queue_size() == 1
|
assert broker.queue_size() == 1
|
||||||
# dequeue
|
# dequeue
|
||||||
task = broker.dequeue()
|
task = broker.dequeue()[0]
|
||||||
assert task[1] == 'test'
|
assert task[1] == 'test'
|
||||||
broker.acknowledge(task[0])
|
broker.acknowledge(task[0])
|
||||||
assert broker.queue_size() == 0
|
assert broker.queue_size() == 0
|
||||||
@@ -61,7 +70,7 @@ def test_disque():
|
|||||||
assert broker.queue_size() == 0
|
assert broker.queue_size() == 0
|
||||||
sleep(1.5)
|
sleep(1.5)
|
||||||
assert broker.queue_size() == 1
|
assert broker.queue_size() == 1
|
||||||
task = broker.dequeue()
|
task = broker.dequeue()[0]
|
||||||
assert broker.queue_size() == 0
|
assert broker.queue_size() == 0
|
||||||
broker.acknowledge(task[0])
|
broker.acknowledge(task[0])
|
||||||
sleep(1.5)
|
sleep(1.5)
|
||||||
@@ -78,8 +87,8 @@ def test_disque():
|
|||||||
broker.enqueue('test')
|
broker.enqueue('test')
|
||||||
Conf.BULK = 5
|
Conf.BULK = 5
|
||||||
Conf.DISQUE_FASTACK = True
|
Conf.DISQUE_FASTACK = True
|
||||||
for i in range(5):
|
tasks = broker.dequeue()
|
||||||
task = broker.dequeue()
|
for task in tasks:
|
||||||
assert task is not None
|
assert task is not None
|
||||||
broker.acknowledge(task[0])
|
broker.acknowledge(task[0])
|
||||||
# test duplicate acknowledge
|
# test duplicate acknowledge
|
||||||
@@ -117,7 +126,7 @@ def test_ironmq():
|
|||||||
# enqueue
|
# enqueue
|
||||||
broker.enqueue('test')
|
broker.enqueue('test')
|
||||||
# dequeue
|
# dequeue
|
||||||
task = broker.dequeue()
|
task = broker.dequeue()[0]
|
||||||
assert task[1] == 'test'
|
assert task[1] == 'test'
|
||||||
broker.acknowledge(task[0])
|
broker.acknowledge(task[0])
|
||||||
assert broker.dequeue() is None
|
assert broker.dequeue() is None
|
||||||
@@ -126,7 +135,7 @@ def test_ironmq():
|
|||||||
broker.enqueue('test')
|
broker.enqueue('test')
|
||||||
assert broker.dequeue() is not None
|
assert broker.dequeue() is not None
|
||||||
sleep(1.5)
|
sleep(1.5)
|
||||||
task = broker.dequeue()
|
task = broker.dequeue()[0]
|
||||||
assert len(task) > 0
|
assert len(task) > 0
|
||||||
broker.acknowledge(task[0])
|
broker.acknowledge(task[0])
|
||||||
sleep(1.5)
|
sleep(1.5)
|
||||||
@@ -141,8 +150,8 @@ def test_ironmq():
|
|||||||
for i in range(5):
|
for i in range(5):
|
||||||
broker.enqueue('test')
|
broker.enqueue('test')
|
||||||
Conf.BULK = 5
|
Conf.BULK = 5
|
||||||
for i in range(5):
|
tasks = broker.dequeue()
|
||||||
task = broker.dequeue()
|
for task in tasks:
|
||||||
assert task is not None
|
assert task is not None
|
||||||
broker.acknowledge(task[0])
|
broker.acknowledge(task[0])
|
||||||
# duplicate acknowledge
|
# duplicate acknowledge
|
||||||
@@ -174,7 +183,7 @@ def test_sqs():
|
|||||||
# enqueue
|
# enqueue
|
||||||
broker.enqueue('test')
|
broker.enqueue('test')
|
||||||
# dequeue
|
# dequeue
|
||||||
task = broker.dequeue()
|
task = broker.dequeue()[0]
|
||||||
assert task[1] == 'test'
|
assert task[1] == 'test'
|
||||||
broker.acknowledge(task[0])
|
broker.acknowledge(task[0])
|
||||||
assert broker.dequeue() is None
|
assert broker.dequeue() is None
|
||||||
@@ -183,26 +192,26 @@ def test_sqs():
|
|||||||
broker.enqueue('test')
|
broker.enqueue('test')
|
||||||
assert broker.dequeue() is not None
|
assert broker.dequeue() is not None
|
||||||
sleep(1.5)
|
sleep(1.5)
|
||||||
task = broker.dequeue()
|
task = broker.dequeue()[0]
|
||||||
assert len(task) > 0
|
assert len(task) > 0
|
||||||
broker.acknowledge(task[0])
|
broker.acknowledge(task[0])
|
||||||
sleep(1.5)
|
sleep(1.5)
|
||||||
# delete job
|
# delete job
|
||||||
broker.enqueue('test')
|
broker.enqueue('test')
|
||||||
task_id = broker.dequeue()[0]
|
task_id = broker.dequeue()[0][0]
|
||||||
broker.delete(task_id)
|
broker.delete(task_id)
|
||||||
assert broker.dequeue() is None
|
assert broker.dequeue() is None
|
||||||
# fail
|
# fail
|
||||||
broker.enqueue('test')
|
broker.enqueue('test')
|
||||||
while task is None:
|
while task is None:
|
||||||
task = broker.dequeue()
|
task = broker.dequeue()[0]
|
||||||
broker.fail(task[0])
|
broker.fail(task[0])
|
||||||
# bulk test
|
# bulk test
|
||||||
for i in range(10):
|
for i in range(10):
|
||||||
broker.enqueue('test')
|
broker.enqueue('test')
|
||||||
Conf.BULK = 12
|
Conf.BULK = 12
|
||||||
for i in range(10):
|
tasks = broker.dequeue()
|
||||||
task = broker.dequeue()
|
for task in tasks:
|
||||||
assert task is not None
|
assert task is not None
|
||||||
broker.acknowledge(task[0])
|
broker.acknowledge(task[0])
|
||||||
# duplicate acknowledge
|
# duplicate acknowledge
|
||||||
@@ -216,6 +225,7 @@ def test_sqs():
|
|||||||
Conf.BULK = 1
|
Conf.BULK = 1
|
||||||
Conf.DJANGO_REDIS = 'default'
|
Conf.DJANGO_REDIS = 'default'
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.django_db
|
@pytest.mark.django_db
|
||||||
def test_orm():
|
def test_orm():
|
||||||
Conf.ORM = 'default'
|
Conf.ORM = 'default'
|
||||||
@@ -229,7 +239,7 @@ def test_orm():
|
|||||||
broker.enqueue('test')
|
broker.enqueue('test')
|
||||||
assert broker.queue_size() == 1
|
assert broker.queue_size() == 1
|
||||||
# dequeue
|
# dequeue
|
||||||
task = broker.dequeue()
|
task = broker.dequeue()[0]
|
||||||
assert task[1] == 'test'
|
assert task[1] == 'test'
|
||||||
broker.acknowledge(task[0])
|
broker.acknowledge(task[0])
|
||||||
assert broker.queue_size() == 0
|
assert broker.queue_size() == 0
|
||||||
@@ -241,7 +251,7 @@ def test_orm():
|
|||||||
assert broker.queue_size() == 0
|
assert broker.queue_size() == 0
|
||||||
sleep(1.5)
|
sleep(1.5)
|
||||||
assert broker.queue_size() == 1
|
assert broker.queue_size() == 1
|
||||||
task = broker.dequeue()
|
task = broker.dequeue()[0]
|
||||||
assert broker.queue_size() == 0
|
assert broker.queue_size() == 0
|
||||||
broker.acknowledge(task[0])
|
broker.acknowledge(task[0])
|
||||||
sleep(1.5)
|
sleep(1.5)
|
||||||
@@ -257,8 +267,8 @@ def test_orm():
|
|||||||
for i in range(5):
|
for i in range(5):
|
||||||
broker.enqueue('test')
|
broker.enqueue('test')
|
||||||
Conf.BULK = 5
|
Conf.BULK = 5
|
||||||
for i in range(5):
|
tasks = broker.dequeue()
|
||||||
task = broker.dequeue()
|
for task in tasks:
|
||||||
assert task is not None
|
assert task is not None
|
||||||
broker.acknowledge(task[0])
|
broker.acknowledge(task[0])
|
||||||
# test duplicate acknowledge
|
# test duplicate acknowledge
|
||||||
|
|||||||
@@ -215,13 +215,19 @@ def test_async(broker, admin_user):
|
|||||||
assert result(result_j.name) == result_j.result
|
assert result(result_j.name) == result_j.result
|
||||||
# groups
|
# groups
|
||||||
assert result_group('test_j')[0] == result_j.result
|
assert result_group('test_j')[0] == result_j.result
|
||||||
|
assert result_j.group_result()[0] == result_j.result
|
||||||
assert result_group('test_j', failures=True)[0] == result_j.result
|
assert result_group('test_j', failures=True)[0] == result_j.result
|
||||||
|
assert result_j.group_result(failures=True)[0] == result_j.result
|
||||||
assert fetch_group('test_j')[0].id == [result_j][0].id
|
assert fetch_group('test_j')[0].id == [result_j][0].id
|
||||||
assert fetch_group('test_j', failures=False)[0].id == [result_j][0].id
|
assert fetch_group('test_j', failures=False)[0].id == [result_j][0].id
|
||||||
assert count_group('test_j') == 1
|
assert count_group('test_j') == 1
|
||||||
|
assert result_j.group_count() == 1
|
||||||
assert count_group('test_j', failures=True) == 0
|
assert count_group('test_j', failures=True) == 0
|
||||||
|
assert result_j.group_count(failures=True) == 0
|
||||||
assert delete_group('test_j') == 1
|
assert delete_group('test_j') == 1
|
||||||
|
assert result_j.group_delete() == 0
|
||||||
assert delete_group('test_j', tasks=True) is None
|
assert delete_group('test_j', tasks=True) is None
|
||||||
|
assert result_j.group_delete(tasks=True) is None
|
||||||
# task k should not have been saved
|
# task k should not have been saved
|
||||||
assert fetch(k) is None
|
assert fetch(k) is None
|
||||||
broker.delete_queue()
|
broker.delete_queue()
|
||||||
|
|||||||
@@ -15,3 +15,4 @@ def test_qmonitor():
|
|||||||
@pytest.mark.django_db
|
@pytest.mark.django_db
|
||||||
def test_qinfo():
|
def test_qinfo():
|
||||||
call_command('qinfo')
|
call_command('qinfo')
|
||||||
|
call_command('qinfo', config=True)
|
||||||
|
|||||||
+2
-2
@@ -96,11 +96,11 @@ You can override this class if you want to contribute and support your own broke
|
|||||||
|
|
||||||
.. py:method:: enqueue(task)
|
.. py:method:: enqueue(task)
|
||||||
|
|
||||||
Sends a task package to the broker queue and returns a tracking id.
|
Sends a task package to the broker queue and returns a tracking id if available.
|
||||||
|
|
||||||
.. py:method:: dequeue()
|
.. py:method:: dequeue()
|
||||||
|
|
||||||
Gets a task package from the broker and returns a tuple with a tracking id and the package.
|
Gets packages from the broker and returns a list of tuples with a tracking id and the package.
|
||||||
|
|
||||||
.. py:method:: acknowledge(id)
|
.. py:method:: acknowledge(id)
|
||||||
|
|
||||||
|
|||||||
+2
-2
@@ -220,8 +220,8 @@ Adapted from `Sebastian Raschka's blog <http://sebastianraschka.com/Articles/201
|
|||||||
|
|
||||||
# wait for 100 results to return and print it.
|
# wait for 100 results to return and print it.
|
||||||
def parzen_hook(task):
|
def parzen_hook(task):
|
||||||
if count_group('parzen') == 100:
|
if task.group_count() == 100:
|
||||||
print(result_group('parzen'))
|
print(task.group_result())
|
||||||
|
|
||||||
|
|
||||||
Django Q is not optimized for distributed computing, but this example will give you an idea of what you can do with task :ref:`groups`.
|
Django Q is not optimized for distributed computing, but this example will give you an idea of what you can do with task :ref:`groups`.
|
||||||
|
|||||||
@@ -11,6 +11,10 @@ Start the monitor with Django's `manage.py` command::
|
|||||||
|
|
||||||
.. image:: _static/monitor.png
|
.. image:: _static/monitor.png
|
||||||
|
|
||||||
|
For all broker types except the Redis broker, the monitor utilizes Django's cache framework to store statistics of running clusters.
|
||||||
|
This can be any type of cache backend as long as it can be shared among Django instances. For this reason, the local memory backend will not work.
|
||||||
|
|
||||||
|
|
||||||
Legend
|
Legend
|
||||||
------
|
------
|
||||||
|
|
||||||
@@ -88,6 +92,13 @@ Average execution time (`Avg time`) is calculated in seconds over the last 24 ho
|
|||||||
|
|
||||||
Since some of these numbers are based on what is available in your tasks database, limiting or disabling the result backend will skew them.
|
Since some of these numbers are based on what is available in your tasks database, limiting or disabling the result backend will skew them.
|
||||||
|
|
||||||
|
Like with the monitor, these statistics come from a Redis server or Django's cache framework. So make sure you have either one configured.
|
||||||
|
|
||||||
|
To print out the current configuration run::
|
||||||
|
|
||||||
|
$ python manage.py qinfo --config
|
||||||
|
|
||||||
|
|
||||||
Status
|
Status
|
||||||
------
|
------
|
||||||
|
|
||||||
|
|||||||
+29
-2
@@ -127,8 +127,20 @@ Getting results by using :func:`result_group` is of course much faster than usin
|
|||||||
|
|
||||||
.. note::
|
.. note::
|
||||||
|
|
||||||
Although :func:`fetch_group` returns a queryset, due to the nature of the PickleField , calling ``Queryset.values`` on it will return a list of encoded results.
|
Calling ``Queryset.values`` for the result on Django 1.7 or lower will return a list of encoded results.
|
||||||
Use list comprehension or an iterator instead.
|
If you can't upgrade to Django 1.8, use list comprehension or an iterator to return decoded results.
|
||||||
|
|
||||||
|
You can also access group functions from a task result instance:
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
|
||||||
|
from django_q import fetch
|
||||||
|
|
||||||
|
task = fetch('winter-speaker-alpha-ceiling')
|
||||||
|
if task.group_count() > 100:
|
||||||
|
print(task.group_result())
|
||||||
|
task.group_delete()
|
||||||
|
print('Deleted group {}'.format(task.group))
|
||||||
|
|
||||||
Synchronous testing
|
Synchronous testing
|
||||||
-------------------
|
-------------------
|
||||||
@@ -318,6 +330,21 @@ Reference
|
|||||||
|
|
||||||
Time taken represents the time a task spends in the cluster, this includes any time it may have waited in the queue.
|
Time taken represents the time a task spends in the cluster, this includes any time it may have waited in the queue.
|
||||||
|
|
||||||
|
.. py:method:: group_result(failures=False)
|
||||||
|
|
||||||
|
Returns a list of results from this task's group.
|
||||||
|
Set failures to ``True`` to include failed results.
|
||||||
|
|
||||||
|
.. py:method:: group_count(failures=False)
|
||||||
|
|
||||||
|
Returns a count of the number of task results in this task's group.
|
||||||
|
Returns the number of failures when ``failures=True``
|
||||||
|
|
||||||
|
.. py:method:: group_delete(tasks=False)
|
||||||
|
|
||||||
|
Resets the group label on all the tasks in this task's group.
|
||||||
|
If ``tasks=True`` it will also delete the tasks in this group from the database, including itself.
|
||||||
|
|
||||||
.. py:classmethod:: get_result(task_id)
|
.. py:classmethod:: get_result(task_id)
|
||||||
|
|
||||||
Gets a result directly by task uuid or name.
|
Gets a result directly by task uuid or name.
|
||||||
|
|||||||
+1
-1
@@ -7,7 +7,7 @@
|
|||||||
arrow==0.6.0
|
arrow==0.6.0
|
||||||
blessed==1.9.5
|
blessed==1.9.5
|
||||||
boto3==1.1.3
|
boto3==1.1.3
|
||||||
botocore==1.2.1 # via boto3
|
botocore==1.2.2 # via boto3
|
||||||
django-picklefield==0.3.2
|
django-picklefield==0.3.2
|
||||||
django-redis==4.2.0
|
django-redis==4.2.0
|
||||||
docutils==0.12 # via botocore
|
docutils==0.12 # via botocore
|
||||||
|
|||||||
Reference in New Issue
Block a user