32

What causes this error in postgresql?

org.postgresql.util.PSQLException: ERROR: canceling statement due to user request

My Software Versions:

PostgreSQL 9.1.6 on x86_64-redhat-linux-gnu, compiled by gcc (GCC) 4.7.2 20120921 (Red Hat 4.7.2-2), 64-bit".

My postgresql driver is: postgresql-9.2-1000.jdbc4.jar

Using java version: Java 1.7

Clue: My postgresql database is on a solid state hard drive and this error happens randomly and sometimes not at all.

4

4 回答 4

29

我们已经找出了这个问题的原因。这是由最新的 JDBC 驱动程序 9.2-100x 中的 setQueryTimeout() 的错误实现来解释的。如果您手动打开/关闭连接,则可能不会发生这种情况,但通常会在连接池到位且autocommit设置为false时发生。在这种情况下,应该使用非零值调用 setQueryTimeout()(例如,使用 Spring 框架 @Transactional(timeout = xxx) 注释)。

事实证明,无论何时在语句执行期间引发 SQL 异常,取消计时器都没有被取消并保持活动状态(这就是它的实现方式)。由于池化,后面的连接没有关闭,而是返回到池中。稍后,当取消计时器触发时,它会随机取消当前与创建此计时器的连接关联的查询。目前,这是一个完全不同的查询来解释随机性效应。

建议的解决方法是放弃 setQueryTimeout() 并改用 PostgreSQL 配置 (statement_timeout)。它没有提供相同级别的灵活性,但至少总是有效。

于 2013-01-04T14:02:45.087 回答
7

这假定 postgresql 的 jdbc jar 文件中的竞争条件错误是造成上述错误的原因。

解决方法1,定期刷新与数据库的连接

一种解决方法是关闭与数据库的连接并定期创建与数据库的新连接。在每几千条 sql 语句之后,只需关闭连接并重新创建它。然后由于某种原因不再抛出此错误。

解决方法 2,打开日志记录

如果您在设置驱动程序时在 JDBC 驱动程序级别打开日志记录,那么在某些情况下会消除竞争条件问题:

Class.forName("org.postgresql.Driver");
org.postgresql.Driver.setLogLevel(org.postgresql.Driver.DEBUG);

解决方法3,捕获异常并重新初始化连接

您还可以尝试捕获特定异常,重新初始化连接并再次尝试查询。

解决方法 4,等到 postgresql jdbc jar 出现错误修复

我认为这个问题可能与我的 SSD 硬盘驱动器的速度有关。如果您收到此错误,请在此处发布如何始终如一地重现它,开发人员非常有兴趣消除此错误。

于 2012-11-12T17:34:37.500 回答
6

If you are getting this error without using transactions

The user has requested the statement be cancelled. The statement is doing exactly what it is told to do. The question is, who requested this statement be cancelled?

Look at every line in your code which prepares the SQL for execution. You could have some method that applies to the statement which cancels the statement under some circumstances, like this:

statement = conn.createStatement();
conn.setAutoCommit(true);
statement.setQueryTimeout(25);
my_insert_statement.setString(1, "moobars");

my_insert_statement.executeUpdate();
statement.close();

In my case, what happened was I had set the query timeout to 25 seconds, and when the insert took longer than that. It passed the 'canceling statement due to user request' exception.

If you are getting this error while using transactions:

If you receive this Exception, double check all your code that does SQL transactions.

If you have a query that is in a transaction and you forget to commit, and then you use that connection to do something else where you operate as if you are not in a transaction, there could be undefined behavior which produces this Exception.

Make sure all code that does a transaction is cleaning up after itself. Make sure the transaction begins, work is done, more work is done, and the transaction is rolled back or committed, then make sure the connection is left in the autocommit=true state.

If this is your problem, then the Exception is not thrown where you have forgotten to clean up after yourself, it happens somewhere long after you have failed to clean up after a transaction, making this an elusive exception to track down. Refreshing the connection (closing it and getting a new one) will clear it up.

于 2012-10-28T23:29:25.893 回答
4

除了 Eric 的建议之外,您还可以在以下情况下看到语句取消:

  • 以同一用户身份登录的管理员或其他连接pg_cancel_backend用于要求您的会话取消其当前语句
  • 管理员向运行您的语句的 PostgreSQL 后端发送信号
  • 管理员请求fast关闭或重新启动 PostgreSQL 服务器

检查可能取消长时间运行的查询的 cron 作业或负载管理工具。

于 2012-10-29T01:05:33.037 回答