September 2008 Archives

Сломали DBD::mysql

| 4 Comments

Начиная с версии 4.007 сломали $dbh->{mysql_auto_reconnect}=1

В результате при потере коннекта к мускулю вместо реконнекта происходит сегфолт перла. Оч. неприятная штука :)

В версии 4.008 так и не пофиксили, хотя с ней в один день выпустили патч.

Маленький perl-модуль к nginx-у, который "склеивает" www.domain -> domain и наоборот (т.е. на каждый урл выдает соотв. 301-редирект, плюс robots.txt для яндекса).

Прелесть в том, что не нужно описывать директиву server для каждого домена. Достаточно все домены - неосновные зеркала запихнуть в один server (поштучно или маской/регэкспом) и вообщем-то всё. Если домен с www, значит основным зеркалом считается без www, и наоборот.

ngx_glue.tar.gz

А вообще это делается как-то так:

server {
    server_name все_неосновные_зеркала_поштучно_или_маской;
    if ($host ~ ^www\.(.+)$) {
        set $h $1;
        rewrite ^(.*)$ http://$h$1 permanent;
        break;
    }
    rewrite ^(.*)$ http://www.$host$1 permanent;
}

Но тут не будет генериться robots.txt

Механизм

Как известно, механизм сессий построен по принципу frontend/backend. За фронтенд отвечает Session::State, за бэкенд - Session::Store. Первый работает с пользователем - передает и принимает айди сессии (например, в урле или куке). Второй - на стороне сервера. Он отвечает за хранение данных сессии.

В качестве фронтенда выберем State::Cookie (больше нечего, не в урлах же сессии передавать), а какой бэкенд - не столь важно.

Как известно, за expire сессий отвечают 2 параметра в конфиге: $c->config->{session}->{expires} и $c->config->{session}->{cookie_expires}, которые отвечают за экспайр сессии и куки соответственно.

cookie_expires использует только Catalyst::Plugin::Session::State::Cookie, причем на стороне сервера оно нигде не хранится, и используется только для выставления expire в HTTP-заголовке. Причем если cookie_expires == 0, то кука будет сессионная.

expires использует Catalyst::Plugin::Session. Когда происходит запрос с проэкспайренной сессией, вызываются методы delete_session_id и delete_session_data. Первый наследуется Session::State (т.о. может удалить куку, даже если она еще не проэкспайрилась), второй - наследуется Session::Store. Его предназначение - удалить данную сессию на сервере.

Результат

Таким образом со стороны пользователя получается всё прозрачно. Выставляем expires == cookie_expires и всё. Т.к. если cookie_expires будет больше - сессия на сервере истечет раньше, будет вызван delete_session_id, и кука всё равно удалится. Наоборот - кука удалится, данные сессии на сервере останутся, но пользователю это неважно - для него этой сессии больше нет.

Немного сложнее с сессионными куками (cookie_expires == 0). Здесь надо выставить expires во что-нибудь вменяемое, например - сутки. Крайне маловероятно, что пользователь не закроет сайт в браузере в течение суток, и при этом ни разу не запросит ни одной страницы.

Проблемы

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

Каталист предлагает "свалить" эту проблему на Session::Store::бэкенды.

Catalyst::Plugin::Session::Store
delete_expired_sessions
This method is not called by any code at present, but may be called in the future, as part of a Catalyst-specific maintenance script.
If you are wrapping around a backend which manages its own auto expiry you can just give this method an empty body.

Но реальность обстоит иначе:

Catalyst::Plugin::Session::Store::DBI
The expires column in your table will be set with the expiration value. Note that no automatic cleanup is done on your session data, but you can use the delete_expired_sessions method to perform clean up. You can make use of the Catalyst::Plugin::Scheduler plugin to schedule automated session cleanup.
Т.о. забота об очистке сессионного хранилища кладется на нас.

Catalyst::Plugin::Session::Store::File
Здесь всё вообще "замечательно":

sub delete_expired_sessions { }

Метод тупо не реализован :) Более того:

    $c->_session_file_storage(
        Cache::FileCache->new(
            {
                cache_root  => $cfg->{storage},
                (
                    map { $_ => $cfg->{$_} }
                      grep { exists $cfg->{$_} }
                      qw/namespace cache_depth directory_umask/
                ),
            }
        )
    );

Как мы видим, модуль вообще не заботится об expire, и даже параметра такого в Cache::File не передает. Т.о. даже невозможно написать maintenance-скрипт, который бы чистил периодически хранилище.

Анализатор поисковых реферреров

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

Понимает все кодировки, в том числе двойной quoted-printable, utf16-be и т.п. Если бы данные кодировки не юзались где-то, я бы не стал делать их поддержку :) При разработке через анализатор было пропущено 1.5 ляма реальных рефов, далее он был доработан, чтобы понимать те рефы, которые он не понимал.

За давностью лет его КПД я, к сожалению, не помню, но точно больше 99.9%, что для эвристики - очень и очень гуд. Плюс, он работает довольно быстро.

Referrer.pm.tar.gz

Модуль экспортирует единственную функцию analyze()
Использование:
my ($se, $kw) = analyze($ref);
$ref - исходный реферрер (без эскейпа, т.е. в том виде как он пришел из заголовка)
$se - поисковик (hardcoded в @cfg_se, для незнакомых возвращает 'other')
$kw - распознанный кейворд (в кодировке cp1251).

Также может вернуть undef, если реферрер битый (нельзя выпарсить домен, или протокол отличен от http(s)

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

При этом имелось несколько бэкендов, и хотелось их ограничивать "по-разному", или не ограничивать вовсе, в зависимости от текущей нагрузки.

Вообщем в итоге родилось следующее решение:

  • Некий демон с определенным интервалом парсит команду "netstat -Lan", и определяет загруженность бэкендов по величине backlog. Далее смотрит текущий limit_rate для каждого из бэкендов в кеше на базе Cache::FastMmap, и понижает/повышает его, если надо.
  • Хендлер для nginx-а определяет ботов по юзер-агенту, и если пришел бот - вытаскивает из кеша текущий limit_rate для бэкенда.
В результате нагрузка "балансирует".

Архив с демоном, хендлером и примером конфига для nginx: nx_limitrate.tgz

Писалось всё исключительно для себя и быстро, поэтому абсолютно всё захардкодено, т.ч. претензии по этому поводу не принимаю :) Впрочем, код очень маленький и интуитивно понятный. А если нет - то велкам :)

About this Archive

This page is an archive of entries from September 2008 listed from newest to oldest.

August 2008 is the previous archive.

October 2008 is the next archive.

Find recent content on the main index or look in the archives to find all content.

Pages

Powered by Movable Type 4.2-en