Блог понаехавшего | шо?

TAG | perl

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

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

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

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

, , , Hide

Jul/09

15

Асинхронность в DBD::Pg

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

  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 })
  • таймауты. вы можете убить запрос по таймауту, если он ещё не отработал
  • конкуррентные запросы. посылаем один и тот же запрос двум серверам и отдаём данные с того сервера, который ответил быстрее.
  • запросы, результат выполнения которых вам абсолютно безразличен

, , Hide

Альфа версия (не доступна из CPAN)
Она умеет работать с XS-ным Class::XSAccessors, что ускоряет работу DBIx::Class-овых приложений процентов на 10-20.

, , Hide

May/09

17

Вчера

May/09

17

Сегодня

Apr/09

17

Отладка в DBIx::Class

Простейший трейс включается переменной окружения 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 – я выполнил.
Удачной Вам разработки.

, , Hide

Синтаксисом языка они не предусмотрены.
Но можно воспользоваться Plain Old Documentation. Всякими
=pod, =comment, =rwerwerwe пользоваться не стоит, потому что ваши комменты вылезут в POD-документации.

Правильный вариант многострочных комментов выглядит так:

=for comment
 
Some comment
 
=cut

, , Hide

Для начала расскажу историю. В далёком 2004-м году чувак по имени Vladi Belperchinov-Shabanski “Cade” написал модуль Algorithm::FloodControl. В те дремучие века люди не знали, что такое ООП и их заботила поддержка Perl4 :-)

Сделал апгрейд этому модулю. Сделал его ООП-шным, добавил поддержку backend-ов для Cache::Memcached::Fast, Cache::FastMmap. Сделал и для Cache::Memcached, ибо не сложно три строки написать, но когда 15 процессов инкрементят запись, то срабатывает от силы 10% инкрементов. Ниипу почему.

Наверное, ещё будут бока с документацией. Это я поправлю.

Algorithm-FloodControl-1.90

, , Hide

Jun/08

8

Настройка Perl для Mac OS X

Upd: тьфу на этот маковский Perl. Поставил ActiveState. И вам советую

Начиная с Mac OS X 10.3 в системе появился Perl. Для написания праграмм в стиле “Hello world” его вполне достаточно. Для установки модулей на понадобится make, а для сборки XS – gcc со всеми вытекающими последствиями.

Всё это можно найти в пакете Xcode Tools, который скачивается с сайта Apple. Будем считать, что у вас он уже установлен.

Итак, пробуем:


$ sudo cpan Catalyst

Проблемы, которые могут возникнуть:

Регистронезависимость ФС

Как в FAT32 с NTFS-ом, HFS+ регистронезависим. Соответственно INSTALL и install – это один и тот же файл. К примеру, LWP при установке пытается установить бинарник HEAD, который затрёт вам стандартную команду head.

Чтобы этого не происходило, добавляем в конфиг CPAN такие строки:

cpan> o conf makepl_arg "INSTALLBIN=/usr/local/bin INSTALLSCRIPT=/usr/local/bin"
cpan> o conf commit

…и ещё одна непонятная проблема

При запуске make в директории с модулем у меня выдавалась ошибка:

make: *** No rule to make target `/System/Library/Perl/5.8.8/darwin-thread-multi-2level/CORE/config.h’, needed by `Makefile’. Stop.

Решается всё такой командой:

$ sudo cp /System/Library/Perl/5.8.1/darwin-thread-multi-2level/CORE/* /System/Library/Perl/5.8.8/darwin-thread-multi-2level/CORE

содрано частично отсюда: http://sial.org/howto/perl/life-with-cpan/macosx/

, , Hide

Find it!

Theme Design by devolux.org