четверг, 12 марта 2009 г.

Система билдов

Кстати, еще летом нас была совсем страшная система создания билда, java ant с вкраплениями питоновского кода.
Когда она ломалась - атас. Никто не проверял, чем закончились эти самые вызовы подпроцессов. Собиралка продолжала работать дальше. Очень долго.
Через примерно 40 минут можно было увидеть - билд не прошел. Почему - догадаться можно. Иногда. Часто - нет. Все очень сложно и запутанно. Повторить - извольте начать все сначала (40 мин, помните?)
И так раз за разом.

Летом я как раз был в Нью-Йорке пару месяцев. И выпала моя неделя почетного билдера. После третьего проблемного билда за мою неделю (мне везло просто сказочно) - озверел. Переписал все к черту.

Теперь, как водится, все делается Питоном. С очень строгими проверками. Теперь все бросает exception и он, заметьте, не съедается как except Exception - долго бил по рукам за такое. Еще бить и бить - есть их у нас в рабочем коде... Если сломалось - стоп с максимально полной инфой.

Система сделана в стиле: запустил и забыл. Иди пить свой кофе. Потом остается только проверить результат.
Билд - пошаговый (Бикинга вовремя почитал). Если шаг был успешен - запомним его (записали в локальный файл). Следующий шаг поломается - получим полную инфу и, самое главное, возможность продолжить с поломавшегося места минуя предыдущие шаги. При это в начале билд делает tag и patch. И есть возможность на этапе, например, сломавшихся юниттестов автоматически обновиться из trunk, сделать patch commit и новый tag. Сильно экономит время.
Все в целом сейчас - 10 мин. Потом идут regression tests, и тут уж помочь ничем нельзя.

Да, мы используем svn без branch for every new task. Такая схема работы - все в trunk. Был бы рад переключиться на bazaar (сторонникам mercurial и git - я вас тоже люблю и уважаю) - но невозможно сейчас по организационным причинам.

Посидев месяц почетным билдером, отладил систему до состояния швейцарских часов.

Теперь у нас скользящий график переходящего знамени. С маленькой поправкой. Индивидум, заваливший билд сегодня - получает знамя на два следующих дня (если завтра будут проблемы от другого индивидума - знамя незамедлительно переходит к виновнику). Очень эффективная система. Позволяющая получить некоторое удовлетворение от того, что ты до хрен знает какого часа решал проблемы, созданные другими.

Сейчас это как правило нерабочие regression - которые 40 мин (летом было 20) и полный стек мало кто проверяет. Я - крайне редко. Но если попался - получи свои 2 почетных дня.

Забавная система получилась.

сериализация и юниттесты (сумбурно)

Я понемногу склоняюсь к мысли, что pickle - очень удобный, но далеко не всегда оптимальный формат.

Во первых - слишком он легко идет. Сериализует почти все, что угодно. В этом есть и минусы.

Вчера после работы чуть задержался в офисе. Приятель получил почетное переходящее знамя билдера (мы их ежедневно делаем), а я за компанию.

Плюс хотел юниттестами новое изобретение покрыть полностью и строго. Без них отдашь хорошую задумку людям, а через месяц-два в ней появится куча слабых мест и откровенных ляпов. Заметил: если программист не понял основную мысль дизайна/архитектуры - он начинает "гладить кошку против шерсти". А на работе на пальцах можно пересчитать тех, кто задумывается: а почему класс А был сделан так, а не как-нибудь иначе? Вот и лепят свои патчики так, что волосы дыбом.
Юниттесты позволяют вовремя дать по рукам. Получивший с большой вероятностью подойдет ко мне с вопросом, а там уж я ему заясню генеральную линию партии.

Это - помимо основной задачи: находить баги в коде. С этим тоже есть своя проблема. Если архитектура "правильная" - тесты пишутся легко и непринужденно, в больших количествах. И выполняют свое главное предназначение. Я только относительно недавно смог ощутить это в полной мере. И, думаю, все еще очень далек от совершенства.

Большая часть нашего немальнького кода, естественно, этому критерию не отвечает. Т.е. тесты баги не находят и не могут найти - т.к. для тестирования функции приходится подменять практически все ее окружение. Но тесты пишут, ибо начальство этого настоятельно требует. Результат тоже небесполезен: если ломается тест в результате твоих изменений - то вынужденно следует review, правится либо тест либо код, а чаще все вместе. Заодно есть шанс познакомиться с результатами чужого творчества и увидеть, что успели сделать другие люди/команды. К слову, американы в основной массе очень не любят смотреть чужой код. Наверное, это общая проблема - до этого мне просто сильно везло с коллегами.

Отвлекся.

Возвращаясь к pickle. Тесты в тот вечер я, как вы уже догадались, не писал.
Внезапно сервисы перестали работать. Практически совсем. Каждая транзакция заканчивалась exception и требуемого ответа не было. У нас клиент-сервер, так что сломалась серверная часть. Локальная катастрофа, вечерний неудачный билд не уйдет к тестерам, они не расскажут о новых ошибках, на один день сломается вся структура. Камрад Нил быстро понял, что последний находящийся в офисе чел из команды сервисописателей дупля откровенно не отбивает, окинул острым взором оставшийся народ и безошибочно направился ко мне.

Проблема была вроде бы тривиальная: что-то не могло пройти через pickle потому что было слабой ссылкой (weakref). Как я быстро обнаружил, пытались протолкнуть весьма нетривиальный объект, внутри которого к тому же было колечко (pickle ведь отлично сериализует объекты с циклическими ссылками).

Какой из многочисленных претендентов имел внутри этот самый weakref - непонятно по генерируемому исключению (к слову, использовался cPickle, а от него stacktrace совсем куцый). Путем дедукции и напряжения мозга сообразил, что пытались запиклить объект из нашего ORM (а-ля SQLAlchemy, но в силу наших внутренних особенностей алхимию использовать невозможно, создано ее подобие).

Я и не предполагал, что кому-то прийдет в голову передавать по сетке объект базы данных. Это же невозможно по ахитектурным соображениям: где сессия, к чему теперь информация об изменениях полей? Весь концепт рушится!!! Примерно как недавно кто-то пытался сериализовать boundmethod.

Быстро переопределил для всех сущностей из ORM __getnewargs__, дабы бросал хорошее и информативное исключение. Естественно, на следующем прогоне сразу же поймал вора.
Потребовался крошечный фикс на 10 строк, и успешный билд ушел.

А на следующий день я крепко насел на Тима, лида этих сервисников (к слову, Тим - весьма неглупый парень и голова у него работает как надо). Настоящая ПРОБЛЕМА была в том, что пересылалось ОЧЕНЬ МНОГО ИЗБЫТОЧНЫХ ДАННЫХ. Которые гораздо быстрее было бы получить на приемнике (они у него на самом деле уже есть). Т.е. вместо того, чтобы проталкивать очень большой объект, очень редко изменяемый (обе стороны уже имеют его в своем кеше, следят за изменениями и сами перечитывают когда нужно, прогнозируемые изменения не чаще раза в день) - можно передать его unique key, и подхватить его на другой стороне.

Отчасти ее видели по резкому снижению производительности серверной части. Но ведь «кажется, стало медленней работать» - еще не показатель»

Тим сильно удивился такому положению дел. Предложил быстрый патчик (и он прошел - еще бы, тривиально, когда знаешь в чем дело). Посопротивлялся идее "а давайте напишем свой pickle с нашими ограничениями".

Это его не спасет, т.к. только свой сериализатор позволит вовремя заорать: не то в меня пихают, на что я согласный!!!

Для начала сделаем свой restricted pickle(унаследоваться от Pickler и UnPickler и зарезать функциональность) , а потом, если будет нужно - перепишем на плюсах, как водится.

Уже практически сказал все, что хотел.
Теперь - во вторых (мелочь).

Специализированный pickle может быть быстрее. Не знаю насколько. Быть может - на "совсем чуть-чуть". Можно быстрее обрабатывать твои специфические типы, не кодируя их как "наследник от object, лежащий в модуле my_pope.my_cardinal.my_priest.my_guy", а просто вставив свой префикс для класса.

Улучшение, наверное, будет иметь только академически интересный прирост в скорости. Но к тому, что в стандартный pickle пролезут совсем неожиданно огромные объекты - будь готов. Просто по недосмотру и незнанию. Сейчас ты контролируешь все - но что будет, если проект разрастется?