понедельник, июня 27, 2011

Msrgincon'11


Подготовка

Вот и прошел очередной Margincon. Попробую рассказать, как оно было на этот раз. Незадолго до, ВНЕЗАПНО выяснилось, что докладов всего 4, и один из них мой. Прознав о моей беде, соратники по OpenStreetMap предложили воспользоваться случаем и сделать lighting talk про OSM. Так что мне пришлось не только спешно дописывать основной доклад, а еще и готовиться к lighting talk. И все в последние два дня. Дальше традиционный поезд Нск-Омск. Twitter хорошо годится для поиска попутчиков. В соседнем купе две девчонки-второкурсницы из НИИЖДа целенаправленно склеили матерого железнодорожника так что он всю дорогу рассказывал им про СЦБ. Затем Омск, традиционная прогулка по набережной с не менее традиционными поисками еды и транспорта до ОмГУ.


Регистрация

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


Доклады

Было всего три штуки. Федор Индутный с докладом про node.js до конференции не добрался чем навсегда испортил себе карму. А жаль, было о чем поговорить с человеком, использующим node.js в продакшене.


Оставшиеся два доклада очень понравились. Александр Гладыш произвел впечатляющий анализ визуальных редакторов бизнес-логики и ненавязчиво презентовал собственный редактор. Макс Трескин рассказал про язык Go. В качестве замены C этот язык выглядит перспективно, но сыровато. Вопрос, есть ли игра Го на Go остался нераскрытым.


Из-за отсутствия Федора участникам пришлось выкручиваться самостоятельно. Lighting talks дали затравку к обсуждению, которое плавно перетекло в open space и afterparty, где желающие могли наобщаться всласть.


Впечатления

Все супер, но мало. И участников и докладов. Университетская аудитория с кафедрой, посаженным проектором и рваным экраном плохо подходит для конференции. Если с докладами еще ничего, то Open Space на ступеньках не очень удался, по сравнению с виски-пати на Codefest. Очень порадовала теплая, дружеская атмосфера. Только ради этого стоит ездить на Margincon. Огромное спасибо Юрию и все кто помогал в организации.


На мой взгляд, формат Margincon'а, как конференции о маргинальных языках, себя исчерпал. Не так уж много этих языков и довольно мало людей могут рассказать что-нибудь помимо стандартного getting started. С другой стороны, хотелось бы избежать рассказов про "новый MVC фреймворк для PHP" или "мобильный битрикс", которые заняли львиную долю того же CodeFest. Хотелось бы услышать доклады про немейнстримные технологии: не-MVC веб-фреймворки, высоконагруженные архитектуры, noSQL и т.п.


Надеюсь, традиция Margincon'а будет продолжаться. Это прекрасный способ узнать что-то новое познакомиться с интересными людьми и услышать в поезде философскую идею Ruby on Rail.


PS: Ни одной фотографии за всю поездку я так и не сделал, так что пост без фотографии.


PSS: Использовать в качестве сайта конференции Lanyrd.com — отличная идея. Можно самому добавить ссылку на слайды не дожидаясь пока это сделают вечно занятые организаторы. Надеюсь, аудиозапись получилась и будет доступна.

четверг, октября 14, 2010

Что писать в лог


Как-то получается, что когда есть о чем писать нет времени, а когда есть время, писать не о чем. Зато есть проверенные временем посты на RSDN, на которые изредка требуется ссылаться. Вот некрокросспостингом я сейчас и займусь.


Регулярно люди спрашивают как и что писать в логи. Могу рассказать как это сделано в одном реальном проекте. Способов применения логов ровно два: а) помогать инженерам отвечать на вопросы пользователей в стиле "Что я только сделал не так?", и б) помогать разработчикам воспроизводить баги. Для этого критично знать, что, как и в какой последовательности происходило в системе. Если по логу не удается локализовать баг с точностью до строчки кода -- не страшно. Задача лога примерно очертить место поиска.


Уровней логгирования используется всего четыре:

  • DEBUG

  • INFO

  • WARNING

  • ERROR


  • Уровень CRITICAL не используется потому что изнутри системы невозможно обрушить ее целиком и навсегда. Поэтому вместо log.crit() вставлен код восстанавливающий систему после ошибки. Внешние сбои, конечно, могут обрушить что угодно, но они фиксируются внешними средствами. В данном случае в роли внешних средств выступает довольно серьезная система мониторинга на основе cacti. На практике не очень интересно, как именно система упала, если отвалился жесткий диск или рухнула сеть.


    На продакшене всегда включен уровень INFO. Очень редко, когда требуется воспроизвести особо злобный баг или проверить правильность алгоритма логгирование переключается на DEBUG. Снижать уровень до WARNING или ERROR не имеет смысла, потому что теряется необходимая информация. Если какая-то подсистема (чаще всего демон) забивает общий лог своими сообщениями, то для нее выделяется отдельный лог файл. Разные части системы пишут в собственные лог файлы. Все логи ротируются раз в сутки, а также при каждом перезапуске системы. Это полезно при апгрейдах, чтобы четко различать какие ошибки относятся к старой версии, а какие к новой.


    Теперь о том, что и когда пишется в лог. Поскольку большая часть ошибок сосредоточена на границах модулей, там же происходит большая часть записей в лог. Далеко не все, что происходит достойно попадания в логи. Достаточно только той информации, которая без этого будет потеряна. Отсюда правило: ничто не должно логгироваться дважды.


    ERROR


    Логгирование с уровнем ERROR пишется в следующих местах:


    • там где мы бросаем исключение, при условии, что есть важная информация, которая не попадает в исключение. На самом деле это архитектурный косяк, но все бывает.

      
      if (phaseOfMoon.isIncorrect()) {     
          log.error("Incorrect phase of moon at " + someEnvironment);     
          throw new AstronomyException("Incorrect phase of moon"); 
      } 
      
    • там, где мы преобразуем исключение в более общее с потерей информации. Обычно это происходит на границы системы, когда мы не хотим отдавать наружу специфичную информацию.
      
      } catch (SQLException ex) {     
          log.error("Databse error", ex);
          throw new SomeInterfaceException("Шеф, все пропало, шеф.") 
      } 
      
    • там, где исключение пробрасывается выше границы нашей системы
    • там, где прекращается обработка исключения
      
      } catch (SomeException ex) { 
          log.error("Не шмогла, ну и ладно", ex); 
      }
      
    • в случае ошибок, не связанных с исключениями
      
      if (positiveValue < 0) {
          log.error("Sanity check: positive is negative");
          return;
      }
      

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


    WARNING

    Общее правило: если какая-то неприятность произошла, но ее появление ожидаемо, неприятность не критична для работы или status quo может быть восстановлен автоматически, то это WARNING. Он пишется в момент обнаружения если неприятность невосстановима, или же в момент автоматического восстановления. Под warning попадают попытки восстановить соединение с сервером, перечитать файл, и т.п.


    INFO

    На уровне INFO логгируются события на уровне точек входа в систему: действия пользователя, вызовы API, запуск и остановка демонов и т.п.


    DEBUG

    Оставляется на усмотрение разработчика. Чаще всего логгируется в местах с нетривиальными алгоритмами и там где для отладки критично знать, по какой из ветвей пошло выполнение.


    Ну и напоследок несколько метрик. На 1000 строк кода приходится 5 записей в лог. Из них:

    • 51% — Error
    • 21% — Warning
    • 13% — Info
    • 15% — Debug

    понедельник, июня 28, 2010

    MarginCon'10

    В субботу прошел очередной MarginCon'10 (потомок RuPy.ru). На этот раз начало было в 0900, так что прогуляться по городу в поисках еды и лулзов не удалось. Ну и ладно, тоже неплохо. По дороге выяснилось, что билет на поезд Новосибирск - Омск можно купить онлайн, при этом его даже забирать не обязательно. Есть электронный check in, как в аэропорту. В следующий раз обязательно воспользуюсь.

    На самой конференции было шесть полновесных докладов, некоторое количество микро-докладов на 5 минут и 1 час свободного общения. Затем желающие собрались в заведении под вывеской "Колчак" на afterparty. Увы, билеты на поезд помешали в полной мере насладиться восточной кухней и живым общением, так что засвидетельствовав свое почтение, пришлось отправляться восвояси.

    Что было хорошо

    • Рекламная кампания. Я узнал о MarginCon'е за месяц, как миминиум из трех источников.
    • Полновесные доклады были очень интересные. Практически не было пересказов мануалов.
    • Видео-доклады были на высоте. Если в нашем углу нет мало интересных людей, их можно приглашать выступать онлайн. Снимаю шляпу перед блестящей идеей. Отдельное спасибо Льву Валкину за рассказ о собственно опыте.
    • Twitter в качестве СМИ показал себя великолепно. Завел себе.

    Что не очень

    • Мало обсуждений. Скорее всего из-за того, что люди рассказывали о своем использовании чужих технологий. У них нет уникального знания, которого нельзя нагуглить самому. Разработчики, рассказывающие о собственных технологиях, вызывают гораздо больший интерес.
    • Lighting talks. Мало, и большей частью ни о чем. Хотя рассказ о Common Lisp очень понравился.
    • Open Spaces. Опять же общих тем для обсуждения не было. В результате все скатилось в привычное "Образование", "Gnome vs. KDE", "emacs vs. vim" и т.п. Идея с выбором тем, которые потом будут организованно обсуждать радостно провалилась. Народ просто не оставил достаточно заявок.

    Что можно изменить

    • Сменить акцент конференции с собственно маргинальных языков на разработку на маргинальных языках. Тогда под формат попадут вещи связанные с языком, вроде: "Какие новые либы вы пропустили", "Куда движется Python", "Что будет в новой версии Rail", "Когда наконец выйдет Scala 2.8". Понятно, что большинство из этих тем недостойны полновесных докладов и в любом случае будут пересказом чьих-то слов, но Lighting Talks ими вполне можно интересно заполнить.
    • Выкинуть Open Spaces. После конференции можно сразу переместиться на afterparty (возможно более организованно) и уже там, под пиво, обсуждать. Ресторан для этого мало подходит. Скорее придется снять какое-нибудь кафе. Под такое дело, я даже готов сдать взнос.
    • Ну и, я надеюсь, слайды и записи будут выложены на сайт.

    среда, марта 10, 2010

    Versions

    Я уже как-то писал про нумерацию версий на RSDN. Возникла нужда сослаться, так что выношу тот пост сюда с некоторыми изменениями.

    Для чего вообще нужна нумерация версий? Самый простой и очевидный ответ: для идентификации сборок выходящих из рук разработчиков. Номер версии должен помогать отвечать на вопросы: Это одинаковые версии или разные? Какая из них была выпущена раньше? Что поменялось со времени предыдущей версии? Что нужно сделать, чтобы собрать точно такую же версию?

    Не стоит путать внутренний и маркетинговый номера версий. Если меркетинг хочет чтобы какая-то версия называлась Gold Enterprise Platinum Edition 7.0 то это никак не влияет на внутренний номер версии.

    Простая и в то же время достаточная система именования версий используют комбинацию из четырех чисел, записанных через точку: major.minor.hotfix.build

    Первые три числа это уникальный номер той версии, которая выкатывается пользователю. Они нужны для того, чтобы одним взглядом оценивать различия между версиями и степень их совместимости. Эти числа назначаются по определенным правилам.

    major — основной номер версии. Переход от одной основной версии к другой означает существенные изменения, часто вызывающие несовместимость с предыдущей версией без дополнительных ухищрений. Смена формата данных, глобальный рефакторинг, редизайн, существенное изменение функционала — достаточные основания для увеличения версии. Исправления багов, даже самых злокозненных, нет. Это число может только инкрементироваться. Увеличение major версии на единицу сбрасывает minor и hotfix в ноль. Традиционно, до релиза major версии равен нулю, с тем, чтобы в момент релиза выкатить пользователям 1.0.0.xxxx и в дальнейшем считать от него.

    minor — дополнительный номер версии. Это номер небольшого изменения в рамках версии. Это может быть появление нового функционала, совместимое изменение формата данных и т.п.

    hotfix — порядковый номер хотфикса. Часто бывает, что все изменения в версии связаны с исправлением багов, а не с добавлением функционала. В этом случае изменяется номер хотфикса. Очень часто, такая версия отдается в виде патча для обновления с предыдущей версии.

    Четвертая цифра это порядковый номер билда. Номера билдов сквозные и не зависят от версий. Это необходимо для того чтобы иметь возможность воспроизвести баг, проявляющийся у клиента точно на той версии, какая установлена у него. Чаще всего используют последовательный номер или номер ревизии из VCS.

    В более сложных случаях, номер версии может включать другие переменные. Например, версия плагина, часто включает номер первой совместимой версии родительской системы. В случае, если пользователю выкатывается сборка из нескольких систем, каждая из которых имеет свою версию, сборка имеет собственную нумерацию версий.

    понедельник, марта 01, 2010

    Bug theory - Initialization flow bug

    С простыми багами вроде покончено. Перейдем к более сложным.

    Initialization flow bug

    Симптомы:

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

    Критические места:

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

    Проблема в том, что инициализация отдельных компонентов происходит позже, чем их первое использование. В многопоточном случае все может быть осложнено возможным race condition.

    Профилактика:

    Локализовать инициализацию в одно месте, практиковать Dependency injection, писать интеграционные тесты, при добавлении зависимости анализировать порядок инициализации для разных случаев.

    среда, февраля 24, 2010

    Bug theory - Redundant expectations

    Redundant expectations

    Симптомы:

    В определенных условиях система делает не то, что ожидается. Баг проявляется только в частных случаях, все остальное время система работает нормально. При воспроизведении бага могут возникнуть проблемы, но если воспроизводить тестовый сценарий в точности баг всегда проявляется.

    Критические места: любые межмодульные взаимодействия, особенно слабо изученными сторонними модулями.

    Это типичный баг "на границе". В определенных условиях сторонний модуль ведет себя не так, как ожидалось. Чаще всего это происходит когда документация на интерфейс была прочитана невнимательно или изначально была неполна. Хороший пример, это NullPointerException при нарушении принципа Деметры.

    person.getLeftHand().getRingFinger();

    Для большинства людей это сработает, но на первом же одноруком мы получим NullPointerException.

    Профилактика:

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

    среда, февраля 17, 2010

    Bug theory - TODO: Implement

    TODO: Implement

    Симптомы:

    Не происходит то, что ожидалось. Выбрасывается NullPointerException, OperationNotSupportedException, InvalidArgumentException или любое другое похожее исключение. Такие баги легко обнаруживаются и легко воспроизводятся. При анализе в коде обнаруживается комментарий "TODO: Implement" или просто отсутствие необходимого кода.

    Критические места:

    Код после рефакторинга, новые фичи, реализации интерфейсов.

    Тоже простой и очевидный баг, который появляется когда разработчик по какой-то причине оставляет часть контракта нереализованной. Это может происходить по разным причинам. Лень заниматься пустой работой, противоречивый или слишком детальный интерфейс, отсутствие времени.

    Профилактика:

    Избегать противоречивых и слишком сложных интерфейсов. После рефакторинга обязательно проверять появившиеся TODO/FIXME.