17 апреля, 2011

Fair lock

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

Переделайте следующий код оставив его многопоточным таким образом, чтобы лампочки зажигались и гасли строго по очереди и в любой момент времени должна быть включена только одна лампочка:
package me.bazhenov.bulb;

public class Main {

 public static void main(String[] args) {
  new Thread(new Bulb("first")).start();
  new Thread(new Bulb("seconds")).start();
 }
}

public class Bulb implements Runnable {

 private final String name;

 public Bulb(String name) {
  this.name = name;
 }

 public void run() {
  Thread self = currentThread();
  while(!self.isInterrupted()) {
   System.out.println(name + " bulb is on");
   try {
    sleep(300);
   } catch (InterruptedException e) {
    self.interrupt();
   }
   System.out.println(name + " bulb is off");
  }
 }
}

Кандидат предложил использовать ReentrantLock в FairSync режиме. В первом приближении эта идея может показаться рабочей. Передать общий лок в оба оъекта типа Bulb и синхронизироватся там на нем. Тем не менее, этот подход не работает. Если мы заглянем в документацию к классу, то увидим следующее описание:

The constructor for this class accepts an optional fairness parameter. When set true, under contention, locks favor granting access to the longest-waiting thread. Otherwise this lock does not guarantee any particular access order. [...] Note however, that fairness of locks does not guarantee fairness of thread scheduling.

FairSync не гарантирует отсутствие race condition'а между потоками. Единственное что он гарантирует это то, что лок возьмет поток который ждал на локе дольше всего. Отсутствие контроля за CPU шедулером не дает нам гарантии что в момент когда поток отпускает лок его оппонент уже попытался сделать acquire на этом же локе (что необходимо для того чтобы сработал FairSync в этой задаче).

К сожалению у меня нет под рукой соответствующего железа, но я подозреваю что на однопроцессорной машине разницы между FairSync и NonfairSync вообще не будет, так как у параллельного потока не будет возможности поставить в очередь заявку на acquire, чтобы при следующем unlock'е его заявка была обслужена.

Правильное же решение задачи остается на совести читателя :)

09 апреля, 2011

Маленький Билд и его друзья

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

Что-то похожее происходит в коллективе с налаженным build процессом, когда разработчик коммитит изменения в систему контроля версий.

О пользе налаженного процесса билда известно много. Но "путь на production" не прост. После того как код написан и перед тем как он будет запущен на production'е было бы неплохо сделать следующие вещи:

  1. откомпилировать код если вы используете компилируемый язык (удивил, да?);
  2. прогнать модульные и интеграционные тесты если они у вас есть;
  3. выполнить статический анализ кода и/или другие проверки которые позволяет выполнить ваша платформа и которые имеют смысл для вашей команды (code style check, code coverage, цикломатическая сложность, dependency matrix и т.д.);
  4. собрать артефакт приложения (желательно в виде одного файла), содержащий в себе весь код и ресурсы необходимые для запуска приложения на целевой платформе;
  5. выполнить приемочное тестирование, если оно у вас есть;
  6. опубликовать артефакт в репозитории (предположительно локальном), чтобы другие члены комманды могли им воспользоватся в своих целях (особенно актуально, если вы разрабатываете билиотеку, а не приложение);
  7. в случае если вы разрабатываете приложение а не библиотеку, произвести deploy в окружение staging тестирования чтобы команда и менеджеры могли оценить текущее состояние проекта.

Этот список действий не догма и варьируется от потребностей проекта. Я же расскажу что и как делаем мы. Сразу отмечу что у нас в проекте используется две платформы: PHP и Java. Я преимущественно буду описывать как мы собираем Java проекты, так как за счет поддержки инструментария билд процесс там получается более целостный и вразумительный. Я думаю, этот опыт будет полезен читателям.

Итак код закоммичен в систему контроля версий. С этого момента начинается его маленькое путешествие на production. Но для начала давайте познакомимся с главными героями. Для того чтобы успешно проводить автоматизированною сборку проекта необходим определенный инструментарий.

В главных ролях

Система контроля версий (VCS)

Какую VCS систему использовать решать вам. Это может быть старый проверенный временем subversion, или модно-распределенный git/mercurial. Но она должна быть. Если на вашем календаре уже 2011 год и вы все еще не пользуетесь какой-либо системой контроля версий, то я настоятельно советую вам переоценить принципы согласно котрым вы принимаете решения.

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

Система сборки проектов (build tool)

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

  1. любой релиз в отличии от исходных кодов должен работать. Подтверждатся это должно каким-либо видом тестирования (хотя бы ручным). Исходные коды же могут находится некоторое время в нерабочем состоянии (например, во время рефакторинга);
  2. релиз может содержать third party библиотеки и программное обеспечение, которое разрабатывается и поддерживается третьими лицами. Хранить все это в системе контроля версий может быть не самым удобным решением;
  3. платформа на которой работает приложение может иметь ограничения на формат релиза. Например, может быть строго определен формат архива с приложением (например, rpm или deb). Хранить исходные коды в VCS в том же формате может быть очень неудобно c точки зрения разработки.

Выражаясь математически: релиз = ƒ(исходники).

Для сборки мы используем Apache Maven, -- это довольно зрелая и продвинутая система сборки, которая впрочем не так проста в изучении как ее более "легковесные" братья вроде ant и gradle (хотя ant — это скорее бабушка, а gradle внучатый племянник). Gradle в последнее время получил довольно много положительных отзывов и продолжает набирать популярность, поэтому вам определенно стоит посмотреть на него, если вы определяетесь с вопросом выбора системы сборки Java-проекта.

Для того чтобы использовать maven более эффективно, у нас есть определенная экосистема для его поддержки.

Во-первых, это репозиторий артефактов о котором я расскажу ниже.

Во-вторых, у нас есть общийэ POM-дескриптор для всех проектов компании, который определяет настройки компилятора, сразу делает доступными библиотеки повсеместно используемые в нашей компании (TestNG, hamcrest, logback и т.д.), а также настраивает плагины для статического анализа кода и логгирования и т.д.

В третьих, у нас есть несколько прототипов проектов (archetype в терминологии maven), которые позволяют одной командой из консоли создать новый проект. Эдакий hello world с уже настроенным логированием, Jetty для тестирования, ActiveMQ и Spring Integration для обработки сообщений, Spring в качестве web-framework'а и много еще чем. Все это очень сильно упрощает старт, особенно людям не знакомым с премудростями настройки всего этого "зоопарка".

Репозиторий артефактов

Еще один вопрос связанный с системой билда — куда ложить его результат? После того как мы собрали приложение и протестировали его, нам надо опубликовать релиз. Необходимо общее централизованное место где бы хранились все артефакты, чтобы любой разработчик знал где искать последний релиз продукта. Позже вы можете достать его и использовать для deploy'я на production или в окружение staging тестирования. Если вы разрабатываете библиотеку то релиз нужен другим членам команды, для того чтобы использовать ее в своем приложении.

В простейшем случае роль репозитория может играть web-сервер. Его настройка для этих задач не займет у вас много времени. Либо это может быть FTP-сервер или сетевой диск доступный всем разработчикам. Так же эту роль может играть continious integration сервер, речь о котором пойдет ниже.

В более сложных случаях удачным решением может быть специализирванное ПО. Особенно это актуально если вы используете Maven, который специфицирует формат репозиториев и протокол работы с ними (поверх HTTP).

Мы используем Artifactory в качестве репозитория, который выполняет несколько ролей в нашей экосистеме:

  1. хранит артефакты делая их (и их исходники) доступными для использования любым программистом в любое время;
  2. кеширует third party библиотеки используемые программистами. Если вы начинаете новый проект и хотите использовать какую-то библиотеку, с высокой долей вероятности она уже есть в локальном репозитории и вы получите ее моментально не дожидаясь загрузки из интернета.

Continuous Integration сервер

Не смотря на грозное название, CI-сервера по своей сути — это триггеры билд процесса которые предоставляют разработчикам удобный способ контроллировать процесс и результат сборок. Они делают несколько вещей:

  1. позволяют конфигурировать политику запуска сборки (при коммите в VCS, через заданные интевалы времени, после успешной сборки зависимого проекта, вручную, и т.д.);
  2. предоставляют возможность следить за процессом сборки публикуя ее логи через web-интерфейс;
  3. предоставляют отчеты по результатам билда (проваленные тесты, предупреждения статического анализатора и т.д.);
  4. строят тренды на основании истории сборок (количество тестов, время затраченное на исправление билда, процент успешных сборок и т.д.);
  5. могут успешно играть роль репозитория артефактов.

Строго говоря использование CI-сервера не обязательно. Более того, на начальных этапах я бы рекомендовал делать сборки на машинах разработчиков. Это позволит сэкономить вам время в процессе допиливания build-процесса, которое на начальных стадиях неизбежно.

Мы до сих пор не используем CI-сервер для сборки билиотек. Библиотеки собираются и релизятся самим программистами. Несмотря на то что этот подход имеет ряд недостатков, он существенно проще.

В качестве CI-сервера мы используем TeamCity, бесплатной версией которой мы полностью довольны. Одна из привлекательных фич этого продукта состоит в том что он предоставляет неплохую статистику по билдам, включая success rate сборок, количество тестов и т.д.

Поезд отправляется

Итак как это все происходит на практике.

За системой контроля версий следит continuous integration сервер. Как только он замечает какие либо изменения, он делает checkout свежей версии и инициирует процесс сборки проекта.

TeamCity позволяет очень детально настроить с какими параметрами будет запущен build. Вы можете предопределить значения системных переменных нужных вашему процессу билда. Эти переменные могут использоваться например для того чтобы специфицировать на какой БД будет выполнятся тестирование.

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

Если тесты проходят, то "состав движется дальше". Следующим шагом является сборка артефактов. В терминах continious integration артефакт это любой результат процесса сборки, главная черта которого — воспроизводимость. Для нас это означает что артефакт является процессом работы машины, а не ручных действий человека.

Обратите внимание, что в смежных областях знаний, например в configuration management, термин "артефакт" имеет немного отличные значения.

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

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

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

Модульные тесты тестируют классы в изоляции от других частей системы, а также в изоляции от внешних систем, таких как базы данных и веб-сервисы. Их задача состоит в том чтобы давать программисту быстрый фидбек относительно того, работает система или нет безотносительно того верно ли она интегрируется с внешними источниками данных. Фактически, любой программист должен иметь возможность сделать checkout исходников проекта, запустить модульные тесты на абсолютно не подготовленной машине (например, без базы данных) и тесты должны выполнится. Единственная причина почему они могут не выполнится, это если в коде приложения допущена ошибка.

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

В сухом остатке. Модульные тесты должны:

  1. выполнятся быстро. Потолок 15-20 секунд, затем их просто перестают использовать. Здесь нужно обратить внимание на то, что модульные тесты должны выполнятся именно на стороне программиста и как можно чаще. И конечно же обязательно перед коммитом в систему контроля версий. На CI-сервере они выполнятся лишь для полноты тестирования;
  2. быть изолированными от внешнего окружения и выполнятся даже на машине без сети.

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

  1. могут требовать довольно сложное окружение для своего выполнения (конкретную БД, возможно даже конкретной версии);
  2. могут быть довольно медленными, так как включают в себя взаимодействие по сети, зачастую с системами производительность которых находятся под контролем третьих лиц;
  3. являются гораздо более хрупкими чем модульные, потому что опираются на заранее установленное окружение, а также на программное обеспечение которое скорее всего меняется без оглядки на ваше конкретное приложение.

Тем не менее тестировать все равно надо, поэтому такие тесты выделяются в отдульную группу и запускаются на CI-сервере. Настроить и поддерживать в актуальном состоянии сложное окружение проще один раз на сервере, чем десять раз на машинах разработчиков. Разработчик может запустить интеграционные тесты у себя, но для этого он должен настроить хотя бы часть окружения на своей машине, что может быть довольно непросто. Частично эти проблему можно решить при помощи виртуализации и таких инструментов как puppet.

Обычно интеграционные тесты на стороне разработчика запускаются только в случае если менялся интеграционный код системы и как правило только тесты на изменившуюся часть системы. С точки зрения maven (а именно, maven-surefire-plugin) интеграционные тесты отличаются от модульных только постфиксом (*Test у модульных, *IT у интеграционных). После того как тесту задан соответствующий постфикс, он автоматически начинает запускатся на нужной фазе сборки проекта.

Если с интеграционными тестами все хорошо, то запускаются приемочные. Приемочные тесты проверяют систему по типу черного ящика. Если вы разрабатываете web-приложение, то оно запускается на web-сервере и тест под видом обыкновенного пользователя начинает ходить по нему и проверять его работоспособность. Пожалуй, это один из самых сложных видов тестирования. В нем довольно легко наделать ошибок, которые могут существенно увеличить стоимость поддержки тестовой инфраструктуры. Это в конце концов делает процесс тестирования менее эффективным. Впрочем, тема приемочного тестирования выходит далеко за рамки моего поста, поэтому я не буду заострять на этом сейчас внимание. Людям искушенным очень советую прочитать книгу Джеза Хамбла и Девида Ферли Continuous Delivery.

Ну а мы отправляемся дальше. Следуюшая остановка — статический анализ кода. Мы используем findbugs для анализа кода. Статические анализаторы действительно находят ошибки и потенциальные уязвимости, так почему бы не делать это при каждом изменении наших исходных файлов.

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

Но, данный шаг можно порекомендовать не всем. Во-первых, для этого требуется существенная поддержка инструментов. Например, для php нет ничего подобного. Во-вторых, получаемая от статического анализатора информация должна оцениваться прагматически. Помните что анализатор показывает риски с формальной точки зрения. У каждого риска есть вероятность материализации и стоимость исправления. Вероятность материализации — это вероятность с которой риск превратится в проблему (то есть вы сможете наблюдать его воочию). Стоимость исправления — это сколько ресурсов (например, в виде человеко часов) нам надо будет потратить если риск все же материализуется в проблему. Перемножив эти два числа мы получим математическое ожидание проблемы. Может оказаться так, что математическое ожидание проблемы гораздо меньше чем стоимость внесения изменений в код здесь и сейчас. Некоторые проблемы дешевле исправлять пост фактум.

После того как статический анализ кода законечен, закончен и процесс непосредственно сборки. Теперь нам осталось опубликовать артефакт в общедоступный репозиторий и можно рапортовать об успешности билда.

Деплой у нас происходит в два этапа. Первый этап это деплой в локальный репозиторий артефактов. Второй этап включает в себя автоматическую доставку приложения на production сервера. Обратите внимание, доставку, но не deploy. Deploy осуществляется только по инициативе и под контролем разработчика. Таким образом, если разаботчик работает над проектом search-service версии 1.0 и на TeamCity успешно выполнился билд под номером 134, то на production кластере в папке проекта автоматически появляется файл search-service-1.0.134.war, который содержит в себе все необходимое для работы приложения и готов к развертыванию по команде разработчика. Это позволяет свести к минимуму участие человека в процессе билда. Человек привлекается только там где его внимание и возможность принимать решения не может заменить машина.

Вот пожалуй и все. Некоторые моменты остались за кадром. Например, как происходит запуск (deploy) приложения на production серверах. Но это тема совсем другого разговора.

Буду рад услышать отзывы и success stories читателей.

14 ноября, 2010

Как я собирал NAS

Коллеги давно просили меня описать процесс сборки и настройки своего NAS-сервера. Этим постом я искупаю свою вину. К тому же тема действительно актуальная и, мне кажется, многим будет интересно с какими проблемами я столкнулся, какое железо и софт использовал.

NAS у меня исполняет несколько обязанностей:

  • файловое хранилище "жирного" контента (фильмы, etc);
  • сервер для Apple TimeMachine (бекапы);
  • качает и раздает торренты;
  • "самопальный" мониторинг сетевой активности с использованием rrdtool;
  • локальный git-репозиторий.

Мой опыт использования NAS начался с QNAP TS-109 PRO II.

Довольно удачная однодисковая железка, которая прослужила мне верой и правдой более года. У нее несколько минусов:

  • собственно всего один диск;
  • нестандартный встроенный linux;
  • ARM-based процессор.
Один диск меня не устраивал по причине отсутствия резервирования. Второй и третий пункт меня напрягали, так как сам я довольно уверенно пользуюсь linux и embedded версия ОС предлагаемая QNAP вкупе с ARM процессором не устраивала меня по ряду причин: сборки не для всех пакетов есть под ARM, а те которые есть, редко обновляются и иногда содержат странные баги (у меня появился довольно изрядный опыт "войны" с rtorrent'ом). Конечно, можно было бы собирать софт из исходников, но это осложнено следующими факторами:

  • установка компилятора и development библиотек тоже довольно нетривиальна (видимо проблема курицы и яйца);
  • будучи программистом с серьезным отношением к тестированию я отношусь к той категории людей которые считают, что если ты сам компилируешь пакет, ты должен его сам и тестировать. А мне этого совсем не хотелось.

Таким, довольно эволюционным путем, примерно через полтора года я пришел к мысли что пора менять storage. На этот момент на рынке было несколько device'ов, которые привлекли мое внимание:

Моя вера в x86 на этом поприще росла с каждой новостью связанной с Intel Atom. Поэтому два первых девайса, опять таки построенные на базе ARM процессора, отсеялись сами собой. Два последних представляют уже больший интерес, но анализируя их спецификации я пришел к мысли что я бы мог сам собрать более подходящую мне железку.

Hardware

Я начал подискивать подходящий miniITX корпус. Мягко говоря это не просто, особенно во Владивостоке. Большинство корпусов в этом форм факторе нацелены на создание nettop'ов: имеют максимум 2 корзины под HDD, кучу лишних фентифлюшек вроде headphones output на фасаде и т.д. Мне же хотелось собрать железку не имеющую ничего лишнего и при этом "на вырост", чтобы через пару лет не пришлось менять снова.

В конце концов я нашел корпус, который меня полностью удовлетворил — Chenbro ES3469.

Этот корпус специально создавался под NAS, поэтому обладает довольно характерными особенностями:

  • четыре корзины для 3.5" HDD с поддержкой горячей замены и индивидуальной индикацией питания и data activity;
  • один внутренний слот для 2.5" HDD (очень удобно так как все четыре жестких можно отдать под хранение данных, а ОС поставить на отдельный пятый жесткий);
  • внешний блок питания, что позволяет сэкономить место внутри и избавится от одного лишнего вентилятора (внешний БП без активного охлаждения);
  • довольно "богатая" индикация на передней панели.

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

Немного огорчило меня то, что одна из корзин сразу не заработала. Сначала я грешил на backplane, но слава богу дело было в неисправном SATA кабеле, который легко поддается замене. Замена правда осуществляется не самым простым образом, — для того чтобы добраться до отсека с корзинами и до backplane'ов, надо буквально говоря разобрать весь корпус, что не очень удобно. Что уж тут поделать, издержки форм фактора.

Следующим шагом стал выбор материнской платы. С ней тоже все оказалось не просто. Надо было найти Atom-based miniITX материнскую плату с полностью пассивным охлаждением и минимум 5 SATA портами! Немного странные требования для форм фактора miniTX, не находите? Такая все же нашлась — Supermicro X7SPA-H:

  • двухядерник Intel Atom D510 1.6Ghz с поддержкой HT;
  • 6 SATA портов;
  • два SO-DIMM DDR2 667MHz модуля, максимум 4Gb.
  • две гигабитные сетевые карты;
  • полностью пассивное охлаждение;
  • довольно простая видеокарта Intel GMA3150, что в моем случае является плюсом, будет меньше кушать и греться;
  • поддержка power on after failure, что для девайсов такого класса очень полезно.

Вобщем, эта материнская плата подошла в моем случае просто идеально. Причем, материнская плата и корпус, что называется, соответствуют друг другу. Например, у обоих есть поддержка индикации power fail и chassis intrusion.

Правда, тут произошел казус. Сразу материнка не завелась: питание есть, вентилятор на корпусе вращается, а power LED не горит и стандартного писка о прохождении POST check'а нету. Отключил всю периферию, проверил что нигде ничего не замыкает, проверил память, — один черт, не заводится. Сбросил CMOS, — не заводится. Причем, если достать RAM, то матерится о ее отсутствии на всю квартиру. В конце концов, на третий день, когда вера в то что материнка жива окончательно иссякла, она при самых странных обстоятельствах (во время очередной операции "выткни-воткни оперативку") завелась. То ли чудо, то ли фокус... То ли я два или три десятка раз подряд неверно вставлял оперативную память.

Дальше стало проще. В качестве жестких я выбрал Western Digital линейки Caviar Green, так как они хорошо себя зарекомендовали еще со времен моего первого NAS'а — тихие и холодные. Сейчас у меня стоят два: WD10EADS и WD10EARS. Насколько я понимаю, отличаются они только объемом кеша 32/64Mb.

Так как я старался собрать максимально тихий device, один из вентиляторов в отсеке с жесткими я отключил, а на второй поставил zalman'овский регулятор скорости вращения. Эмпирическим путем была подобрана скорость, которая дает тихую работу. Температура при этом держится в районе 40-42 градусов, что меня вполне устраивает.

Software

Теперь немного об используемом программном обеспечении. Я "на короткой ноге" с Ubuntu, поэтому я выбрал именно этот дистрибутив. Так же, наслушавшись страшных историй о том как восстанавливают данные с RAID-зеркал, я сразу принял решение использовать файловую систему с поддержкой избыточности вместо аппаратного RAID'а.

Собственно, сейчас таких файловых системы две: BTRFS и ZFS. Первая находится на стадии беты и не предназначена для production использования. Вторая довольно стабильная и широко используемая, но в linux реализована в рамках проекта FUSE из-за лицензионных ограничений (изначально ее разработала Sun для ОС Solaris). По совокупности факторов я решил использовать ZFS.

ZFS вещь довольно интересная. Она позволяет собрать из списка блочных девайсов виртуальный том с заданными настройка избыточности. У нее есть ряд преимуществ перед аппаратным RAID'ом:

  • в зеркале могут участвовать абсолютно разные жесткие (разного размера);
  • выход из строя блочного девайса не запрещает клиентам читать и писать на том, до тех пор пока есть хотя бы одна живая реплика;
  • rebuild тома (scrub в терминах ZFS) тоже не блокирует клиентов, они по прежнему могут читать и писать;
  • настройки избыточности можно задавать отдельно для директорий. То есть вы можете хранить все в двух копиях, а особо важные для вас папки в трех (для этого том должен состоять как минимум из трех физических дисков).

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

Создание ZFS тома делается очень просто:

zpool create tank raidz1 /dev/sda /dev/sdb

После создания, том будет автоматически примонтирован в /tank. Теперь можно заливать на него информацию. Просмотр статуса по виртуальным томам делается следующими командами:

zpool status
zpool iostat -v

Перебалансировка и починка тома осуществляется при помощи команды:

zpool scrub tank

В интернете есть множество информации о ZFS. Если хотите использовать ZFS, советую обратится обратится к официальному мануалу.

Надо понимать что такая гибкость не дается бесплатно. ZFS прожорлив по памяти, поэтому советую оснастить машину гигабайтом, а лучше двумя гигабайтами оперативной памяти. Я так и сделал, благо она дешевая.

С ZFS у меня произошел только один негатвиный инцедент. Во время начальной заливки данных на том, oomkiller'у не понравилось что драйвер ZFS ест много памяти, и он его убил :). Это издержки того что ZFS работает в user space. После reboot'а все стало нормально, ничего чинить не пришлось. Больше такого у меня не повторялось.

Так же, я использую NAS как сервер для Apple Time Machine, поэтому мне надо было настроить его так, чтобы мой mac mini воспринимал его как AFP сервер. В интернете есть полно мануалов, одним из которых я и пользовался.

Вот пожалуй и все что касается софта.

В итоге

Atom все же поменял мое представление о том, какой должен быть домашний файловый сервер. На базе атома вполне можно собрать тихий и холодный storage, и спихнуть на него "домашнюю работу". При этом это все это будет работать под управлением стандартного дистрибутива вашей любимой ОС (хоть windows). А сама железка мне обошлась даже немого дешевле чем Acer easyStore H340.

31 октября, 2010

HighLoad++ 2010 - впечатления

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

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

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

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

Организация была вполне приемлемая: был WiFi (который впрочем почему-то не ловил мой телефон) и достаточно еды (на которую у меня впрочем не было времени). Расписание было составлено таким образом, что самые интересные доклады параллельно не шли, а это радует.

Основные понравившееся доклады

Performance tweaks and tools for Linux / Joe Damato

В этом докладе рассказывается об основных инструментах профилирования под Linux — lsof, strace, ltrace, oprofile. Но об этом знают если не все, то почти все. Интересен этот доклад другим. Joe будучи Ruby хакером создал набор довольно интересных инструментов для профилирования Ruby приложений при помощи этих низкоуровневых инструментов.

Секреты Фейсбука: как выдержать 50 миллионов запросов в секунду / Robert Johnson

Robert Johnson — ведущий инженер по разработке ПО в компании Facebook (а по совместительству, как и я, владелец Jackson'а). Роберт много говорил не только о технической стороне масштабирования, но и об организационных мерах и том, каких направлений должен придерживаться коллектив участвующий в разработке высоконагруженного приложения. Основные посылы следующие:

  1. большому проекту рано или поздно прийдется столкнутся с горизонтальным масштабированием;
  2. все изменения должны быть инкрементальными (нет изменений, которые не могут быть отменены);
  3. если вы не можете что-то измерить, вы не можете этим управлять;
  4. работа в небольших независимых коллективах (от 3 до 7 человек) над разными проектами (фотографии, личные сообщения и т.д.);
  5. люди ответственные за что-либо должны иметь контроль над этим.

В личном общении с Робертом я смог узнать следующие вещи:

  • социальные графы очень тяжело partition'ить. Я спросил не пытались ли они заимствовать опыт MMORPG проектов? По идее у них схожие задачи, но гораздо более жесткие требования к latency. Роберт сказал, что это интересная идея, но ничего подобного они не пробовали делать. Так же он не особо верит в том что алгоритмы кластеризации графов смогут помочь им решить эту проблему в общем виде и достичь при этом вменяемой производительности;
  • их custom'ный протокол репликации в mysql, обошелся им в десяток строк кода, что и обусловило использование такого, на мой взгляд, странного решения;
  • в довольно большом проекте в угоду производительности коллектива в целом приходится принимать такие решения, как писать HipHop. Насколько я понял, Роберт не особо верит в его широкий adoption за пределами Facebook. Это довольно специфические решение проблемы в их специфическом окружении.

Быстрое развертывание шаблонов и статики в Mail.ru / Николай Кондратов

Несмотря на немного "попсовое" название, доклад был довольно интересный. Ребята рассказывали о том, как они организовали работу верстальщиков и какие инструменты они им дали для развертывания приложения на сервере. Основная особенность больших систем не только в высокой нагрузке, но и в очень сложных процедурах развертывания системы. Последний факт почему-то редко получает много внимания, а зря.

В mail.ru особенности процесса следующие:

  • шаблоны пишутся на псевдоязыке и позже могут быть транслированы в C/Perl;
  • шаблоны и статика доставляются на production отдельно от кода приложения, тем самым код и шаблоны могут меняться независимо. Схема выглядит довольно хрупко. На мой вопрос: "как вы избегаете несовместимых изменений между кодом и шаблонами?", они ответили: "мы внимательно следим за этим";
  • у каждого разработчика есть свой сетевой диск (который монтируется при помощи FUSE). Этот диск связан с отдельным для разработчика доменом, на котором крутится приложение;
  • артефакт с шаблонами и статикой доставляется на production машину, с которой посредством p2p сети (torrent) распространяется по frontend'ам. Исходная машина работает в режиме суперсидирования (то есть каждый блок отдается только единожды). Ребята объяснили такое решение тем, что в противном случае у них заканчивается network bandwidth на исходной машине (размер артефакта порядка 100Mb).

Progressive downloads and Rendering / Stoyan Stefanov (Yahoo)

Yahoo, как известно "помешана" на оптимизации на клиентской стороне. Этот доклад всецело посвящен такого рода оптимизациям. Много говорилось про user perception и то как люди оценивают производительность. О том что стабильность производительности (среднеквадратичное отклонение) важнее, чем абсолютные значения latency. Про аттрибуты async и deffered, которые предоставляет HTML5. Про то что такое chunked response и как его использует Google и Amazon. Как CSS может значительно снизить производительность вашего web-приложения. Доклад довольно довольно последовательный, так что очень советую посмотреть слайды а также видео.

Я из этого доклада я вынес следующее: performance is a feature (этот тезис всплывал на конференции не раз). Если вы хотите чтобы ваше приложение быстро рендерилось на стороне клиента (что очень важно еще и в связи с ростом процента мобильных клиентов), то к этому вопросу над подходить как к фиче. Несмотря на расхожее мнение, добавить такого рода оптимизации в уже существующее приложение написанное без оглядки на вопросы client side performance, может быть довольно непросто.

Thinking Clearly About Performance / Carry Millsap

"Пушка" первого дня. Несмотря на относительно низкую практическую ценность доклада, я считаю что излагаемая в нем информация очень важна, — она вправляет мозги. В докладе излагается последовательный, целостный, математический подход к оценке производительности и пропускной способности. Поэтому вдвойне жалко что зал был почти пустой: человек 15-20.

Основные постулаты:

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

Я очень советую прочитать одноименную публикацию, чтобы получить более полное представление об этом материале.

Закулисы

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

  • Sphinx очень популярен. Этим фактом, впрочем, никого не удивишь, но тем не менее полезно было получить тому подтверждение от людей реально его использующих;
  • Redis тоже довольно популярное решение, что для меня стало новостью. Я встретил по меньшей мере пятерых человек из разных компаний, которые используют это хранилище;
  • стандартная библиотека Scala изобилует довольно "детскими" багами как функциональными, так performance-related. Если вы хотите использовать Scala в крупном проекте, есть смысл подумать об использовании стандартной библиотеки Java, как бы грустно это ни звучало;
  • системы асинхронного обмена сообщениями тоже получили довольно широкий adoption. Очень много кто использует те или иные реализации очередей (в основном memcacheQ и Redis);
  • и несмотря на это, практически никто не знаком с EIP, основополагающим трудом в этой области;
  • вопреки расхожему мнению, вопрос найма специалистов стоит остро в любой точке нашей страны (а возможно и планеты). Владивосток, будучи относительно небольшим городом, в этом отношении не особо хуже Москвы.

Ложка дегтя

Впрочем, не все так безоблачно. До сих пор, я намеренно игнорировал негативные моменты связанные с конференцией:

  • некоторые доклады были попросту скучные, а некоторые я считаю вредные. Абсолютизм пропагандируемый в докладе "Практическое создание крупного масштабируемого web 2.0 проекта с нуля" это на мой взгляд один из худших феноменов, который может настигнуть специалиста. Масштабируемость в этом докладе провозглашается чуть ли не самодостаточным свойством системы ради которого и "крутится Земля". Попытки аудитории обратить внимание докладчика на то что иногда существуют определенные требования к гарантии в отношении consistency (которые влияют на принимаемые решения и выбор инструментов) не увенчались успехом;
  • некоторые участники в личном общении беззастенчиво утоляли собственные комплексы. Один участник в течении 10 минут пытался мне объяснить что настраивать GC в Java очень просто. Объяснения сводились примерно к следующему. В Java есть один стоящий GC — Parallel GC, все остальное — ошибка природы. Серьезно к такому мнению относится не получается (на самом деле, это еще один частный случай абсолютизма описанного в предыдущем пункте);
  • в рамках конференции был круглый стол об использовании SMP систем. Intel прислала на это событие не технического специалиста, а, если я не ошибаюсь, менеджера по связям с общественностью. Стоит ли говорить, что это не тот кого мы ждали;
  • вообще, в конце докладов оставалось очень мало времени на вопросы. Иногда доходило до смешного, в конце доклада организаторы говорили: "у нас есть время на два вопроса". Два вопроса?! Вы издеваетесь?! По факту, обсуждение докладов осуществлялось за кулисами в компании 5-10 особо заинтересованных участников и самого автора.

В сухом остатке

Участие в конференции составило 15 000 рублей. Для иногородних еще стоимость билетов и проживания в городе. Поэтому я бы дал следующий совет тем, кто собирается поехать на HighLoad++ в будущем. И пожалуй я это выделю в отдельный абзац.

Основной источник информации на такого рода event'ах — это личное общение.

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

Вот наверное и все. Если у вас есть еще какие-либо вопросы, с удовольствием отвечу на них.

30 августа, 2010

Keep it simple, stupid

Простота. Краеугольный камень нашей профессии. Наверняка вам приходилось слышать от своих коллег: “это можно сделать гораздо проще”. Или вы говорили это вашим коллегам: “смотри, можно сделать так. Это ведь гораздо проще”, а в ответ получали взгляд полный непонимания, как бы говорящий вам: “и это ты считаешь проще?”.

В программной инженерии определенно произошла инфляция слова “простота”.

Простота — это расстояние

Не существует общепринятого определения простоты. Тем не менее, если мы заглянем в словарь то увидим, что очень часто рядом со словом простота фигурируют такие понятия как: “предсказуемость”, “прямолинейность”, “ясность”, “очевидность”.

Что мы считаем простым? Процессы и объекты чье поведение легко предсказать. Если “оно” ведет себя так как я от “него” ожидаю, значит я понимаю как “оно” работает и скорее всего смогу “его” изменить не поломав. В противоположность этому, если “оно” ведет себя непредсказуемо, значит я не понимаю основополагающих принципов лежащих в “его” основе. И любая попытка “его” изменить может окончится таким же непредсказуемым результатом. В этом случае процесс постулируется слишком сложным.

Такие “предсказания” возможны благодаря тому что в голове человека есть ментальная (поведенческая) модель описывающая этот процесс или объект. И чем сильнее соответствует модель реальности, тем проще расценивается объект. Так что да, простота есть расстояние. Расстояние между ожидаемым и действительным.

Мера простоты — понятие субъективное

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

Любой взрослый человек без проблем восстановит порядок букв в алфавите, но это не значит что алфавит объективно прост. Это всего лишь значит что вам его вбили в голову в школе. На самом деле в порядке следования букв в алфавите нет никакой логики и очевидности. Объективно алфавит сложен и непонятен. Я думаю латинский алфавит по памяти восстановят гораздо меньше людей, даже при том что порядок сильно пересекается с кириллицей.

Но более интересна обратная ситуация. Если вы считаете какой-то процесс сложным, это еще не означает что процесс объективно непредсказуем и непонятен. Это может означать что у вас просто нет подходящей поведенческой модели. Возможно не стоит клеймить все вокруг сложным, возможно просто стоит... научится?

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

def qsort: List[Int] => List[Int] = {
  case Nil => Nil
  case pivot :: tail =>
    val(lessThan, greaterOrEqualThan) = tail partition(_ < pivot)
    qsort(lessThan) ::: pivot :: qsort(greaterOrEqualThan)
}

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

  1. создавать процессы близкие к ожиданию пользователей;
  2. создавать в головах пользователей новые поведенческие модели (учить пользователей).
​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​Помните этот комикс? ​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​

Последний продукт просто ужасен, верно? Или нет? Я незнаю. Мне ясна посылка этого комикса, — существует тенденция к чрезмерному усложнению вещей (или их недостаточному упрощению, как вам больше нравится).

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

Это кабина еще советского Ту-154М, реактивного самолета выпуска середины 80-х. Обычный человек не сможет идентифицировать большинство приборов, рычагов управления и их назначения. Значит ли это что интерфейс сложен для тех для кого он создавался, для пилотов?

Тенденции к упрощению существуют и в авиации, так что мы можем поискать более простой реактивный лайнер (так сказать, "для нубов"). Давайте посмотрим на кокпит широкофюзеляжного A-380, который начал эксплуатироваться в 2007 году. Считается что A-380 более прост в управлении. Об этом говорит даже тот факт что экипаж A-380 составляет два человека (против 4 человек в Ту-154М).

Аналоговых приборов почти не осталось, им на смену пришли цифровые приборы и LCD панели. Рычаги управления стали больше похожи на джойстик. Основным органом управления, судя по всему, стала клавиатура. Но стал ли он проще? Для пилотов может быть, для всех остальных никакой разницы. Если вы чувствуете себя как слон в посудной лавке в кабине Ту-154, тоже самое будет в кабине A-380.

Конечно, если вы можете создать продукт который будет решать задачи пользователя и при этом будет простым как электрочайник, то это просто прекрасно. Но есть сферы где человеку нужен полный контроль над происходящим и масса информации чтобы принять верное решение. И в этом случае может быть не лучшей идеей сводить интерфейс к одной кнопке в UI/одному методу в API и т.д.

Простота многомерна

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

Простота использования

Если речь идет об API или бибилиотеке, то как просто ее развернуть на клиенте и начать использовать?

Простота написания

Насколько просто писать код с использованием той или иной технологии. Как много кода прийдется писать? Как много ошибок я могу допустить в процессе написания? Здесь все просто, и зачастую именно об этом виде простоты идет речь когда два программиста спорят о простоте. Но это далеко не единственное (и иногда не главное) качество.

Простота тестирования

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

Именно поэтому для меня простота тестирования (тестируемость) — очень важное качество кода. Если я пишу код и не могу сделать так, чтобы верификацией работоспособности за меня занималась машина, то скорее всего этого кода не должно быть.

Простота верификации

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

Простота понимания

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

Это не исчерпывающий список. Вы можете изобразить все это на spider графике. Как и всегда, достичь максимума по всем направлениям вряд ли получится. Надо определить приоритеты и следовать им. Кому-то важнее простота написания, кому-то простота тестирования. Но в любом случае этот выбор должен быть осмыслен. Это то, что в наши дни очень тяжело найти — осмысленный выбор. В большинстве случаев выбор делается неявно в угоду упрощения процесса которым разработчик занимается именно сейчас. Это свойственно всем людям, — идти по пути наименьшего сопротивления.

Проблема в том, что упрощений в чистом виде не так много. Чаще приходится иметь дело с разменом. Упрощая одно, вы автоматически усложняете другое. Например, упрощение тестирования зачастую связанно с “расслоением” системы и усложнением ее понимания. Это размен на который мы идем, неважно отдаем мы себе в этом отчет или нет. Вопрос в том, выгоден ли этот размен для нас? Но это тема совсем другого разговора...

10 августа, 2010

Lock vs. Lease

Представьте себе типичную задачу, есть поток событий и этот поток событий надо разделить между несколькими пользователями системы. Типичный пример: модерация. Скажем, есть три модератора, которые просматривают добавляемый пользователями контент (допустим рекламные кампании). Мы хотим “распилить” поток событий между модераторами, чтобы они не делали одну и ту же работу дважды.

В самом простом случае, мы можем использовать mq в любом его проявлении. Пользователи просто берут сообщение из головы и обрабатывают его. Эта схема хорошо работает, но к сожалению не всегда применима. Например, если пользователи имеют возможность указывать критерии событий которые хотят обрабатывать. Последнее требование очень часто в том или ином виде всплывает в подобных задачах, потому что люди (точно так же, как и процессоры) плохо переносят context switch. Например, если я настроился на модерацию авто тематики, то я фигурально выражаясь загрузил в кеш ключевые слова относящиеся к этой тематике и буду очень расстроен если мне подсунут объект с тематикой относящийся к сотовым телефонам.

Окей, мы не можем использовать mq, что тогда? Далее приходит на ум следующее — хранить события в реляционной таблице (чтобы иметь возможность фильтрации) и брать shared lock на каждое из событий/объектов при их обработке. В данном случае lock’и позволяют исключить дублирующую обработку события. Ничего криминального в использовании локов для решения этой задач нет, но если вы начнете имплементировать подобное решение, то вы очень быстро прийдете к тому что lock’и обладают семантикой отличной от той которая необходима вам. И вот почему...

Lock timeout

Если мы говорим про web-приложение, то необходимо понимать что факт закрепления события за конкретным пользователем должен иметь timeout. HTTP — это stateless протокол. После того как вы закрепили событие за пользователем, он может просто закрыть браузер и пойти по своим делам. Если мы хотим чтобы любое событие было рано или поздно обработано, мы обязаны аннулировать устаревшие локи.

Но традиционный механизм lock’ов не имеет timeout’ов, и это решение вполне обосновано, так как обычно lock’и или используются внутри одного адресного пространства (а следственно проблемы исчезновения lock owner’а нет) или используются в distributed окружении, но имеют механизм autorelease’а (при разрыве соединения текущего владельца блокировки и координатора автоматически происходит release).

Это означает в традиционных use case’ах использовании lock’ов между текущим владельцем лока и координатором всегда есть канал связи, чего нет в нашем случае.

Immediate locking

В нашем примере, если мы не можем взять lock на событие это означает что данное событие уже кто-то обрабатывает. В этом случае нам надо просто пропустить это событие и попытаться заблокировать следующее. Но в большинстве случаев использования lock’ов, если lock уже взят, то клиент вынужден ждать своей очереди. Это вполе логично, так как действия выполняемые в разных потоках представляют собой различные не связанные между собой действия. Все они должны быть выполнены последовательно, для чего и используется lock.

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

Lease

Проблема в том, что люди видя эту семантическую разницу пытаются расширить интерфейс локов для того чтобы покрыть им данный use case использования. Это опасно, так как в механизм добавляется элемент autorelease’а, который легко может нарушить целостность данных. Представьте, что если при традиционном использовании локов (скажем, параллельном изменении коллекции элементов) autorelease произойдет до того момента когда владелец лока успеет выполнить операцию?

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

Помните DHCP? Это протокол автоматической конфигурации сетевого стека компьютера. Сегодня его можно найти буквально в любом домашнем маршрутизаторе. DHCP позволяет компьютеру автоматически получать IP-адрес и автоматически конфигурировать сетевой стек в зависимости от топологии сети в которой находится компьютер.

Обратите внимание, когда компьютер получает IP-адрес по DHCP, он не посылает запрос “дайте мне IP 192.168.1.15”. Он посылает запрос: “дайте мне любой свободный адрес”. В ответ он получает lease. Lease — это аренда адреса. Аренда имеет timeout, по истечении которого, если lease не был продлен, то аренда аннулируется и IP-адрес возвращается в список доступных. Вполне подходит под нашу задачу, верно?

В случае DHCP, клиенту по большому счету все равно с каким IP-адресом работать. В нашей задаче клиенту все равно какие именно события обрабатывать. Главное чтобы они удовлетворяли формальным требованиям о которых мы говорили раньше.

Модель аренды гораздо ближе к природе задачи. Во-первых, timeout’ы, которые нам просто необходимы, лежат в природе механизма аренды. Во-вторых, аренда не подразумевает владения объектом. Фактически в этот же момент с этим же объектом может происходить какая-то другая операция (например, индексация), которая скорее всего имеет возможность выполнятся параллельно, а значит данные находящиеся в объекте не являются предметом защиты от конкурентного доступа. Это сигнализирует о том что локи в данном случае не уместны. В этой задаче lock’и могут служить только инструментом обеспечения целостности списка арендованных объектов, но не более того.

P.S. интересно услышать к какими способами решения схожих задач приходилось сталкиваться вам?

05 июня, 2010

About motivation

Does anybody know why we do what we do? Why do we wake up everyday and go to our workplaces? It seems that the most obvious reason is money. We need money to function in the society. We need some funds to make some plans, if you wish.

It turns out that not only money motivate people to do their job. OSS is the proof. Well, yes, we can make money from open source projects. And a lot of companies do this already, but it's not the point. The point is that there is another force motivating people. Most good programmers like some of my colleagues and buddies work not because of money, but because they feel passion to write programs. Money is a required but not sufficient part of motivation.

Daniel H. Pink some time ago gave an excellent talk about motivation factors and how to use them in work process.

Some companies, like Atlassian, show outstanding results in the process of self organizing their employees. They simply said to their developers:

next 24 hours you could do whatever you want with whoever you want, but you should show results to the company within next 24 hours.

Looks familiar, right? Yes, it's some sort of Google's 20% culture.

Nevertheless, so called FedEx days was born (like FedEx Ground day-definite delivery, I believe):

Overall, I'd say we did pretty damn well. People built some kick ass projects, we all learnt about new technologies, and everyone seemed to have a good time.
[...]
Will we repeat the exercise? Definitely. I think it was great thing to do - developers got to play, exercise their minds a little, build some fun stuff and learn about new technologies.

There are a lot of really fun things provided by Atlassian FedExes. Which makes me believe in the words if Daniel Pink:

Pay people enough to take the issue of money off the table.

There is no need to pay people more that that. Instead of raising salary you would better invest more aggressively in creating an attractive and efficient work environment. If you work in a IT company give your team some time to share experience and thoughts about new technologies, provide really good hardware for your employees (it's so cheap anyway), and, holy crap, do not ever shape an Internet connection. I mean it, never.