February 4th, 2016

404

success (2)

Я всё ещё не написал пакетирование (в процессе), но не выдержал и поставил с помощью setup.py на свою машину. Оно очевидно не работает, потому что базы нет, адресов нет, ничего нет - то есть проверка базового цикла.

Найдено две ошибки: одна тривиальная - опечатка в имени конфига, который ожидается в системе, а вот вторая очень даже нетривиальная.


Структура программы:

while True:
wait()
run_in_parallel(job * N)

run_in_parallel:
* get data from DB
* do job
* save results

do job большей частью происходит вне питона, и его запускают в параллель. Чтобы GIL не сходил с ума на параллельном выполнении кода (подготовки данных в БД и т.д.) используются блокировки.

Каждая "параллельная задача" выглядит так:
* acquire lock
* get data from DB
* prepare
* release lock
* do IO
* acquire lock
* process result
* save to db
* release lock

На моей машине очевидно, всё ломалось в районе "get data from DB". Тредик честно пытался 10 раз переподключиться к базе данных и умирал со словами "так жить нельзя".

... И при этом не снимал lock, и умирал. Очевидно, второй тред при этом навсегда оставался в заблокированном состоянии, и основной тред бесконечно ожидал завершения потомка.

Решение было простое: засунуть release в "finally:" в коде, но в целом оно было очень и очень тонким багом, который как раз на продакшене бы не возник до момента длительной аварии в БД.

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

success (3)

Итого:

Девопсовое:
* Пять ошибок в пакетировании, две в setup.py (выползли на этапе сборки/установки).
* Кривой конфиг dupload на CI'е
* Кривой репозиторий под reprepro, переделал в aptly
* Пришлось переделывать job'у у jenkins'а.

После запуска:
* разумеется, опечатки в sql'е. 2 шт.
* одна опечатка в имени функции, которая должна была быть поймана pytest'ом, но не была, ибо mock слишком добрый.
* Слишком скупой вывод в медленных и важных местах (тестами не обнаруживатся по понятным причинам).
* Ощущение, что схалтурил, ибо sql'ный debug включается/выключается только в коде.
* Уродливое форматирование логов (слишком много квадратных скобок), ибо не подумал.
* Логическая ошибка с конвертациями величин честно попавшая даже в тесты. Исправление - 17 негодующих тестов.

Чистых проблем: 2 sql опечатки, одна опечатка в коде, одна логическая ошибка. Дальше полёт нормальный. Вроде бы.