Perl 如何确保准备和执行之间的DB(Postgres)连接仍然有效?

Perl 如何确保准备和执行之间的DB(Postgres)连接仍然有效?,perl,postgresql,connection,reconnect,dbd-pg,Perl,Postgresql,Connection,Reconnect,Dbd Pg,我有一个守护程序脚本,它永远在while循环中运行。我有一个准备好的语句,这个语句在每个循环上执行 例如: my $dbh; sub get_dbh { return DBI->connect(...); } my $dbh = get_dbh(); my $sth = $dbh->prepare("SELECT ....") or die $DBI::errstr; while (1) { // is connection sti

我有一个守护程序脚本,它永远在while循环中运行。我有一个准备好的语句,这个语句在每个循环上执行

例如:

  my $dbh;
  sub get_dbh {
      return DBI->connect(...);
  }

  my $dbh = get_dbh();
  my $sth = $dbh->prepare("SELECT ....") or die $DBI::errstr;

  while (1) {
      // is connection still there??
      //if (!$dbh->ping) {
      //    $dbh = get_dbh();
      //}

      $sth->execute('param1');

      // do some other things ... sleep between 0 and 3600
  }
若准备好的语句是在几个小时前准备好的,那个么问题就会发生(或可能发生)。连接可能会中断,我的程序也会执行。在每次执行之前检查$dbh->ping看起来有点过分了

MySQL支持MySQL\u自动\u重新连接,非常有效。Pg没有类似的东西。我读过关于DBI::Apache的文章,但在我看来它依赖于mod_perl等。它显然是针对web应用程序的

是否有“最佳实践”方法来检查连接状态并在需要时重新连接#


我可以为每个循环准备语句,但这不是解决方案,只是解决问题的一种方法。

我不明白你为什么说在每次执行
之前执行
ping
是过分的,但另一种方法是显式处理
execute
失败的情况,因为数据库句柄无效,需要重新连接、准备语句并再次发出
execute
。这会稍微快一点,但我认为没有理由避免使用
ping
策略

是否有“最佳实践”方法来检查连接状态并在需要时重新连接#

是的,至少在我看来是这样,因为只有一种方法没有竞争条件,那就是在重试循环中执行查询,如果出现错误,它会处理错误

否则,您仍然有:

  • 准备
  • 选择1或任何您的测试语句
  • 网络断开、后端崩溃、管理员重启服务器等等
  • 执行
  • 喷溅
  • 正确的行为需要伪代码:

    while not succeeded:
        try:
            execute_statement()
            succeeded = True
        except some_database_exception:
            if transaction_is_valid():
                // a `SELECT 1` or `select 1 from pg_prepared_statements where name = 'blah'
                // succeeded in transaction_is_valid(), so the issue was probably
                // transient. Retry, possibly with a retry counter that resets the 
                // connection if more than a certain number of retries.
                // It can also be useful to examine the exception or error state to 
                // see if the error is recoverable so you don't do things like retry
                // repeatedly for a transaction that's in the error state.
            else if test_connection_usable_after_rollback():
                // Connection is OK but transaction is invalid. You might determine
                // this from the exception state or by seeing if sending a `ROLLBACK`
                // succeeds. In this case you don't have to re-prepare, just open
                // a new transaction. This case is not needed if you're using autocommit.
            else:
                // If you tried a SELECT 1; and a ROLLBACK and neither succeeded, or
                // the exception state suggests the connection is dead. Re-establish
                // it, re-prepare, and restart the last transaction from the beginning.
                reset_connection_and_re_prepare()
    
    冗长烦人?是的,但通常很容易包装在助手或库中。其他一切仍受种族限制

    最重要的是,如果您的应用程序正在执行多个事务,那么它需要记住它所执行的所有操作,直到事务提交为止,并且在出现错误时能够重试整个事务。或者告诉用户“哦,我吃了你的数据,请重新输入并重试”


    如果您不介意竞争,只想通过定期检查来处理任何明显死机的连接,只需将最后一次查询的时间存储在一个变量中。发出查询时,检查时间戳是否超过几分钟,如果超过几分钟,则发出
    SELECT 1
    或查询
    pg_prepared_语句
    ,以检查您准备的语句。你要么准备好向用户吐出错误,要么就用正确的错误处理来包装整个过程。。。在这种情况下,时间检查和测试毫无意义。

    当您知道可能会让连接闲置一个小时时,ping/测试连接是非常合理的

    更好的方法是使用DBI,并使其相对容易:

    while (1) {
        my $dbh = DBI->connect_cached(..., { RaiseError => 1 });  # This will ping() for you
        my $sth = $dbh->prepare_cached('SELECT ...');
    
        $sth->execute('param1');
    
        # Do work, sleep up to 1 hour
    }
    
    这样,您将在连接的整个生命周期中重复使用相同的准备好的语句


    (值得一提的是,现代DBD::Pg ping是通过一个高效的本机PostgreSQL调用实现的。)

    过度杀戮可能是一个很难的词,但这意味着我必须在每次执行之前执行它。我实际上在循环中有3-4次执行。我没有测试“重新连接”,但它可能会迫使我“重新准备”语句?此方法所做的只是将
    execute
    用作
    ping
    ,除此之外,如果它能工作,就没有什么可做的了。对失败的
    执行的响应将与失败的
    ping的响应相同:在发出
    执行之前,您必须连接到数据库并再次准备查询。我决定将您的应答im与connect\u cached和是的,间歇性检查空闲连接没有什么错,只是你可以通过某种方式“验证”我不同意的连接。