3

可能是一个很长的镜头,但我想知道是否有人以前见过这样的错误,因为我无法在生产环境之外重现它。基本上情况如下:

  1. 我有一个名为My::Budget::Module(为简单起见重命名)的模块,它负责更新应用程序中给定对象的“预算”
  2. My::Budget::Module使用我构建的一个对象MooMy::Bulk::Update::Module它执行以下操作:
    • 构建需要更新的数据库行数组
    • 构建一个 MySQL 更新查询字符串/语句,它将一次更新所有行
    • 实际上一次更新所有行
  3. 然后My::Bulk::Update::Module将执行更新并将已更新的行标记为“陈旧”,以便它们不会被缓存

在添加要更新的行之后但在实际应用更新的代码返回之前,错误似乎总是发生在某处。

如果您查看我在下面包含的堆栈跟踪,您会看到错误采用以下形式

Attempt to bless into a reference at...

并且发生这种情况的点是在 from 的构造函数中Moo/Object.pmVersion 2.003002Moo参见cpan此处

Attempt to bless into a reference at /path/to/module/from/cpan/Moo/Object.pm line 25 at /path/to/module/from/cpan/Moo/Object.pm line 25.
Moo::Object::new(My::Bulk::Update::Module=HASH(0xf784b50)) called at (eval 1808) line 28
MongoDB::Collection::new(My::Bulk::Update::Module=HASH(0xf784b50)) called at /path/to/my/bulk/update/module line XXXX
My::Bulk::Update::Module::apply_bulk_update(My::Bulk::Update::Module=HASH(0xf784b50)) called at /path/to/my/budget/module line XXXX
My::Budget::Module::update_budget(My::Budget::Module=HASH(0xf699a38)) called at /path/to/my/budget/module line XXXX

向后移动堆栈跟踪会导致MongoDB::Collection&这就是事情开始变得非常奇怪的地方。

MongoDB::Collection也是一个模块,但此时出现的模块会有所不同,除了它始终是一个对象cpan外,我在这里看不到模式。Moo此外,我不确定为什么要实例化这个模块,因为MongoDB::Collection::new在提到的那一行没有调用。

此外,从堆栈跟踪来看,它看起来像MongoDB::Collection并被Moo::Object实例化,第一个参数是My::Bulk::Update::Module=HASH(0xf784b50). 鉴于应用程序逻辑,我认为不MongoDB::Collection应该在这里实例化也不应该My::Bulk::Update::Module传递给它MongoDB::Collection

除了它是一个Moo对象之外,My::Bulk::Update::Module它不扩展任何其他模块,并且被设计为一个独立的“实用程序”模块。它仅在整个应用程序中的一处使用。

有没有人见过类似的东西?

编辑:添加更多代码 -apply_bulk_update根本没有做太多。这里没有调用MongoDB::CollectionMongoDB::Collection只是“碰巧”成为这个特定示例中堆栈跟踪中包含的模块。这并不总是MongoDB::Collection- 我也见过MongoDB::Timestamp,,,MongoDB::CursorSearch::Elasticsearch::Serializer::JSONSearch::Elasticsearch::Logger::LogAny

sub apply_bulk_update
{
    my $self = shift;
    my ($db) = @_; # wrapper around DBI module

    my $query  = $self->_generate_query(); # string UPDATE table SET...
    my $params = $self->_params; # arrayref

    return undef unless $params && scalar @$params;

    $db->do($query, undef, @$params);        
}

代码有时apply_bulk_update一被调用就死掉,有时在调用时死掉,有时_generate_query在查询在最后一行执行之后......

4

1 回答 1

4

以防万一有人感兴趣...

经过大量进一步调试后,错误被追踪到调用My::Bulk::Update::Module::apply_bulk_updateMy::Bulk::Update::Module::_generate_query调用的确切点,但这些子例程中的日志记录代码确定它们没有按预期执行。

为了确定发生了什么,B::Deparse用于重建这些子程序主体的源代码(或者至少是位于这些子程序指向的内存地址的源代码)

使用这个库后,例如

B::Deparse->new->coderef2text(\&My::Bulk::Update::_generate_query)

很明显,错误发生在My::Bulk::Update::_generate_query指向包含完全不同的东西(即MongoDB::Collection::new等)的内存位置时。

此问题似乎已通过Sub::Defer模块中的以下提交在上游解决(这是 的依赖项Moo)。

https://github.com/moose/Sub-Quote/commit/4a38f034366e79b76d29fec903d8e8d02ee01896

如果您阅读提交的摘要,您可以看到所做的更改:

防止 defer_info 和 undefer_sub 对过期的潜艇进行操作。验证 defer_info 和 undefer_sub 的参数是否引用了实际的实时潜艇。使用我们存储到 deferred 和 undeferred subs 的弱引用来确保原始 subs 仍然存在,并且我们不会返回与重用内存地址相关的数据。还要确保我们不会使与未命名潜艇相关的数据过期。由于用户可以通过 undefer_sub 捕获未延迟的 sub,因此如果不使用 fieldhash,我们将无法跟踪到期时间。现在,避免引入这种复杂性,因为我们泄漏的数量不应该那么大。

升级版本Sub::Defer似乎已经解决了这个问题。

于 2018-04-17T07:27:36.913 回答