суббота, 13 декабря 2008 г.

История одного бага

Началось все несколько месяцев назад. Один из клиентов сайта сообщил, что не смог опубликовать свою вакансию в нашем разделе Вакансии. (Для клиентов я написал специальную админку, где они самостоятельно редактируют и публикуют свои вакансии).

Ошибка оказалась очень странной - статус вакансии был "опубликовано", но даты показа/снятия с показа - пустые. В итоге вакансию не было видно на сайте, при этом система не давала пользователю опубликовать ее повторно, т.к. считала, что та уже опубликована.

Странность в том, что в коде только _одно_ место, где вакансия получает статус "опубликовано". Там же и устанавливаются даты снятия с показа. Все это в одной трансакции т.е. установить статус без даты никак не возможно:


def publish_jobpost(self, jobpost):
# ...
jobpost.published_date = now
jobpost.status = 'publish'
jobpost.expires_on = expires_on
client.jb_credit -= jobpost.price


Вообще-то, в коде есть еще одно место, где jobad.status меняется на 'publish'. У клиента есть возможность снять вакансию с показа. В этом случае он может затем передумать и снова "включить" ее.


def toggle_vacancy(self, id):
if c.vacancy.status == 'offline':
status = 'publish'
else:
status = 'offline'


Проблема только в том, что поставить статус offline можно только из status=='publish'. Т.е. возвращаемся к исходной проблеме.

Я честно говоря, поломал голову и бросил. Работы хватает, а глюк этот встречался только у некоторых клиентов (где-то в 10% случаев).

Но затем глюк повторился еще раз. В следующем месяце - еще пару раз. Пришлось заняться багой снова. Леше Маслову пришла в голову светлая мысль - написать триггер, который будет проверять, что при insert/update даты установлены правильно если status='publish'.

Триггер написали и забыли. И вот наконец вчера это случилось. Триггер упал, что повлекло за собой exception, который я получил по почте, вместе с traceback'ом. И что же было в трейсбеке:


Module doupy.controllers.sc:333 in toggle_vacancy
else:
c.vacancy.status = status


Как всегда, случилось "невозможное"! Очевидно у записи был статус offline. Но как? И тут я вспомнил про маааленький крон-скриптик, который "снимает" с показа старые вакансии, у которых уже давно истек срок "годности". Вот скриптик:


mysql -B ... <<EOF
update jobad_posts set post_status = 'offline' \
where expires_on < date_add(now(), interval - 2 month);
EOF


И, понятно, update успешно срабатывал для черновиков, у которых expires_on == '0'. Понятно стало и почему так редко он проявлялся и не проявлялся ни разу у меня на рабочей машине - нужно было ждать, чтобы черновик был поврежден в результате крон вызова.

6 коммент.:

Dasha комментирует...

bug is a bug :)

Dasha комментирует...

hello

Анонимный комментирует...

многабукафф...

Анонимный комментирует...

Интереснейшая статья! Браво!

Galinka комментирует...

bug is not good

Galinka комментирует...

interesting

Отправить комментарий