Cryptic Moo (Perl) Ошибка "Попытка благословить ссылку на..."
Вероятно, это длинный путь, но мне интересно, видел ли кто-нибудь подобную ошибку раньше, поскольку я не могу воспроизвести ее вне рабочей среды. По сути, ситуация такова:
- У меня есть модуль под названием
My::Budget::Module
(переименован для простоты), который отвечает за обновление "бюджета" для данного объекта в приложении My::Budget::Module
используетMoo
объект, который я построил, называетсяMy::Bulk::Update::Module
который делает следующее:- создать массив строк базы данных, которые необходимо обновить
- создайте строку / оператор запроса на обновление MySQL, который обновит все строки одновременно
- на самом деле обновить все строки сразу
My::Bulk::Update::Module
затем выполнит обновление и пометит строки, которые были обновлены, как "устаревшие", чтобы они не были кэшированы
Кажется, ошибка всегда возникает где-то после добавления строки, подлежащей обновлению, но до возврата кода, который фактически применяет обновление.
Если вы посмотрите на трассировку стека, которую я включил ниже, вы увидите, что ошибка принимает вид
Attempt to bless into a reference at...
и точка, в которой это происходит, находится в конструкторе Moo/Object.pm
который Version 2.003002
из Moo
от 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::Collection
здесь и MongoDB::Collection
в данном конкретном примере просто "случается" быть модулем, включенным в трассировку стека. Это не всегда MongoDB::Collection
- Я также видел MongoDB::Timestamp
, MongoDB::Cursor
, Search::Elasticsearch::Serializer::JSON
, Search::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
а иногда после выполнения запроса на последней строке...
1 ответ
На всякий случай кому-то было интересно...
После части дальнейшей отладки ошибка была прослежена до точной точки, где My::Bulk::Update::Module::apply_bulk_update
или же My::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 ссылаются на фактические живые подпрограммы. Используйте слабые ссылки, которые мы храним для отложенных и отложенных подпрограмм, чтобы убедиться, что исходные подпрограммы все еще живы, и мы не возвращаем данные, связанные с повторно используемым адресом памяти. Также убедитесь, что мы не истекаем данные, связанные с неназванными подпрограммами. Так как пользователь может захватить подчиненную подпрограмму через undefer_sub, мы не можем отследить истечение срока действия без использования хеша поля. А пока избегайте этой сложности, поскольку количество, которое мы пропускаем, не должно быть таким большим.
Обновление версии Sub::Defer
кажется, решил проблему.