Решил поделиться одним модулем, который используется у меня на VisaMap

Занимается он простой вещью – склоняет существительные: Вася, Васи, Васе и так далее.

Принцип работы прост – он ходит на Яндекс-Склонятор и кэширует результаты в базе (если не кэшировать, то они меня банят). Если Яндекс не осилил склонение слова, то этот fail тоже кэшируется в базе.

Синтаксис прост:

>>> from django_declension import declension
>>> declension(u'говняшка').genitive
u'говняшки'

Забрать его можно здесь: http://github.com/gugu/django-declension/

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

В vim это делается так. В корне проекта пишем:

ctags -R
vim -t MyClassName

и попадаем на определение класса. Удобно?

Сейчас если мы поставим указатель на вызов функции или создание экземпляра класса и нажмем Ctrl+] (для виндузятников это Ctrl+мышка), то перейдем на определение класса.

А комбинация Ctrl+T вернет нас назад.

Авторы ctags с детства не любят питон, поэтому в ~/.ctags питонюки пишут:

--python-kinds=-i

Зачем? Так надо.

Вышла новая версия DBIx::Class::Schema::Loader с множеством изменений. Одно из этих изменений – генерация POD-документации для автоматически созданных классов. Пользователи PostgreSQL получат ещё одну вкусняшку – все комментарии к их таблицам и столбцам автоматически переедут в документацию. Не буду показывать на автора этого патча пальцем, так как это неприлично.

И второе. Test::Pod::Coverage::Permissive. Принцип работы такой же, как и у Test::Pod::Coverage, только тесты валятся лишь в том случае, если появилась ещё одна не­за­до­ку­мен­ти­ро­ван­ная фунция.
Т.е. при первом запуске у вас тесты пройдут в любом случае. Задокументируете функцию — тесты пройдут. Напишете ещё одну незадокументированную функцию — обвалятся.

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

К чему это я — пользуйтесь и пишите баги.

Странно, что этого до сих пор никто не написал: App::Pm2Port

Создаёт порт из перлового модуля и отправляет его разработчикам FreeBSD.

Как работать:

pm2port CSS::Croco

Тестируем и находим баги.

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

— Капитан, что вы думаете об оптимизации производительности?
— Я, как и все адекватные личности не думаю о ней без надобности. Ещё я терпеть не могу биомассу, которая занимается преждевременной оптимизацией. О ней нужно думать лишь тогда, когда всё действительно уж хреново. Как правило, программисты обычно пишут проект немытыми руками, вследствие чего он начинает тормозить. Потом они на этот проект навешивают пять уровней кэширования, надеясь, что это загладит их проёб. Постараюсь привести интересную цитату профессора из MIT (спасибо, vovkasm-у):
Continue reading »

Moose – это альтернативная объектная система для Perl 5. Она прячет от вас хитрые манипуляции с хэшами и “благословление” объектов и превращает Perl в язык с хорошим ООП.
Continue reading »

Все мы привыкли работать с базой в стиле:

  1. выполнить запрос
  2. дождаться ответа
  3. продолжить выполнение

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

Для PostgreSQL в DBD::Pg есть некоторое подобие асинхронности. И иногда оно таки нам помогает.

Вы можете продолжить выполнять приложение, не дожидаясь выполнения запроса. Включается это параметром pg_async к prepare-запросу:

  use strict;
  use warnings;
  use Time::HiRes 'sleep';
  use DBD::Pg ':async';
 
  my $dbh = DBI->connect('dbi:Pg:dbname=postgres', 'postgres', '', {AutoCommit=>0,RaiseError=>1});
 
  ## Запустить длинный запрос
  my $sth = $dbh->prepare("SELECT pg_sleep(5)", {pg_async => PG_ASYNC});
  $sth->execute(5);
 
  ## Пока работает, мы можем что-то сделать
  print "Your query is processing. Thanks for waiting\n";
  check_on_the_kids();
 
  while (!$dbh->pg_ready) {
    check_on_the_kids();
    ## и подождём чуть-чуть
    sleep 0.1;
  }
 
  print "The query has finished. Gathering results\n";
  my $result = $sth->pg_result;
  print "Result: $result\n";
  my $info = $sth->fetchall_arrayref();

Для pg_async есть три константы:

  • PG_ASYNC – выполнение запроса в асинхронном режиме
  • PG_OLDQUERY_CANCEL – если в этот момент работал предыдущий запроса, то он отменяется
  • PG_OLDQUERY_WAIT – блокируемся для ожидания предыдущего запроса и только потом начинаем выполнять новый

Так же есть три вспомогательных метода -
pg_cancel – отменить запрос. Реально открывается ещё одно соединение, в котором посылается SELECT pg_cancel_backend(?);
pg_ready – возвращает true, если запрос выполнился.
pg_result – блокируется до момента выполнения запроса, после чего возвращает то же, что и ->execute в стандартном режиме.

Из минусов – нельзя установить callback на момент исполнения запроса. Нужно постоянно проверять.
Так же в один момент времени в одном соединением можно выполнять только один запрос. Но что мешает открыть десяток соединений и по очереди посылать в них запросы?)

Попробую рассказать о применениях этой технологии:

  • тяжёлые запросы + сложная логика. Равномерно загружаем свой сервер и сервер БД.
    while (){
    my $foo = compute_foo();#тяжёлая функция
    #блокируемся до тех пор, пока не выполнится предыдущий
    $dbh->do('UPDATE stats SET foo=foo+1', { pg_async => PG_ASYNC + PG_OLDQUERY_WAIT });
    }
  • работа с несколькими серверами БД
    $first_dbh->do('DELETE FROM old_data', { pg_async => PG_ASYNC });
    $second_dbh->do('UPDATE new_data SET status=0', { pg_async => PG_ASYNC })
  • таймауты. вы можете убить запрос по таймауту, если он ещё не отработал
  • конкуррентные запросы. посылаем один и тот же запрос двум серверам и отдаём данные с того сервера, который ответил быстрее.
  • запросы, результат выполнения которых вам абсолютно безразличен

Как недавно оказалось, у DBI есть отличный профайлер. Включается он очень просто.

DBI_PROFILE=1 perl test.pl
DBI::Profile: 27.088730s 58.89% (52900 calls) test.pl @ 2009-07-08 23:08:39

Как видите, он краток. А кратк. сестр. тал. Он показывает, стоит ли нам вообще оптимизировать SQL.
он говорит, что на базу потрачено 60% времени.
Если нужно оптимизировать базу, то запускаем скрипт с DBI_PROFILE=2
Тогда нам покажется подробный отчёт о времени, затраченном на каждый тип запроса.

Сегодня таки вышел Catamoose. А если простыми словами – Catalyst версии 5.08

Изменений просто дохрена:

  1. Первое и главное. Catalyst переписали на Moose. Теперь он будет есть не 80Мб, а 200Мб (:
  2. Избавились к чертям от тормоза NEXT, который ещё и работает через задницу
  3. Проапгрейдили Catalyst::Log, поправили баги и…
  4. всё

Обещаю, что если вы не полный распиздяй, и не вставляли костылей в сам Catalyst, то переезд на Catamoose больше минуты не займёт. А вот размер проекта в памяти вырос на 19Мб.

Простейший трейс включается переменной окружения DBIC_TRACE=1. Сейчас каждый запрос будет выводиться на STDERR перед выполнением.

Но что, если нам его недостаточно? Посмотрим, что нам даёт DBIx::Class. А даёт он нам класс DBIx::Class::Storage::Statistics.
Методы этого класса вызываются до и после каждого запроса, либо транзакции.

Попробуем после каждого запроса выводить время, которое он исполнялся. Это описано в DBIx::Class::Manual::Cookbook.

Добавляем схему:

  __PACKAGE__->storage->debugobj(new My::Profiler());
  __PACKAGE__->storage->debug(1);

И создаём модуль:

  package My::Profiler;
  use strict;
 
  use base 'DBIx::Class::Storage::Statistics';
 
  use Time::HiRes qw(time);
 
  my $start;
 
  sub query_start {
    my $self = shift();
    my $sql = shift();
    my $params = @_;
 
    $self->print("Executing $sql: ".join(', ', @params)."\n");
    $start = time();
  }
 
  sub query_end {
    my $self = shift();
    my $sql = shift();
    my @params = @_;
 
    my $elapsed = sprintf("%0.4f", time() - $start);
    $self->print("Execution took $elapsed seconds.\n");
    $start = undef;
  }
 
  1;

Отлично. Сейчас после каждого запроса пишется время выполнения. Если мы в My::Profile::print мы заменим print на carp, то после каждого SQL-запроса мы получим место его выполнения.

Но время выполнения запроса – это средняя температура по больнице, потому что может быть много быстрых запросов. Хотелось бы получить более красивую статистику. И она есть – DBIx::Class::QueryLog

Этот модуль сохраняет статистику по каждому выполненному запросу, группирует одинаковые запросы и сортирует по суммарному времени выполнения. Как им пользоваться – написано в документации. Свою задачу – ткнуть Вас носом в интересную фичу DBIx::Class – я выполнил.
Удачной Вам разработки.

© 2012 Андрей Костенко Suffusion theme by Sayontan Sinha