Блог 7even

про ruby, rails, sinatra, git и всё на свете

Сказ о Deadlock-ах

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

Бэкграунд

Есть rails-приложение, построенное по принципу RIA, в котором фронт-энд логически разделен с бэк-эндом. Фронт-энду нужно знать, что происходит на серверсайде, и поэтому раз в секунду приходит запрос на некий урл, где определенный экшен определенного контроллера производит какие-то действия и рендерит ответ в формате JSON.

И среди действий этого контроллера есть обновление времени последнего доступа. Реализовано оно одним UPDATE-запросом примерно следующего вида:

1
UPDATE items SET access_time = NOW() WHERE id IN (34256, 34978, 34147)

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

1
2
3
4
5
6
7
postgres[22432]: [30-1] ERROR:  deadlock detected
postgres[22432]: [30-2] DETAIL:  Process 22432 waits for ShareLock on transaction 189302415; blocked by process 22443.
postgres[22432]: [30-3]  Process 22443 waits for ShareLock on transaction 189302416; blocked by process 22432.
postgres[22432]: [30-4]  Process 22432: UPDATE "items" SET "access_time" = '2011-07-08 08:49:03.429301' WHERE "items"."id" IN (691, 690, 692, 689, 686, 688, 687)
postgres[22432]: [30-5]  Process 22443: UPDATE "items" SET "access_time" = '2011-07-08 08:49:03.414084' WHERE "items"."id" IN (686, 687, 688, 689, 691, 690)
postgres[22432]: [30-6] HINT:  See server log for query details.
postgres[22432]: [30-7] STATEMENT:  UPDATE "items" SET "access_time" = '2011-07-08 08:49:03.429301' WHERE "items"."id" IN (691, 690, 692, 689, 686, 688, 687)

Краткое введение в Ruby, ч. 1

Эта статья посвящается моим друзьям/знакомым PHP- (и не только) программистам, которым лень/некогда (зачеркнуть второе лишнее) познакомиться с Ruby и освоить азы этого языка.

Установка

Ruby предоставляет интерактивную консоль irb, в которой можно вводить код и сразу видеть результат его выполнения (аналогично js-консоли в firebug). irb устанавливается вместе с самим Ruby, поэтому будем ставить его.

Сразу обращу внимание - есть способ пропустить этот шаг, и перейти на tryruby, где аналогичная консоль (с некоторыми разумными ограничениями) доступна прямо в браузере. Для тех, кто не боится процесса установки (а, возможно, Ruby уже есть в вашей системе), этот раздел.

Windows

Честно говоря, ни разу не пробовал заниматься подобными извращениями, но говорят, что это работает (там в комплекте и Ruby, и рельсы, и еще докучи всякого небесполезного барахла).

Mac OS X

Начиная с 10.5 (а, возможно, и с 10.4, точно не знаю) Ruby предустановлен в системе, и достаточно просто открыть Терминал.app и набрать irb; но там предустановлена стремительно устаревающая версия 1.8, и стоит установить последнюю самому. Самый удобный способ - это использовать RVM:

1
2
3
4
5
6
7
8
9
10
11
# ставим сам RVM
$ bash < <(curl -s https://rvm.beginrescueend.com/install/rvm)
# добавляем загрузку RVM в шелл
$ echo '[[ -s "$HOME/.rvm/scripts/rvm" ]] && . "$HOME/.rvm/scripts/rvm" # Load RVM function' >> ~/.bash_profile
# и перезагружаем .bash_profile (вместо этого можно открыть новую сессию в консоли)
$ source .bash_profile
# теперь ставим последнюю версию Ruby
$ rvm install 1.9.2
# и ставим ее активной для текущей сессии
# и дефолтной для всех последующих сессий
$ rvm use 1.9.2 --default

Пользователям zsh нужно заменить .bash_profile на .zshrc - хотя они и так об этом знают.

При ручной установке Ruby необходимо ставить дополнительные библиотеки вроде readline, и подключать их при компиляции интерпретатора Ruby - RVM делает это автоматически.

Linux

Ruby можно поставить из репозиториев большинства дистрибутивов, но иногда приходится подолгу ждать обновления пакета, чтобы получить свежую версию Ruby; также всегда можно собрать из исходников, но не все любят этим заниматься (особенно пользователи дистрибутивов, в которых установка пакета сводится к команде наподобие sudo apt-get install ruby); поэтому, опять же, я предлагаю использовать RVM, описанный в предыдущем подразделе.

Git Detached HEAD

На неделе с рабочим git-репозиторием случилась занимательная история.

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

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

На следующее утро я пришел в офис и по привычке решил сделать pull перед началом работы, но он почему-то выдал мне коммит далеко не первой свежести, причем с пометкой (forced update). Не помню, зачем, но я снова сделал pull (или fetch) - и каково же было мое удивление, когда второй раз он выдал правильный последний коммит, сделанный мной вчера вечером!

Последующие фетчи и пуллы работали аналогичным образом, через один - то выдается forced update со старым коммитом, то правильный HEAD. Попытки сделать новую локальную ветку, новую локальную копию, даже новый идентичный прежнему репозиторий на сервере - ничего не принесли.

Работа с несколькими соединениями с БД в Rails3

Вместо предисловия

В первой статье я расскажу о проблеме, которая встала передо мной при работе над rails-админкой для проекта, написанного на php/PostgreSQL.

Проект был в общем-то реализован до меня, и когда я пришел в команду, у него было 3 интерфейса: для пользователей, для менеджеров, и для админов. Причем, если первые два работали в любых браузерах и были выполнены практически в одном и том же дизайне, то админка работала исключительно в IE, и вроде бы использовала какой-то ActiveX (или что-то подобное, совместимое исключительно с виндой).

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