Какой безопасный способ использовать fork с Apache :: DBI под mod_perl2? - PullRequest
0 голосов
/ 31 марта 2010

У меня проблема при использовании Apache :: DBI в дочерних процессах. Проблема в том, что Apache :: DBI предоставляет единый дескриптор для всех процессов, которые его используют, поэтому я получаю

DBD :: mysql :: db selectall_arrayref не выполнено: команды не синхронизированы; вы не могу запустить эту команду сейчас /usr/local/www/apache22/data/test-fork.cgi строка 20.

Переподключение не помогает, поскольку Apache :: DBI переподключается во всех процессах, как я понял, следующая ошибка

Сервер обнаружил внутренний ошибка и не удалось завершить запрос.

Сообщение об ошибке: драйвер DBD не имеет реализовал атрибут AutoCommit в /usr/local/lib/perl5/site_perl/5.8.9/Apache/DBI.pm строка 283.

Вот код источника:

use Data::Dumper 'Dumper';
use DBI ();

my $dbh = DBI->connect($dsn, $username, $password, {
        RaiseError => 1,
        PrintError => 0,
    });
my $file = "/tmp/test-fork.tmp";

my $pid = fork;
defined $pid or die "fork: $!";

if ($pid) {
    my $rows = eval { $dbh->selectall_arrayref('SELECT SLEEP(1)') };

    print "Content-Type: text/plain\n\n";
    print $rows ? "parent: " . Dumper($rows) : $@;
}
else {
    my $rows = eval { $dbh->selectall_arrayref('SELECT SLEEP(1)') };

    open FH, '>', $file or die "$file: $!";
    print FH $rows ? "child: " . Dumper($rows) : $@;
    close FH;
}

Код, который я использовал для переподключения:

...
else {
    $dbh->disconnect;
    $dbh = DBI->connect($dsn, $username, $password, $attrs);
    my $rows = eval { $dbh->selectall_arrayref('SELECT SLEEP(1)') };

    open FH, '>', $file or die "$file: $!";
    print FH $rows ? "child: " . Dumper($rows) : $@;
    close FH;
}

Есть ли безопасный способ использовать Apache :: DBI с разветвлением? Можно ли как-нибудь создать новое соединение?

Ответы [ 2 ]

1 голос
/ 31 марта 2010

вижу несколько вариантов:

  • Явно закройте ваши ручки БД при разветвлении и откройте их при необходимости.

например:.

my $dbh = DBI->connect(...);

my $pid = fork;
defined $pid or die "fork: $!";

if ($pid) {
    # parent...
}
else {
    # child...
    undef $dbh;

Эту задачу можно упростить, храня $dbh в объекте и передавая этот объект по мере необходимости в части вашей системы. Объект будет отвечать за повторное открытие $ dbh по мере необходимости, поэтому остальная часть приложения не должна заботиться о деталях. Сохраняйте код инкапсулированным и хорошо отделенным от других частей системы.

Я использую DBIx :: Connector в моей системе внутри объекта Moose, который использует делегирование метода для предоставления dbh. Приложение просто делает:

my $dbh = $db_dbj->dbh;
my $sth = $dbh->prepare(...);
# more boring DBI code here

... И DBH повторно подключается / восстанавливается по мере необходимости, невидимо.


Кроме того, вы должны быть очень осторожны с использованием простых файловых дескрипторов в многопроцессорной среде. Вы могли бы очень легко забить ваши данные. open (my $fh, $file) or die "Cannot open $file: $!" намного безопаснее.

Я также немного нервничаю, когда вижу, что вы используете блоки eval {} без проверки содержимого $@. Вы просто маскируете ошибки, а не имеете дело с ними, поэтому может происходить больше вещей, чем вы знаете. Проверьте значения результата (или лучше, используйте явный модуль обработки исключений, такой как Try :: Tiny . Use use strict; use warnings;.

PS. Я только что заметил, что вы явно включаете DBI в свой код. Не делай этого. Если вы используете Apache :: DBI в вашем файле startup_modperl.pl (или как вы называете файл начальной загрузки), вам никогда не придется включать сам DBI. Я не могу сказать наверняка, но я не был бы уверен, что нужный пакет вызывается (прошло некоторое время с тех пор, как я посмотрел на внутренности Apache :: DBI; хотя он может позаботиться об этом за вас).

0 голосов
/ 27 апреля 2010

Не раскошелиться под mod_perl2. Используйте Apache2 :: Subprocess . Смотрите также Это плохая идея, чтобы форк под mod_perl2?

...