About

Обновления Jabberdict

Jabberdict переехал с Appengine на свой родной сервер. В процессе переезда веб часть была переписана с использованием розовых пони, а XMPP часть превратилась в настоящий сервер на базе wokkel. Wokkel — эта такая штука, чтобы писать XMPP-сервера с использованием twisted; а twisted в свою очередь — это такая событийно ориентированная (сетевая) библиотека для python. Часто источником вдохновения служил xmppforum. Пользуясь случаем благодарю hoverhell за xmppforum и ценные советы.

Из положительных приобретений переезда стоит отметить значительную экономию дискового пространства (словари занимают ~120Мб, а не ~1Гб). Боты успешно перенесены на собственный домен (jabberdict.ru), а ещё у них появились vCard и ники.

XMPP оказался загадочным и существенно различным у разных клиентов. Но если внимательно читать документацию (и смириться в с тем, что Google Talk — это не совсем стандартный и весьма урезанный XMPP), то все встает на свои места.

Appengine вас ограничивает

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

  • Екатерина Волкова: если статья не найдена, найти похожие слова и выдать варианты.
  • Ged Korshunov.ru: придумать ботам более нормальные ники, т.е. заполнить vcard.
  • Rain: а чего для словарных ботов не использовать тот же домен, что и для сайта проекта — jabberdict.ru?

Похожие варианты

Классическим (в понимании компьютерных лингвистов) является расстояние Левенштейна, которое предложила Екатерина. Проблема в том, что при запросе вычислять это расстояние для миллиона (или даже ста тысяч слов) дело неблагодарное, а провести вычисления заранее невозможно.

Мне видятся два подхода: вычислять что-нибудь типа soundex для слова или использовать модель ошибок.

Soundex — это способ представления слова в соответствии с его звучанием. Первая буква слова сохраняется, остальные заменяются кодами в соответствии с таблицей; результирующий код имеет длину четыре символа (буква и три цифры). Для русского языка soundex не работает, но есть неплохие попытки адаптации (что-то сейчас хабр умер, но оживет, думаю).

Главная проблема такого подхода: он ловит неправильное написание (ошибочная замена гласных и т.п.), но не ловит опечатки. То есть, если перепутать пару согласных или написать слово в неправильной раскладке, то soundex уже не поможет.

С точки зрения Appengine такой подход влечет за собой распухание базы данных, а как вы все помните, бесплатный лимит уже почти исчерпан.

Второй способ довольно любопытен, а главное крайне лаконичен (21 строка на Python). Основная идея такова: мы вводим некую модель изменения слова (удаления, вставки и прочее), потом перебираем все варианты (на среднестатистическое слово их несколько сотен). Затем просто ищем по словарю все варианты «исправлений». Не сработало? Давайте сделаем две итерации.

Не лучший вариант с точки зрения производительности (~10 слов в секунду в худшем случае), но зато не нужно ничего хранить. Ну и потом мы же помним, что Datastore медленно пишет, но зато быстро читает?

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

vCard

Логичная просьба. Видеть в ростере бота с именем «Словарь Ожегова» куда приятнее, чем ozhegov@jabberdict.appspotchat.com. Но, увы, в Appengine этого нет.

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

Домен

Не получился красивый ник, так давайте сделаем хотя бы красивый адрес. Например ozhegov@jabberdict.ru.

Мы все помним, что есть такая прекрасная штука, как Google Apps for Domain. Там и почта на своем домене, и сайти, и Google Talk. И XMPP адреса, наверное, тоже можно перенести на свой домен, правда?

Нет. «The use of Google Apps domains in XMPP addresses is not yet supported for apps».

Вместо заключения

Если вы хотите сделать форум, или сайт-визитку, то GAE отлично подходит. Я, конечно, лукавлю, есть масса относительно сложных штук, которые можно теперь делать в Appengine (так или иначе), но если вы делаете что-то нестандартное, то будьте готовы к тому, что вы упретесь в потолок. Не потолок бесплатного аккаунта, а потолок возможностей самой платформы.

А я буду обдумывать перенос сервера на другую платформу.

Jabberdict

Запустил новый проект Jabberdict. Это такой бот-словарь для Jabber/XMPP. Написан на Google App Engine.

До того момента у меня был простой бот-словарь с несколькими тысячами пользователей. Бот жил на сервере JB (спасибо, JB!). К сожалению, старый сервер JB каким-то образом заменили на новый с 256 Мб ОЗУ, а в такой ситуации не до ботов.

Я не хотел бросать словарь, в частности потому, что сам им пользуюсь. Чтобы не перегружать сервер xkcd.ru, я решил переписать словарь на App Engine. Для небольших нагрузок App Engine бесплатен. Стоит заметить, что ограничения по XMPP в бесплатной версии App Engine очень либеральные.

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

Грубо говоря, процесс был таким: StarDict → слегка модифицированный PyStarDict → csv → Datastore. Конец истории хороший, но более 85% бесплатной квоты (1 Гб) уже израсходовано, хотя все словари в формате csv занимали не более 400 Мб; и это при том, что от части словарей пришлось отказаться. Datastore хранилище универсальное, а значит имеет большой оверхед (нужно хранить названия и типы полей каждой записи, а не только их значения).

Следующим этапом была рассылка сообщений старым пользователям. Логично было бы сделать из старого бота простой автоответчик, но новый ejabberd забыл все подписки. В этом случае все видят бота офлайн (даже рассылка presence вручную не до конца решает проблему). Поэтому пришлось активно рассылать сообщения. Собственно говоря, на данный момент процесс рассылки все ещё не закончен (и не закончится ещё как минимум неделю).

Всех интересующихся приглашаю пользоваться. Отзывы отправляются командой «!feedback» любому из словарей.

И снова переезд

Начитавшись Ивана Сагалаева решил переехать с TekTonic на Linode.

Основными аргументом в пользу переезда было то, что у Linode есть датацентры в Лондоне, а это должно значительно ускорить доступ к сайту (67 ms у Linode против 146 у TekTonic). Были, конечно и другие проблемы: довольно частые перезагрузки, некоторая кривизна дистрибутива и другие мелкие глюки. Ещё говорят, что Xen VDS работает несколько лучше, чем Virtuozzo.

Но в процессе переезда я столкнулся просто с ужасной работой TekTonic: sales не отвечал неделями, «терялись» письма системе поддержки, ломалось их собственное доменное имя и не синхронизировались ns-сервера.

В целом переезд, практически, закончен. Были освоены новые инструменты: runit — прекрасная система запуска сервисов, не нуждающийся в представлении ngnix и rdiff-backup в качестве замены rsync. Ещё немного ранее был установлен прекрасный fail2ban, который просто работает (хотя можно выполнить тонкую настройку). Не обошлось, правда, без косяков: так webdav nginx’а не может использоваться для синхронизации Zotero, пришлось запускать за nginx’ом lighttpd. А rdiff-backup имеет ужасно многословный синтаксис (почти везде приходится писать абсолютные пути) и выводит странные сообщения об ошибках.

Пока Linode работает просто отлично, сервер стал намного отзывчивее.

P. S.Основное имя теперь xkcd.ru (без www).

Исходники xkcd.ru

Под влиянием LiveJournal userhoverhell‘а наконец-то выложил исходники архива на bitbucket. Если кому-то это интересно, изучайте: bitbucket.org/Davydov/xkcdru.

Помимо этого я начал прикручивать постинг в LJ для архива. В связи с чем я выдрал из Zapys файл lj.py. Теперь lj.py живёт независимой жизнью: bitbucket.org/Davydov/ljpy. Из того, что добавил я, следует отметить (безопасную) challenge-response авторизацию. Написал маленький пример использования. Тимофей Бабич собирается включить обновлённую версию lj.py обратно в Zapys.

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

Проблемы Google Reader

Некоторое время назад решил сделать главную страницу архива редиректом HttpResponseRedirect на последний переведённый комикс.

Столкнулся с двумя проблемами. Во-первых, Google теперь не знает, как называется сайт, и в поиске выдаётся ссылка на главную страницу с заголовком последнего проиндексированного перевода. Во-вторых, если в Google Reader добавить RSS по адресу http://www.xkcd.ru (а не http://www.xkcd.ru/feeds/xkcd), то Reader берёт название подписки не из соответствующего поля фида, а по названию сайта, то есть по названию последнего переведённого комикса.

Интересно, что с этим теперь делать?

Стресс-тест

Напомню, что недавно я столкнулся с тем, что в какой-то момент сервер затыкался и переставал отдавать данные наружу. Перезагрузка Apache, как правило, помогала.

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

Сначала решил попробовать httperf от HP, но нагрузка, которую он создаёт, слишком далека от реальной (вероятно, можно что-то сделать, чтобы приблизить условия к боевым, но сходу ничего не придумалось).

Решил сделать свой небольшой стресс-тест:

#!/bin/sh
COUNT=5
for site in $@; do
  for num in `seq $COUNT`; do
    out=`mktemp -d -p .`
    wget --append-output=log --no-verbose --mirror -P $out $site &
  done
done

Этот скрипт на каждый URL, переданный ему в качестве параметра, создаёт COUNT Wget’ов, рекурсивно выкачивающих сайт. Никакой статистики скрипт не отдаёт, но можно в живую понаблюдать за расходом памяти, например, при помощи htop.

На моём сервере крутится архив переводов (два экземпляра), этот блог и несколько редко используемых legacy PHP скриптов. На всё это отводится 294 Mb.

Для тестирования я передавал скрипту 3 URL’а: архив, блог и один из PHP скриптов. Итого 15 Wget’ов. Помимо этого я периодически при помощи Firefox’а проверял, откликается ли сервер.

Итак, что же показал тест? При работе Apache через 5 минут свободная память заканчивалась, и начинал расти своп. На запросы сервер отвечал, но иногда с огромной задержкой. В том случае, если в качестве сервера выступал lighty, максимум, которого мне удалось достичь — это 142 Mb.

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

lighttpd

За некоторое время MySQL и Apache 2 съедали всю доступную память. Решил вопрос радикально: установил lighttpd с FastCGI.

Потребление памяти радикально уменьшилось. Посмотрим, что будет под нагрузкой. В статье пишут, что lighty течёт.

Документация по настройке django+lighttpd есть, но она не подробная и не актуальная.

mod_rewrite у lighttpd не так крут, но мне его хватило.

Проблема reCAPTCHA

Проект reCAPTCHA фактически стал стандартом в мире «капч». На мой взгляд, главная проблема состоит в том, что программа не умеет по-настоящему скрывать, какие слова она уже знает, а какие — пока нет.

Приведу несколько примеров:

reCAPTCHA

reCAPTCHA

reCAPTCHA

Выше мы можем легко определить, что нечитаемое слово — это то, которое reCAPTCHA не знает.

reCAPTCHA

А в этом случае всё ещё более тривиально: незачёркнутое слово пока не распознано.

Во всех четырёх случаях мы можем ввести только одно из двух слов, и reCAPTCHA будет считать, что мы всё сделали правильно. Если эффект халтурного ввода капчи станет массовым, то это может существенно ухудшить качество распознавания книжек.

GB2312

Нет. Я не пользуюсь и не буду пользоваться этой ужасной почтой.

Однако меня поражает упорство этой компании. Если отсутствие RSS и изобретение нестандартных полузакрытых протоколов ещё как-то можно объяснить экономической целесообразностью, то абсолютное нежелание поддерживать кодировку GB 2312 я понять не могу.

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

Естественно, я писал письма в поддержку и получил гордое молчание в ответ. Как решить проблему? А очень просто, настроить переадресацию на нормальный почтовый ящик.