使用PDO在postgres中自动回放

时间:2014-03-30 11:20:56

标签: php postgresql pdo

我发现postgres + PDO 自动回滚以前在抛出异常时会发生变化(即使出现异常和吞噬!)。示例(伪代码):

  $transaction->begin();
  try {
     $manager->insert("INSERT ...");
     try {
       $manager->exec("A QUERY BREAKING SOME DB CONSTRAINT LIKE A UNIQUE INDEX ...");
     } catch (\Exception $ex) {
        // IT IS CAUGHT AND SWALLOWED!
     }
     $transaction->commit();
  } catch (Exception $ex) {
     $transaction->rollback(); // THIS CLEARLY DOES NOT RUN!
  }

在postgres中,第一个插件被还原。在mysql中。

任何人都能对这件事情有所了解吗?是否有可能改变这种荒谬的行为?我想自己完成我的回滚,并且当他认为合适的时候不要去做。

1 个答案:

答案 0 :(得分:3)

这不是PDO的错,它是PostgreSQL事务管理所固有的。参见:

PostgreSQL不会回滚事务,但会将其设置为中止状态,只能回滚,并且除ROLLBACK之外的所有语句都报告错误:

  

ERROR: current transaction is aborted, commands ignored until end of transaction block

(我很惊讶在官方文档中没有找到这个;我想我需要编写一个补丁来改进它。)

因此。当您在PDO中尝试/ catch并吞下异常时,您正在捕获PHP端异常,但您没有改变PostgreSQL事务处于中止状态的事实。

如果您希望能够吞下异常并继续使用该事务,则必须在每个可能失败的语句之前create a SAVEPOINT。如果失败,您必须ROLLBACK TO SAVEPOINT ...;。如果成功,您可以RELEASE SAVEPOINT ...;。这会给数据库带来额外的开销,用于事务管理,增加往返次数,并加快事务ID的烧毁(这意味着PostgreSQL必须进行更多的后台清理工作)。

通常最好设计您的SQL,以便在正常情况下不会失败。例如,您可以验证客户端的大多数约束,将服务器端约束视为第二级保证,同时捕获客户端的大多数错误。

如果这不切实际,请使您的应用程序容错,以便它可以重试失败的事务。有时这仍然是必要的 - 例如,您通常不能使用保存点从死锁事务中止或序列化失败中恢复。保持容易发生故障的事务尽可能短,只做最少的工作也很有用,这样你就可以更少地跟踪和重复。

所以:在可能的情况下,不要吞下异常,而是在重试循环中运行容易出错的数据库代码。确保您的代码记录了在错误时重试整个事务所需的信息,而不仅仅是最新的语句。

请记住,任何事务都可能失败:DBA可能会重新启动数据库以应用修补程序,系统可能会因为失控的cron作业而耗尽RAM等。因此,容错应用程序是好的设计无论如何。

至少使用PDO异常和处理异常来支持你 - 你已经领先于大多数开发者。