出现异常后重试操作:请批评我的代码 [英] Retrying an operation after an exception: Please criticize my code

查看:93
本文介绍了出现异常后重试操作:请批评我的代码的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我的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.


  1. 我摆脱了 * 10 的东西,就像另外一张海报一样,我

  2. 此函数能够将 $ func()所做的任何操作的值返回给调用者。

  3. 从语义上讲,该代码更类似于您正在执行的操作,至少在我迷惑的头脑中更是如此。

  4. _retry 0,sub {}; 仍将执行一次,但永远不会重试,这与您当前的版本不同,它将永远不会执行子

  1. I got rid of the *10 thing, like another poster, I couldn't discern its purpose.
  2. this function is able to return the value of whatever $func() does to its caller.
  3. Semantically, the code is more akin to what it is you are doing, at least to my deluded mind.
  4. _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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆