Помогите устранить неполадку постоянно повторяющейся ошибки mod_perl2 / $ SIG {__ DIE__} - PullRequest
0 голосов
/ 27 января 2010

Это mod_perl2 в Apache 2.2, ActiveState Perl 5.10 для win32.

Я переопределяю $SIG{__DIE__} и включаю флаг RaiseError DBI, который AFAICT из документов должен вызывать мое переопределение при сбое вызова базы данных. Кажется, почти всегда, за исключением одного случая, и я не могу понять, почему.

Мой скрипт имеет переменную our $page, и, будучи mod_perl2, я могу получить это из переопределения следующим образом:

use Carp::Trace;
my $full_trace = Carp::Trace::trace;
$full_trace =~ m/^(ModPerl::ROOT::ModPerl::Registry::.*::)handler .*$/m;
my $page;
if (defined $1)
{
    eval '$page = $' . $1 . 'page';
    if (defined $page)
    {
        $json = 1 if defined $$page{json_response};
        if (defined $$page{dbh})
        {
            my $errno = $$page{dbh}->state;
            if ($errno ~~ $$page{error_handling}{allowed})
            {
                # allowed to let it go--no report, expected possible user error at some level that couldn't be caught sooner (usually db level)
                my $errmsg = $$page{error_handling}{translation_map}{$errno};
                if (defined $errmsg)
                                    {
                                               ...

Это отлично работает. Теперь, в пределах этого $page, у меня есть ссылка на массив «допустимых» значений ошибок, с которыми я хочу сделать что-то другое, когда они возвращаются из БД. Когда БД выдает одну из этих ошибок, я хочу перевести ее в удобное для пользователя сообщение $r->print в JSON и остановить выполнение (поведение A). По какой-то причине он вместо этого возвращает управление сценарию (поведение B).

Вот основная часть моего сценария:

{
    $$page{error_handling}{allowed} = ['22007'];
    $$page{json_response}{result} = $page->one_liner("select 'aa'::timestamp");
    $$page{json_response}{test} = $$page{error_handling}{state};
}
    $page->make_json; # just JSONifies $$page{json_response} and prints it

Если я закомментирую первую строку, я получаю обычную ошибку (обработка чего-то неожиданного) (поведение C), чего я и ожидаю, потому что я не добавил ошибку, которая происходит, в список разрешенных ошибок. Что действительно странно, если я обрежу эту первую строку и вставлю ее в мое переопределение $SIG{__DIE__}, это сработает: ответ JSON переопределяется, печатается, и выполнение останавливается до назначения {test} (поведение A). Тем не менее, я могу установить {allowed} для любого набора чисел, и, пока он содержит, в частности, «22007», я получаю поведение B. Если это не так, я получаю поведение C. Еще более странно, я могу на самом деле заполнить мое переопределение чем-нибудь (предупреждениями, вызовами CORE::die и т. д. - до тех пор, пока оно компилируется), и я по-прежнему получаю поведение B - даже если переопределение больше не содержит кода, который сделал бы это возможным! Кроме того, я не получаю ожидаемых результатов вызовов warn и CORE::die, просто молчание в журналах, поэтому я даже не могу попытаться вручную отследить путь выполнения через мое переопределение.

Я перезапускал Apache2.2 между каждым сохранением скрипта. Я даже переместил переопределение в тот же файл скрипта, что и сам скрипт, из модуля, где он обычно находится, и закомментировал весь файл модуля, где обычно переопределение, и перезапустил.

Если я уберу эту первую строку или извлеку из нее '22007', я смогу warn и die, а в остальном вручную отладлю все, что мне нравится, и все будет работать, как и ожидалось. Что такое «22007», что он никогда не выводит ничего другого, несмотря на перезагрузку сервера? Нет ссылок на «22007» где-либо еще во всем проекте, кроме карты перевода, и я могу полностью удалить ее из этого файла и перезапустить, и результат ничем не отличается. Он ведёт себя так, как если бы он кешировал мое переопределение ранее в тот же день и никогда не забудет. Это также не проблема кеша браузера, потому что я могу добавить случайные строки запроса, и результаты ничем не отличаются.

Это самый странный и самый разочаровывающий опыт mod_perl2, который у меня когда-либо был, и у меня закончились идеи. У кого-нибудь есть намеки? Единственное, о чем я могу подумать, это проблема с кэшированием, но я перезапускал сервис бесчисленное количество раз.

Так как это был конец дня, я подумал, что попытаюсь полностью перезагрузить серверный компьютер, и это все равно ничего не изменило. Я даже перед перезапуском сервера изменил единственную строку, в которой {state} назначено:

$$page{error_handling}{state} = 'my face'; # $errno;

И, тем не менее, на выходе впоследствии было {test} как «22007», что и должно быть, только если я оставил = $errno без изменений.

Даже если это был, скажем, обратный прокси-сервер, который проходит через кэширование, эта ситуация не имеет смысла для меня, поскольку запрос может быть другим. После полного перезапуска сервера, как он может по-прежнему присваивать значение, которого больше нет в коде, т. Е. Как он может использовать мое старое переопределение $SIG{__DIE__} после полного перезапуска, когда его больше нет ни в одном файле?

Обновление: Я также попытался изменить допустимые ошибки на '42601' и изменить вызов db на 'select', который генерирует этот код ошибки, но не добавляет его в карту перевода. Это все еще дает мне поведение B, устанавливая {state} в '42601', так что это не специфично для '22007'. Любой код ошибки, который вставляется в {allowed}, если эта ошибка действительно происходит, он использует старую версию переопределения. Вызвать ошибку, которой нет в {allowed}, и она запускает текущую версию. Но как узнать, находится ли текущая ошибка в {allowed} или что это вообще что-то значит, прежде чем перейти к переопределению? (Поскольку переопределение является единственным местом, где {allowed} отображается для текущей ошибки.)

1 Ответ

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

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

package MyModule::ErrorLogging;
sub InsanityWorkaround # duplicates part of $SIG{__DIE__} override for allowed errors
{
    my ($page) = @_;
    my $r = $$page{r};
    my $errno = $$page{error_handling}{state};
    if ($errno ~~ $$page{error_handling}{allowed})
    {
        # allowed to let it go--no report, expected possible user error at some level that couldn't be caught sooner (usually db level)
        my $errmsg = $$page{error_handling}{translation_map}{$errno};
        if (defined $errmsg)
        {
            use JSON::XS qw(encode_json);
            $$page{json_response} =
            {
                error => $errmsg,
            };
            my $response = encode_json($$page{json_response});
            $r->content_type("application/json");
            $r->print($response);
            exit(0);
        }
        else
        {
            return 0; # get back to script where {state} can be checked and output can be customized even further
        }
    }
    return;
}

Тогда мой сценарий становится:

{
    $$page{error_handling}{allowed} = ['22007']; # don't be bothered by invalid timestamp error
    $$page{json_response}{result} = $page->one_liner("select 'aa'::timestamp");
    MyModule::ErrorLogging::InsanityWorkaround($page);
}

Это дает поведение А.

...