Merge pull request #41 from Koed00/dev

Workaround for osx implementation
This commit is contained in:
Ilan Steemers
2015-08-04 07:49:12 -07:00
6 changed files with 53 additions and 21 deletions
+2 -8
View File
@@ -22,12 +22,6 @@ from multiprocessing import Queue, Event, Process, Value, current_process
# external # external
import arrow import arrow
# optional
try:
import psutil
except ImportError:
psutil = None
# Django # Django
from django.utils import timezone from django.utils import timezone
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
@@ -37,7 +31,7 @@ from django import db
import signing import signing
import tasks import tasks
from django_q.conf import Conf, redis_client, logger from django_q.conf import Conf, redis_client, logger, psutil
from django_q.models import Task, Success, Schedule from django_q.models import Task, Success, Schedule
from django_q.monitor import Status, Stat from django_q.monitor import Status, Stat
@@ -149,7 +143,7 @@ class Sentinel(object):
if not self.start_event.is_set() and not self.stop_event.is_set(): if not self.start_event.is_set() and not self.stop_event.is_set():
return Conf.STARTING return Conf.STARTING
elif self.start_event.is_set() and not self.stop_event.is_set(): elif self.start_event.is_set() and not self.stop_event.is_set():
if self.result_queue.qsize() == 0 and self.task_queue.qsize() == 0: if Conf.QSIZE and self.result_queue.qsize() == 0 and self.task_queue.qsize() == 0:
return Conf.IDLE return Conf.IDLE
return Conf.WORKING return Conf.WORKING
elif self.stop_event.is_set() and self.start_event.is_set(): elif self.stop_event.is_set() and self.start_event.is_set():
+29 -3
View File
@@ -1,11 +1,20 @@
import logging import logging
from signal import signal from signal import signal
from multiprocessing import cpu_count from multiprocessing import cpu_count, Queue
# django
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
from django.conf import settings from django.conf import settings
# external
import redis import redis
# optional
try:
import psutil
except ImportError:
psutil = None
class Conf(object): class Conf(object):
""" """
@@ -36,8 +45,19 @@ class Conf(object):
# Maximum number of tasks that each cluster can work on # Maximum number of tasks that each cluster can work on
QUEUE_LIMIT = conf.get('queue_limit', None) QUEUE_LIMIT = conf.get('queue_limit', None)
# Number of workers in the pool. Default is cpu count. +2 for monitor and pusher # Number of workers in the pool. Default is cpu count if implemented, otherwise 4.
WORKERS = conf.get('workers', cpu_count()) WORKERS = conf.get('workers', False)
if not WORKERS:
try:
WORKERS = cpu_count()
# in rare cases this might fail
except NotImplementedError:
# try psutil
if psutil:
WORKERS = psutil.cpu_count() or 4
else:
# sensible default
WORKERS = 4
# Sets compression of redis packages # Sets compression of redis packages
COMPRESSED = conf.get('compress', False) COMPRESSED = conf.get('compress', False)
@@ -63,6 +83,12 @@ class Conf(object):
# The redis stats key # The redis stats key
Q_STAT = 'django_q:{}:cluster'.format(PREFIX) Q_STAT = 'django_q:{}:cluster'.format(PREFIX)
# OSX doesn't implement qsize because of missing sem_getvalue()
try:
QSIZE = Queue().qsize() == 0
except NotImplementedError:
QSIZE = False
# Getting the signal names # Getting the signal names
SIGNAL_NAMES = dict((getattr(signal, n), n) for n in dir(signal) if n.startswith('SIG') and '_' not in n) SIGNAL_NAMES = dict((getattr(signal, n), n) for n in dir(signal) if n.startswith('SIG') and '_' not in n)
+5 -2
View File
@@ -117,10 +117,13 @@ class Stat(Status):
self.reincarnations = sentinel.reincarnations self.reincarnations = sentinel.reincarnations
self.sentinel = sentinel.pid self.sentinel = sentinel.pid
self.status = sentinel.status() self.status = sentinel.status()
self.done_q_size = sentinel.result_queue.qsize() self.done_q_size = 0
self.task_q_size = 0
if Conf.QSIZE:
self.done_q_size = sentinel.result_queue.qsize()
self.task_q_size = sentinel.task_queue.qsize()
if sentinel.monitor: if sentinel.monitor:
self.monitor = sentinel.monitor.pid self.monitor = sentinel.monitor.pid
self.task_q_size = sentinel.task_queue.qsize()
if sentinel.pusher: if sentinel.pusher:
self.pusher = sentinel.pusher.pid self.pusher = sentinel.pusher.pid
self.workers = [w.pid for w in sentinel.pool] self.workers = [w.pid for w in sentinel.pool]
+2 -1
View File
@@ -55,7 +55,8 @@ def test_cluster_initial(r):
assert c.is_starting is False assert c.is_starting is False
sleep(0.5) sleep(0.5)
stat = c.stat stat = c.stat
assert stat.status == Conf.IDLE if Conf.QSIZE:
assert stat.status == Conf.IDLE
assert c.stop() is True assert c.stop() is True
assert c.sentinel.is_alive() is False assert c.sentinel.is_alive() is False
assert c.has_stopped assert c.has_stopped
+6 -2
View File
@@ -59,7 +59,7 @@ This can be useful if you have several projects using the same Redis server.
workers workers
~~~~~~~ ~~~~~~~
The number of workers to use in the cluster. Defaults to CPU count of the current host, but can be set to a custom number. The number of workers to use in the cluster. Defaults to CPU count of the current host, but can be set to a custom number. [#f1]_
recycle recycle
~~~~~~~ ~~~~~~~
@@ -232,4 +232,8 @@ Optional
$ pip install hiredis $ pip install hiredis
.. py:module:: django_q .. py:module:: django_q
.. rubric:: Footnotes
.. [#f1] Uses :func:`multiprocessing.cpu_count()` which can fail on some platforms. If so , please set the worker count in the configuration manually or install :ref:`psutil<psutil>` to provide an alternative cpu count method.
+9 -5
View File
@@ -30,7 +30,7 @@ State
Current state of the cluster: Current state of the cluster:
- **Starting** The cluster is spawning workers and getting ready. - **Starting** The cluster is spawning workers and getting ready.
- **Idle** Everything is ok, but there are no tasks to process. - **Idle** Everything is ok, but there are no tasks to process. [#f1]_
- **Working** Processing tasks like a good cluster should. - **Working** Processing tasks like a good cluster should.
- **Stopping** The cluster does not take on any new tasks and is finishing. - **Stopping** The cluster does not take on any new tasks and is finishing.
- **Stopped** All tasks have been processed and the cluster is shutting down. - **Stopped** All tasks have been processed and the cluster is shutting down.
@@ -43,7 +43,7 @@ The current number of workers in the cluster pool.
TQ TQ
~~ ~~
**Task Queue** counts the number of tasks in the queue **Task Queue** counts the number of tasks in the queue [#f1]_
If this keeps rising it means you are taking on more tasks than your cluster can handle. If this keeps rising it means you are taking on more tasks than your cluster can handle.
You can limit this by settings the :ref:`queue_limit` in your cluster configuration, after which it will turn green when that limit has been reached. You can limit this by settings the :ref:`queue_limit` in your cluster configuration, after which it will turn green when that limit has been reached.
@@ -52,7 +52,7 @@ If your task queue is always hitting its limit and your running out of resources
RQ RQ
~~ ~~
**Result Queue** shows the number of results in the queue. **Result Queue** shows the number of results in the queue. [#f1]_
Since results are only saved by a single process which has to access the database. Since results are only saved by a single process which has to access the database.
It's normal for the result queue to take slightly longer to clear than the task queue. It's normal for the result queue to take slightly longer to clear than the task queue.
@@ -118,11 +118,11 @@ Reference
.. py:attribute:: task_q_size .. py:attribute:: task_q_size
The number of tasks currently in the task queue. The number of tasks currently in the task queue. [#f1]_
.. py:attribute:: done_q_size .. py:attribute:: done_q_size
The number of tasks currently in the result queue. The number of tasks currently in the result queue. [#f1]_
.. py:attribute:: pusher .. py:attribute:: pusher
@@ -151,3 +151,7 @@ Reference
.. py:classmethod:: get_all(r=redis_client) .. py:classmethod:: get_all(r=redis_client)
Returns a list of :class:`Stat` objects for all active clusters. Takes an optional redis connection. Returns a list of :class:`Stat` objects for all active clusters. Takes an optional redis connection.
.. rubric:: Footnotes
.. [#f1] Uses :meth:`multiprocessing.Queue.qsize()` which is not implemented on OS X.