About Blog Dev | Alfa Romeo SZ Conkeror wishlist

Archive for November, 2007

Темы

С подачи lorien‘а блог обзавёлся возможностью переопределять только нужные темплейты в темах. Всего лишь добавил загрузчик темплейтов (это оказалось очень просто), да настройку THEME - теперь первым делом темплейты ищутся в themes/{{ THEME }}/, а уж потом в templates/. :-) Ещё думаю сделать добавление стилей в зависимости от этой настройки, но не решил каким образом будет лучше. Наверное, темплейт тег, который будет проверять существование файликов со стилями кастомных и добавлять их в хидер. Или есть варианты лучше?

Ещё подумываю добавить мультиязычность. ;-) Но как это реализовать?… Пока знаю только django-multilingual и i18ndynamic. Может кто знает лучший вариант? Было бы неплохо, чтоб комментарии к разным языкам не пересекались, наверное… Или плохо? :\

JSON в куках

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

Но есть один момент, который будет неочевиден любому, кто не сильно часто сталкивается с джаваскриптом и вообще особенностями разных браузеров (к примеру, мне ;-) - обязательная экранизация строк. Так, как они экранизуются в урлах. А то FF и IE нормально воспринимают, а Опера и Сафари - нет. ;-)

Напоминание, на всякий случай: в Python для этого есть urllib.quote и unquote, а в JS - escape и unescape.

Nginx + Django: mod_wsgi vs FastCGI - en

(Translation of my previous post).

Yesterday I have finally sorted out my trouble with compilation of nginx’ mod_wsgi and got it working (how much means slash in our life ;-).

Configuration to get Django application running is quite simple — django.wsgi, which is used for Apache’s mod_wsgi, fits perfectly. I’ll not describe all configuration, because code is unstable and needs testing and is’s contra-indicated to run it on production now.

Lets do the fun — testing. :-) Simple page, 15 queries (PostgreSQL on same pc), 2 workers for nginx (I have tried 3 and 4 — they both are slighty slower, for 2-4 ms). Two variants of testing queries (ab -n 1000 -c 20 and ab -n 10000 -c 500) and three variants of servers (mod_wsgi, prefork fastcgi, threaded fastcgi). Hardware (this is not so interesting, because performance is interesting only in comparison, but why not to show it?) — Core 2 Duo T7300 and 2 Gb of RAM.

First — ab -n 1000 -c 20 (around of 10-12 runs for every variant):

  • mod_wsgi takes 14.2-14.3 ms per query, quite stable performance
  • prefork fastcgi takes 12.5-16.5 ms (mostly near of 12 ms, but raises from time to time), eats bigger amount of RAM — I have an xmobar1 showing usage of RAM and mod_wsgi has a two-three percents (percent is 20 megs) lesser usage
  • threaded fastcgi takes 24-25 ms per query — it uses only one core of CPU. I have tried to get upstream working in nginx — it works, but uses only 1 process for some reason :-(

I.e. in most cases for such load FastCGI is slighty faster (and uses somewhat bigger amount of resources). But… lets go further? ;)

Second — ab -n 10000 -c 500 (here I got 3-4 runs for every variant):

  • mod_wsgi takes 13-14 ms. Pretty stable result. Each worker consumes 21/15 megs of RAM (VSZ/RSS)
  • prefork fastcgi takes 15.5-17 ms per request, but it runs from 40 to 50 serving instances, each consuming 16-20 VSZ (10-13 RSS) megs of RAM! xmobar says that usage raises up to 50% (by leaps, usually around 45-48%) — compare to 33% for mod_wsgi! Additional 300 mbytes of RAM.
  • threaded fastcgi — most “interesting” variant. With this level of concurrency it just dies. With -c 100 — dies. It lives with -c 50 with slighty lower speed than with -c 20, but process eats near of 300 mb of VSZ.

What I can say here? Lets wait for stable mod_wsgi release! :-) Thanks, Manlio, for this piece of code. :-)

1

xmobar — small statusbar, written on Haskell. Displays system state and other various information

Nginx + Django: mod_wsgi vs FastCGI

Вчера вечером таки связался с автором mod_wsgi (до этого были какие-то проблемы - он говорит, что мне отвечал, но у меня даже в спамбоксе пусто было) и скомпилировал nginx с mod_wsgi (ха-ха, как много решают слеши в нашей жизни ;-).

Запуск джанговского приложения под nginx’ом не вызвал абсолютно никаких проблем - использовался тот же файлик django.wsgi, который я приводил раньше для апача. Больше о настройке решил ничего не писать, потому как код ещё не стабильный и требует тестирования, потому использование где-то в реально жизни пока больному не показано.

Но тут начинается веселье - тестирование. ;-) Простая страничка, 15 запросов (PostgreSQL на этом же компе), инфы по этим запросам приходят мелочи (ещё хочу потестировать, чтоб было инфы побольше, а постгрес был на другом компе). 2 воркера (пробовал и 3, и 4 - выходит медленнее в любом варианте, на 2-4 мс). Два варианта запросов (ab -n 1000 -c 20 и ab -n 10000 -c 500) и три варианта серверов (mod_wsgi, prefork fastcgi, threaded fastcgi). Железо (всё равно интересно только в сравнении на одном железе и софте, потому конфигурация несущественна, но всё же) - Core 2 Duo T7300 и 2 Gb RAM.

При ab -n 1000 -c 20 (на каждый вариант сервера пришлось порядка 10-12 тестирований):

  • mod_wsgi стабильно выдаёт 14.2-14.3 мс на запрос
  • prefork fastcgi выдаёт 12.5-16.5 мс (в основном около 12, но иногда подскакивает), жрёт больше рамы - у меня xmobar1 показывает количество съеденной памяти, при mod_wsgi там на пару процентов (процент - 20 мегов) меньше всегда
  • threaded fastcgi выдаёт 24-25 мс на запрос - он использует только одно ядро, я пытался настроить в нгинксе upstream - он работает, но использует почему-то только 1 процесс

Т.е. в большинстве случаев при такой нагруженности фастцги выходит немного быстрее (хотя и кушает немножко больше ресурсов). Но… идём дальше? ;)

При ab -n 10000 -c 500 (тут вышло по 3-4 запуска):

  • mod_wsgi выдаёт 13-14 мс. Каждый воркер хавает примерно 21/15 мегов памяти (VSZ/RSS)
  • prefork fastcgi выдаёт 15.5-17 мс на запрос, но при этом запускается от 40 до 50 обслуживающих процессов, каждый из которых кушает 16-20 VSZ (10-13 RSS) мегов памяти! По xmobar’у расход выходит до 50% (скачками, обычно около 45-48) относительно 33% у wsgi. Это лишних 300 мегов оперативки!
  • threaded fastcgi - самый интересный вариант. При таком количестве одновременных запросов он умирает. При -c 100 - тоже умирает. При -c 50 - живёт, но скорость выходит чуть ниже, чем при -c 20, а рабочий процесс хавает до 300 мегов VSZ.

Что тут можно сказать? Ждём, когда mod_wsgi станет стабильным! :-)

1 -

xmobar - это такой статусбар на хаскеле, показывает состояние системы и вообще чего мне захочется

Пробелы в Питоне

Нашёл отличную статью Оливера Фромме про пробелы и отступы в Питоне. Рекомендуется к прочтению, особенно противникам Питона.

Пара слов о декораторах

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

def render_to(tmpl):
    def renderer(func):
        def wrapper(request, *args, **kw):
            output = func(request, *args, **kw)
            if not isinstance(output, dict):
                return output
            return render_to_response(tmpl, output, 
                   context_instance=RequestContext(request))
        return wrapper
    return renderer

Конечно, можно дописать пару строк к возврату враппера и получить в принципе работающий механизм:

        wrapper.__name__ = func.__name__
        wrapper.__doc__ = func.__doc__
        return wrapper

Но на самом деле это не самый красивый метод. Куча всяких руководств для начинающих по написанию декораторов всегда рекомендует юзать модуль Мишеля Симионато - decorator. В принципе, всё конечно просто отлично, но есть у него огромный недостаток - это дополнительная библиотека, в то время как есть отличное решение из стандартной библиотеки - functools.wraps. Его использование ничем не отличается от использования decorator‘а:

from functools import wraps

def render_to(tmpl):
    def renderer(func):
        @wraps(func)
        def wrapper(request, *args, **kw):
            output = func(request, *args, **kw)
            if not isinstance(output, dict):
                return output
            return render_to_response(tmpl, output, 
                   context_instance=RequestContext(request))
        return wrapper
    return renderer

И всё становится белым и пушистым. :-) Один момент непонятен - почему его не пишут во всех этих руководствах для начинающих? Ведь эти начинающие с течением времени становятся продолжающими и точно так же не знают о простой и приятной штуке прямо в stdlib‘е.

Шрифтовая эпопея

В общем, после моих жалоб на качество линуксовых шрифтов я получил несколько наставлений. ;) Первый несколько помог, спасибо Паше, но оставались кривоватые места - не так много, но изредка напрягающие. И вот по ссылке из ещё одного коммента я нашёл, во-первых, неплохой блог, а во-вторых - серию постов про то, как же победить шрифты. Оказалось, что ужас наводят шрифты DejaVu, которые мне неоднократно хвалили. :) В принципе, реальных шагов оказалось два (для меня ;):

  • Удаление ttf-dejavu, (и -core/-extra от него же) из системы напрочь (на более свежей, чем у меня, системе проверка показала, что приходится удалять ubuntu-desktop, но он у меня был давно мёртв ;).
  • Установка приоритета микрософтовых шрифтов в fontconfig’е повыше - это просто редактирование /etc/fonts/conf.d/60-latin.conf. Правда, это уже было сделано на всякий случайно, но кто его знает, чего дальше ждать. :-)

Это всё. Теперь все шрифты выглядят на 5 баллов. :-) Правда, я не могу сказать, что мне нравится больше, чем в винде (в линухе несглаженные шрифты выглядят хуже, а я не слишком люблю сглаживание), но по крайней мере я могу нормально всем этим пользоваться. Виндой с ClearType’ом я пользоваться не могу, кстати. :-)

Reinteract

Симон Виллисон дал ссылку на reinteract, интерактивный интерпретатор для питона, можно даже сказать - следующее поколение IPython. Но IPython - это консольная штука, а тут - GTK, что позволяет небольшой интерактивный редактор (к сожалению, привычные из обычных шеллов емаксовые биндинги M-b/M-f/M-d/C-w/etc отсутствуют, может попросить автора, чтоб сделал?), рисовать графики, играть звуки и т.д. :) Присутствует система плагинов.

В общем, выглядит хорошо достаточно, но требует полировки. :)

Переезд

Вчера вечером заимпортировал все более-менее значимые посты из старого вордпрессовкого блога, осталось решить непонятную траблу с flatpages (сейчас почему-то работает только about :( ), и переезд можно будет считать окончательно завершённым - поставлю со старого блога редирект на этот, по возможности чтоб не промазывать по постам.

query.py

Я сейчас не очень плотно слежу за сообщениями в django-developers, но всё же избирательно тыкаю на треды (избирательность больше зависит от автора первого поста, чем от темы ;) - и наткнулся на сегодняшний (для Австралии :-) пост Трединника о рефакторинге query.py. Так что всё отлично, он движется и там есть заметные изменения1. Он хочет к концу месяца приступить к тестированию. Не так быстро, как думалось сразу ;), но всё же до НГ! Ещё бы и newforms-admin подоспел, совсем бы замечательно было. :-)

1 -

из тех, что упомянул Малькольм - теперь Q() имеют полный доступ к запросу (для добавления пунктиков во WHERE и колонок/табличек).

Подкасты

Недавно Макс Ищенко подал отличную идею - непонятно, почему я не додумался сам - когда едешь в машине, слушать подкасты. А так как я езжу в ней каждый день утром и вечером минимум почти по часу (сегодня, если верить магнитоле, вышло вечером 46 минут ;-), это выходит достаточно полезная штука.

Так как раньше я подкасты никогда не слушал (потому как всё равно мне лично при слушании ничего другого продуктивно не удаётся делать, проще уже читать быстренько всё), я воспользовался первым же советом из комментариев к его посту - скачал подкаст radio-t.

И случился прикол. Заглянул только что в его этот пост, и увидел мнение Макса в последнем комментарии, абсолютно совпадающее с моим. Два учаснега вещают что-то на уровне детского сада: о боже, я в новой Опере 9.5 не увидел ничего, кроме синхронизатора закладок! - даже я, к Опере испытывающий неприязнь, знаю о куче изменений; как мне не хватает абсолютно гиковской штучки показывания маленьких изображений рабочих столов в Places (чёрт, какая она гиковская! Гиковский - это xmonad, а показывание финтифлюшек - это как раз для казуалов ;)!

Короче, я абсолютно недоволен последним их подкастом. Завтра с утра послушаю ещё один, лежащий на флешке - но что там может быть принципиально лучше? При таком уровне, когда один может сказать “ой, я не знаю точно, как там крадут эти домены, я думаю это где-то на уровне днс-серверов”, OMG!

В общем, этот radio-t был моим первым подкастом и явно неудачным. :) Надо бы попробовать что-то ещё, а то так и останусь думать, что подкасты - это явный отстой. Неужели нету чего-нибудь… более гиковского? Может mobile-review’шные послушать?..

Сноски и обновление пингбека

Как я и писал, я решил добавить поддержку сносок. Оказалось это совсем просто, потому что порт Markdown на Python уже имеет такой плагин. Потому все изменения свелись к тому, что кинул mdx_markdown.py в каталог проекта и немножко переписал свою функцию text_to_html. Правда, пришлось немного поправить код самого плагина, чтобы сноски выводились так, как хочется мне, а не как придумал себе автор. :) Должен сказать, что генерация DOM в питоновском коде - то ещё уродство, лучше уж какие-то простенькие темплейты, что ли… :\

Вторая и главная новость состоит в том, что я добавил модель PingbackClient, которая запоминает те адреса, которые уже пинговались, и просто не даёт их пинговать при редактировании поста - чтоб не ждать каждый раз, пока оно пройдёт все урлы. Кроме того, это же позволит следить за тем, куда же таки дошли пинги. :) А ещё я закинул всю реализацию как отдельный проект на Google Code. :)

P.S.Кстати, замечания и дополнения привествуются. :) А то чувствую, ещё пару раз гляну на ping, и перепишу её. Уж больно ужасно вышло. :-)

Комментарии

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

Сначала я, в лучших традициях чукотской наивности, просто попытался добавить вызов функции залогинивания после подтверждения правильности комментария и чистоты помыслов его автора, но не тут-то было! Джанга мне быстренько рассказала, кто здесь хозяин и где собственно у пользователя свойство backend?

Я понял, что двумя пальцами и асфальтом не обойдусь и пошёл читать доку. После беглого просмотра жалкой пары абзацев по предыдущей ссылке я уловил, что мне надо в любом случае применить функцию authenticate, которая и установит пользователю искомое свойство. В надежде, что я обойдусь без написания своего бекенда, решил порыться в гугле - и мои опасения, в который раз, оказались обоснованными. :-) Существует тикет, как раз по моему вопросу, закрытый больше года назад Малькольмом Трединником с объяснением: “нечего голову морочить, читайте доки и пишите бекенды, это несложно”.

На самом деле всё оказалось не просто несложно, а вообще просто бесплатно. ;-) Написание первоначальной версии бекенда (только для подтверждения комментариев) заняло у меня порядка 5 минут с ловлей блох, а доработка до более модной версии, которая теперь работает и для активации пользователя (активировался - и уже залогинен :-) заняла ещё порядка 10 минут с обдумыванием, а как же будет лучше это реализовать.

Получился небольшой кусочек кода, который к тому же ещё обладает способностью к лёгкому расширению. :-) На самом деле бекенд - это просто небольшой класс, который должен иметь два метода - authenticate, для проверки валидности пришедших мандатов1, и get_user, для получения юзера и добавления его к объекту запроса (т.е. к request).

get_user, использующийся с джанговской стандартной модельюUser (а я в блоге только ей и пользуюсь), всегда выходит одинаковый (про это немного написано в последнем абзаце), потому говорить тут не о чем. А вот authenticate получился достаточно интересным, на мой взгляд: он вызывает динамически (хе-хе, использование getattr даёт право написать это слово) необходимый метод для совершения действа (например, активирования пользователя) - почему я и написал про лёгкую расширяемость.

А теперь два слова про однообразный get_user. На самом деле, когда я писал сам бекенд, я понял только, зачем нужен authenticate, и чтоб разобраться с get_user (не хотелось опубликовывать куцый пост ;-), мне пришлось рыться в джанговском contrib.auth. Внимательное чтение легко объяснило, что к чему, но я заметил очень неприятную штуку - кучу жёстко закодированных вещей. Собственно, у меня самого такое встречается (и мне откровенно неудобно такое отсылать в репозиторий, но время часто не резиновое, и общее красивое решение некогда делать), но тут… В общем, у меня роятся мысли это поправить всё, вроде не выглядит сложным. Надеюсь, соберусь это сделать (и для диплома полезно будет ;-).

1 -

не могу подобрать русского перевода для слова “credentials” - Лингво из адекватных предлагает только “мандаты”, но меня и оно как-то не устраивает. В общем, сочетания логин и пароль, или openid, или, в данном случае, активационного ключа.