我的Perl应用程序使用暂时不可用的资源,导致使用die
的异常。最值得注意的是,它访问由多个线程共享的SQLite数据库以及通过DBIx::Class
使用的其他应用程序。每当发生此类异常时,都应重试该操作,直到达到超时。
我更喜欢简洁的代码,因此我很快就厌倦了 为每个这样的操作键入7个额外的行:
use Time::HiRes 'sleep';
use Carp;
# [...]
for (0..150) {
sleep 0.1 if $_;
eval {
# database access
};
next if $@ =~ /database is locked/;
}
croak $@ if $@;
...所以我将它们放入(DB访问特定的)函数中:
sub _retry {
my ( $timeout, $func ) = @_;
for (0..$timeout*10) {
sleep 0.1 if $_;
eval { $func->(); };
next if $@ =~ /database is locked/;
}
croak $@ if $@;
}
我称之为:
my @thingies;
_retry 15, sub {
$schema->txn_do(
sub {
@thingies = $thingie_rs->search(
{ state => 0, job_id => $job->job_id },
{ rows => $self->{batchsize} } );
if (@thingies) {
for my $thingie (@thingies) {
$thingie->update( { state => 1 } );
}
}
} );
};
有没有更好的方法来实现这个?我是在重新发明轮子吗?是 我应该使用CPAN上的代码吗?
答案 0 :(得分:4)
我可能倾向于像这样写重试:
sub _retry {
my ( $retrys, $func ) = @_;
attempt: {
my $result;
# if it works, return the result
return $result if eval { $result = $func->(); 1 };
# nah, it failed, if failure reason is not a lock, croak
croak $@ unless $@ =~ /database is locked/;
# if we have 0 remaining retrys, stop trying.
last attempt if $retrys < 1;
# sleep for 0.1 seconds, and then try again.
sleep 0.1;
$retrys--;
redo attempt;
}
croak "Attempts Exceeded $@";
}
它与现有代码的工作方式不同,但有一些优点。
*10
的事情,就像另一张海报一样,我无法辨别其目的。$func()
所做的任何值返回给其调用者。_retry 0, sub { };
仍将执行一次,但与现有版本不同,永远不会重试,永远不会执行sub。 更多建议(但稍微不那么理性)的抽象:
sub do_update {
my %params = @_;
my @result;
$params{schema}->txn_do( sub {
@result = $params{rs}->search( @{ $params{search} } );
return unless (@result);
for my $result_item (@result) {
$result_item->update( @{ $params{update} } );
}
} );
return \@result;
}
my $data = _retry 15, sub {
do_update(
schema => $schema,
rs => $thingy_rs,
search => [ { state => 0, job_id => $job->job_id }, { rows => $self->{batchsize} } ],
update => [ { state => 1 } ],
);
};
这些也可能是您代码的便利添加。 (未经测试)
答案 1 :(得分:3)
我看到的唯一真正的问题是缺乏最后的陈述。我就是这样写的:
sub _retry {
my ($timeout, $func) = @_;
for my $try (0 .. $timeout*10) {
sleep 0.1 if $try;
eval { $func->(); 1 } or do {
next if $@ =~ /database is locked/; #ignore this error
croak $@; #but raise any other error
};
last;
}
}
答案 2 :(得分:1)
我可能会使用'return'而不是'last'(在Chas Owens修改的代码中),但净效果是相同的。我也不清楚为什么要将重试函数的第一个参数乘以10。
IMNSHO,将(重新)将常见的骨架代码分解为函数要好得多,而不是一遍又一遍地连续编写相同的代码片段。太危险了:
这些是支持使用函数或等效内联代码抽象的标准参数。
换句话说 - 创造功能的好工作。 Perl允许您动态创建函数(感谢Larry)非常有用!
答案 3 :(得分:0)
Attempt似乎与我上面描述的非常接近。现在,如果可以指定某种异常过滤器,那将会很方便。