出现异常后重试操作:请批评我的代码 [英] Retrying an operation after an exception: Please criticize my code
问题描述
我的Perl应用程序使用的资源有时会暂时变得不可用,从而导致使用 die
导致异常。最值得注意的是,它使用 DBIx :: Class
访问由多个线程以及与其他应用程序共享的SQLite数据库。每当发生此类异常时,都应重试该操作,直到达到超时为止。
My Perl application uses resources that become temporarily unavailable at times, causing exceptions using die
. Most notably, it accesses SQLite databases that are shared by multiple threads and with other applications using through DBIx::Class
. Whenever such an exception occurs, the operation should be retried until a timeout has been reached.
我更喜欢简洁的代码,因此我很快就厌倦了重复
为每个这样的操作键入7条额外的行:
I prefer concise code, therefore I quickly got fed up with repeatedly typing 7 extra lines for each such operation:
use Time::HiRes 'sleep';
use Carp;
# [...]
for (0..150) {
sleep 0.1 if $_;
eval {
# database access
};
next if $@ =~ /database is locked/;
}
croak $@ if $@;
...所以我将它们放入了(特定于数据库访问的)函数中:
... so I put them into a (DB access-specific) function:
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上有我应该使用的代码吗?
Is there a better way to implement this? Am I re-inventing the wheel? Is there code on CPAN that I should use?
推荐答案
我可能会倾向于这样写重试:
I'd probably be inclined to write retry like this:
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 $@";
}
它与现有代码的工作方式不同,但具有一些优势
It doesn't work identically to your existing code, but has a few advantages.
- 我摆脱了
* 10
的东西,就像另外一张海报一样,我 - 此函数能够将
$ func()
所做的任何操作的值返回给调用者。 - 从语义上讲,该代码更类似于您正在执行的操作,至少在我迷惑的头脑中更是如此。
-
_retry 0,sub {};
仍将执行一次,但永远不会重试,这与您当前的版本不同,它将永远不会执行子
- I got rid of the
*10
thing, like another poster, I couldn't discern its purpose. - this function is able to return the value of whatever
$func()
does to its caller. - Semantically, the code is more akin to what it is you are doing, at least to my deluded mind.
_retry 0, sub { };
will still execute once, but never retry, unlike your present version, that will never execute the sub.
建议的抽象度(但略低于理性):
More suggested ( but slightly less rational ) abstractions:
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 } ],
);
};
这些也可能是对代码的方便补充。 (未经测试)
These might also be handy additions to your code. ( Untested )
这篇关于出现异常后重试操作:请批评我的代码的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!