понедельник, 25 августа 2014 г.

Celery воркер зависает на "mingle: searching for neighbors"

Сегодня ВНЕЗАПНО все Celery воркеры перестали принимать любые задачи, при том что RabbitMQ (используется в качестве брокера) очереди были пустыми, а остальные части системы функционировали нормально. Не помогала ни перезагрузка Celery воркеров, ни перезагрузка RabbitMQ сервера.

После недолгого копания по логам проблема была локализирована, любой Celery воркер как будто зависал на моменте поиска соседей, оставляя в логах что-то такое:

2014-08-25 14:29:12 [INFO:celery.worker.consumer] Connected to amqp://guest:**@127.0.0.1:5672//
2014-08-25 14:29:12 [INFO:celery.worker.consumer] mingle: searching for neighbors

Быстрый гуглинг указал на существующую Celery issue, а уже в ней и нашелся ответ на проблему. Оказывается на корневом разделе подошло к концу место (было доступно порядка 150 Мб) и Celery в связке с RabbitMQ зависала не показывая никаких признаков жизни. Все починилось банальной чисткой корневого раздела, но осадок остался и на себя, что не поставил уведомления о заканчивающемся месте на корневом разделе и на Celery, что она никаким образом не пытается обработать эту ситуацию и проблему приходится вычислять окольными путями.

воскресенье, 4 мая 2014 г.

Range для не целых чисел

Почему-то никогда не думал, что rangexrange) понимают только целые числа (в Python 3 ситуация такая же) и функцией нельзя воспользоваться, как например:

range(0., 5., .5)
range(Decimal('0'), Decimal('10'), Decimal('1.5'))

Поэтому пришлось сделать свою замену:

import operator


def arange(start, stop=None, step=None):
    """
    Implement range function not only for integers as Python's builtin
    function, but for Decimals and floats as well.

    Returns generator with arithmetic progession, not list.
    """
    klass = type(start)
    lt_func = operator.lt

    stop = start if stop is None else stop
    start = klass(0) if start == stop else start
    step = klass(1 if step is None else step)

    assert isinstance(stop, klass), (
        'Start and stop limits have different types, {0!r} != {1!r}.'.
        format(type(start).__name__, type(stop).__name__)
    )
    assert step, "Step shouldn't be a zero: {0!r}.".format(step)

    if start < stop and step < 0 or start > stop and step > 0:
        raise StopIteration
    elif start > stop and step < 0:
        lt_func = operator.gt

    while lt_func(start, stop):
        yield start
        start += step

В отличии от range в Python 2 arange отдает генератор, а не список, что вообщем-то удобней и правильней, ну и функция всегда вернет элементы такого же типа, как были переданы в start и stop аргументы, даже если шаг - это целое число или он не указан вовсе.

Как всегда код оформлен как Gist на GitHub'е, там еще доктесты и юниттесты.

четверг, 25 апреля 2013 г.

Миграция с Posterous на Blogger

Как вы, наверное, знаете Twitter сначала купил Posterous, а потом его закрыл. И так как на Posterous'е был мой блог, я перенес все посты из него сюда. Сорри, если RSS фид обновился слишком рьяно.

Если у кого есть схожая проблема и он еще не перевез никуда свой Posterous, вот есть маленький скриптик, который делает всю работу.

зы. Так как этот блог стал моим единственным, то я сменил его название на более честное. Впредь тут будут появлятся информация и по Flask'у, и по Django, но в большинстве случаев думаю просто по Python'у.

четверг, 20 декабря 2012 г.

Запускаем gunicorn из Python'а

Иногда бывает надо запустить gunicorn внутри Python скрипта, например, в manage.py. Конечно всегда можно воспользоваться subprocess.call:

import subprocess


app = 'package.module:app'
host, port = '0.0.0.0', 8000
subprocess.call('gunicorn -b {}:{:d} -w 4 {}'.format(host, port, app))

Но как-то это не комильфо подумал я и решил найти более труЪ-способ :)

Решение пришло не сразу, но пришло, надо всего лишь переопределить sys.argv и вызвать метод run,

import sys

from gunicorn.app.wsgiapp import run


app = 'package.module:app'
host, port = '0.0.0.0', 8000
sys.argv = [
    sys.argv[0],
    '-b', '{}:{:d}'.format(host, port),
    '-w', '4',
    app
]
run()

Не очень круто вышло, не находите? А все потому что gunicorn еще использует устаревший optparse для парсинга аргументов с коммандной строки, а там задавать список аргументов в parse_args совсем не обязательно, ведь по дефолту берется список sys.argv[1:].

Так что на таком простом примере лично мне ясно видно, насколько optparse устарел и как вашему приложению, которое его еще использует надо сломя голову переезжать на argparse! И это я еще не рассказал вам о бесподобном управлении под-коммандами в argparse :)

четверг, 29 ноября 2012 г.

Чиним gunicorn'овский Internal Server Error

Если вдруг, после запуска gunicorn перестал работать и показывает ошибку похожую на:

Internal Server Error

Traceback:

Traceback (most recent call last):
  File "/Users/playpauseandstop/Projects/project/env/lib/python2.7/site-packages/gunicorn/workers/async.py", line 45, in handle
    self.handle_request(req, client, addr)
  File "/Users/playpauseandstop/Projects/project/env/lib/python2.7/site-packages/gunicorn/workers/async.py", line 73, in handle_request
    resp, environ = wsgi.create(req, sock, addr, self.address, self.cfg)
  File "/Users/playpauseandstop/Projects/project/env/lib/python2.7/site-packages/gunicorn/http/wsgi.py", line 161, in create
    path_info = path_info.split(script_name, 1)[1]
IndexError: list index out of range

то это говорит о том, что какая-то сволочьое-то приложение установило переменную окружения SCRIPT_NAME, которую gunicorn не может нормально обработать. Чтобы пофиксить, просто удаляем переменную окружения,

$ unset SCRIPT_NAME

и перегружаем страницу (ну можно еще перегрузить gunicorn для пущей важности). That's all!