帮助解决一致可重复的 mod_perl2 / $SIG{__DIE__} 错误

发布于 2024-08-19 03:51:31 字数 3252 浏览 4 评论 0原文

这是 Apache 2.2 上的 mod_perl2,win32 上的 ActiveState Perl 5.10。

我覆盖 $SIG{__DIE__} 并打开 DBI 的 RaiseError 标志,文档中的 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 中,我有一个“允许”错误值的数组引用,当它们从数据库返回时,我想对其执行不同的操作。当数据库抛出这些错误之一时,我想将其转换为用户友好的消息,即 JSON 格式的 $r->print 消息,并停止执行(行为 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 - 即使override 不再包含任何使其成为可能的代码!另外,我没有得到任何调用 warnCORE::die 的预期结果,只是在日志中保持沉默,所以我什至无法尝试通过我的覆盖手动跟踪执行路径。

我在每个脚本保存之间重新启动了 Apache2.2。我什至将覆盖移动到与脚本本身相同的脚本文件中,从通常所在的模块中移出,并注释掉覆盖通常所在的整个模块文件,然后重新启动。

如果我取出第一行,或者从中取出“22007”,我可以警告死亡,或者手动调试我喜欢的一切,一切都按预期工作。尽管服务器重置,“22007”却从未输出任何不同的内容,这是怎么回事?除了翻译映射之外,整个项目中的其他任何地方都没有引用“22007”,我可以从该文件中完全删除它并重新启动,结果没有什么不同。它的行为就好像它已经缓存了我当天早些时候的覆盖并且永远不会忘记。这也不是浏览器缓存问题,因为我可以添加随机查询字符串,结果没有什么不同。

这是我经历过的最奇怪、最令人沮丧的 mod_perl2 体验,我已经没有想法了。有人有任何提示吗?我唯一能想到的是这是一个缓存问题,但我已经无数次重启服务了。

因为那天已经结束了,我想我应该尝试完全重新启动服务器计算机,但它仍然没有改变任何东西。我什至在重新启动服务器之前,更改了 {state} 分配给此的唯一行:

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

然而,之后的输出将 {test} 作为“22007”,只有当我完好无损地保留 = $errno 时,它才应该是这样。

即使它是反向代理来进行缓存,这种情况对我来说也没有意义,因为请求可能不同。服务器完全重新启动后,它如何仍然分配代码中不再存在的值,即,完全重新启动后,它如何使用我的旧 $SIG{__DIE__} 覆盖,当它不再存在于任何文件中?

更新:我还尝试将允许的错误更改为“42601”,并将数据库调用更改为“select”,这会产生该错误代码,但没有将其添加到翻译地图。它仍然给我行为 B,将 {state} 设置为“42601”,因此它并不特定于“22007”。放入 {allowed} 中的任何错误代码,如果该错误确实发生,则它正在运行旧版本的覆盖。导致 {allowed} 之外的错误,并且运行当前版本。但在进行覆盖之前,它如何知道当前错误是否在 {allowed} 中,或者是否意味着什么? (因为覆盖是唯一可以对当前错误进行 grep 的地方。)

This is mod_perl2 on Apache 2.2, ActiveState Perl 5.10 for win32.

I override $SIG{__DIE__} and turn on DBI's RaiseError flag, which AFAICT from the docs, should call my override when a database call fails. It seems to almost always, except in one case, and I can't understand why.

My script has an our $page variable, and being mod_perl2, I can get at this from the override like so:

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 = 

This works fine. Now, within that $page, I have an array ref of 'allowed' error values that I want to do something different with when they come back from the DB. When the DB throws one of these errors, I want to translate it into a user-friendly message, $r->print that in JSON, and stop execution (behaviour A). For some reason, it instead returns control to the script (behaviour B).

Here's the main part of my script:

{
    $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

If I comment out the first line, I get a normal error (handling something unexpected) (behaviour C), which is what I expect, because I haven't added the error that's occurring to the list of allowed errors. What's really strange is, if I cut that first line and paste it into my $SIG{__DIE__} override, it works: the JSON response is overridden, printed, and execution stops before {test} is assigned (behaviour A). Stranger still, I can set {allowed} to any set of numbers, and so long as it contains '22007' in particular, I get behaviour B. If it doesn't, I get behaviour C. Even more strange, I can actually fill my override with anything (warnings, calls to CORE::die, etc.--as long as it compiles) and I get behaviour B still--even though the override no longer contains any of the code that would make it possible! Also I don't get any of the expected results of the calls to warn and CORE::die, just silence in the logs, so I can't even attempt to manually trace the path of execution through my override.

I have restarted Apache2.2 in between every script save. I have even moved the override to the same script file as the script itself, out of the module where it normally is, and commented out the entire module file where the override normally is, and restarted.

If I take out that first line, or take '22007' out of it, I can warn and die and otherwise manually debug all I like, and everything works as expected. What is it about '22007' that it never outputs anything different despite server resets? There are no references to '22007' anywhere else in the entire project, except the translation map, and I can delete it from that file entirely and restart and the result is no different. It's behaving as if it has cached my override from earlier in the day and will never ever forget. It's not a browser cache issue either, because I can add random query strings and the results are no different.

This is the strangest and most frustrating mod_perl2 experience I've ever had, and I've run out of ideas. Does anybody have any hints? The only thing I can think of is that it's a caching problem, yet I've restarted the service countless times.

Since it was the end of the day I thought I would try fully restarting the server computer, and it still didn't change anything. I even, before restarting the server, changed the only line where {state} is assigned to this:

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

And yet, the output afterwards had {test} as '22007', which is what it should be only if I had left = $errno intact.

Even if it was, say, the reverse proxy it goes through doing the caching, this situation doesn't make sense to me, since the request can be different. After a full server restart, how can it still be assigning a value that is no longer in the code, i.e., how can it be using my old $SIG{__DIE__} override after a full restart, when it no longer exists in any file?

Update: I also tried changing the allowed errors to '42601' and changing the db call to 'select', which produces that error code, but did not add it to the translation map. It still gives me behaviour B, setting {state} to '42601', so it's not specific to '22007'. Any error code that is put into {allowed}, if that error actually occurs, it's running the old version of the override. Cause an error that's not in {allowed} and it runs the current version. But how does it know whether the current error is in {allowed}, or that that even means anything, before getting to the override? (Because the override is the only place where {allowed} is grepped for the current error.)

. $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) { ...

This works fine. Now, within that $page, I have an array ref of 'allowed' error values that I want to do something different with when they come back from the DB. When the DB throws one of these errors, I want to translate it into a user-friendly message, $r->print that in JSON, and stop execution (behaviour A). For some reason, it instead returns control to the script (behaviour B).

Here's the main part of my script:

If I comment out the first line, I get a normal error (handling something unexpected) (behaviour C), which is what I expect, because I haven't added the error that's occurring to the list of allowed errors. What's really strange is, if I cut that first line and paste it into my $SIG{__DIE__} override, it works: the JSON response is overridden, printed, and execution stops before {test} is assigned (behaviour A). Stranger still, I can set {allowed} to any set of numbers, and so long as it contains '22007' in particular, I get behaviour B. If it doesn't, I get behaviour C. Even more strange, I can actually fill my override with anything (warnings, calls to CORE::die, etc.--as long as it compiles) and I get behaviour B still--even though the override no longer contains any of the code that would make it possible! Also I don't get any of the expected results of the calls to warn and CORE::die, just silence in the logs, so I can't even attempt to manually trace the path of execution through my override.

I have restarted Apache2.2 in between every script save. I have even moved the override to the same script file as the script itself, out of the module where it normally is, and commented out the entire module file where the override normally is, and restarted.

If I take out that first line, or take '22007' out of it, I can warn and die and otherwise manually debug all I like, and everything works as expected. What is it about '22007' that it never outputs anything different despite server resets? There are no references to '22007' anywhere else in the entire project, except the translation map, and I can delete it from that file entirely and restart and the result is no different. It's behaving as if it has cached my override from earlier in the day and will never ever forget. It's not a browser cache issue either, because I can add random query strings and the results are no different.

This is the strangest and most frustrating mod_perl2 experience I've ever had, and I've run out of ideas. Does anybody have any hints? The only thing I can think of is that it's a caching problem, yet I've restarted the service countless times.

Since it was the end of the day I thought I would try fully restarting the server computer, and it still didn't change anything. I even, before restarting the server, changed the only line where {state} is assigned to this:

And yet, the output afterwards had {test} as '22007', which is what it should be only if I had left = $errno intact.

Even if it was, say, the reverse proxy it goes through doing the caching, this situation doesn't make sense to me, since the request can be different. After a full server restart, how can it still be assigning a value that is no longer in the code, i.e., how can it be using my old $SIG{__DIE__} override after a full restart, when it no longer exists in any file?

Update: I also tried changing the allowed errors to '42601' and changing the db call to 'select', which produces that error code, but did not add it to the translation map. It still gives me behaviour B, setting {state} to '42601', so it's not specific to '22007'. Any error code that is put into {allowed}, if that error actually occurs, it's running the old version of the override. Cause an error that's not in {allowed} and it runs the current version. But how does it know whether the current error is in {allowed}, or that that even means anything, before getting to the override? (Because the override is the only place where {allowed} is grepped for the current error.)

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

抱着落日 2024-08-26 03:51:31

这是我的临时解决方法,但我想解决这个谜团,而不必在任何有允许错误的数据库调用的地方添加额外的行。

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);
}

这是行为 A。

This is my temporary workaround, but I would like to solve the mystery and not have to add the extra line everywhere I have a DB call with allowed errors.

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;
}

Then my script becomes:

{
    $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);
}

This is giving behaviour A.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文