ActiveRecord::StatementInvalid: PG InFailedSqlTransaction 活动记录:语句无效,PG事务失败。

84
我正在尝试创建一个ActiveRecord对象,但在创建时出现了这个错误。
(0.1ms)  ROLLBACK
ActiveRecord::StatementInvalid: PG::InFailedSqlTransaction: ERROR:  current transaction is       aborted, commands ignored until end of transaction block

关于这个问题,有什么想法吗?


你是否在使用列类型为 json 的 PSQL?将其更新至 PSQL 9.4 并使用 jsonb。问题解决! - germs12
这是我们遇到的主要问题。请参考以下链接:https://www.postgresql.org/docs/9.1/static/transaction-iso.html - untwal
12个回答

130

其它答案都没有解决这个问题的根本原因

问题在于当Postgres抛出异常时,它会使得同一连接上未来的事务受到影响。

解决方法是回滚引起问题的事务:

begin
  ActiveRecord...do something...
rescue Exception => e
  puts "SQL error in #{ __method__ }"
  ActiveRecord::Base.connection.execute 'ROLLBACK'

  raise e
end

请查看参考文献


2
还在 Rails AR::Transactions 手册 中提到。 - Somazx
18
请不要从Exception中捕获异常,正如此处所述:https://dev59.com/R2kw5IYBdhLWcg3wNn-hException是Ruby异常层次结构的根源,因此当您从Exception中捕获异常时,您会捕获所有东西,包括子类,如SyntaxError、LoadError和Interrupt。 - adantj
1
在我贴链接的评论中,明确说明了你的代码存在问题,并且提供了部分错误建议的原因。该链接还提供了其他信息。除非你没有阅读评论或链接,否则你的“为什么不呢?”评论毫无意义。如果你不花时间思考你的问题和答案,那么为什么要发布它们呢? - adantj
3
在立即重新引发异常的情况下,救援异常是完全可以的;正如所给出的答案中所做的那样。 - Sampson Crowley
1
使用 raise ActiveRecord::Rollback 进行回滚对我很有帮助。此外,参考 https://api.rubyonrails.org/classes/ActiveRecord/Transactions/ClassMethods.html。 - Al17
这感觉像是一个遮盖真正根本原因的绷带。真正的解决方案是识别异常并修复它。 - Code-Apprentice

87

我遇到了这个问题。只需要重新启动Rails服务器,就可以解决问题。


7
我刚遇到这个问题,重新启动服务器确实解决了它。有人能解释一下这种行为吗? - plu
2
我在测试环境中遇到了同样的问题(rspec抛出错误)。运行rake db:drop + rake db:create + rake db:migrate解决了这个问题。可能是我在迁移方面搞错了。 - Nobu
在 Rails 控制台中进行测试。重新加载不足,需要重新启动控制台。谢谢! - Volte
这不是一个可行的解决方案,当服务应该自主运行时,重新启动服务器不是一个选项。问题在于PG连接处于糟糕状态,需要重新连接或执行“ROLLBACK”以退出当前事务。 - thesmart
1
在升级到PG12和Ruby 2.6.6之后,我遇到了这个问题。我使用rvm,升级到2.6.6需要为新的gemset重新构建pg gem。rake db:test:prepare无法解决问题,但是RAILS_ENV=test rake db:drop db:create db:migrate解决了这个问题。 - Martin Streicher
就重置数据库的评论达成一致。当我执行rake db:drop + rake db:create + rake db:migrate时,问题得到了解决。 - Carson Cole

16

这个问题是在我的测试环境中出现的,原因是每个测试都被包裹在自己的事务中。

我使用了database_cleaner gem,并将其配置为如果测试使用javascript,则不将测试包装在事务中。所以为了解决这个问题,我为导致此问题的每个spec添加了js: true。(尽管实际上这些specs并没有使用javascript,但这是确保测试不会被包装在事务中最方便的方法。虽然我相信还有更少hack的方法可以做到)

供参考,这是来自spec/support/database_cleaner.rb的database_cleaner配置:

RSpec.configure do |config|

  config.before(:suite) do
    DatabaseCleaner.clean_with :deletion
  end

  config.before(:each) do
    DatabaseCleaner.strategy = :transaction
  end

  config.before(:each, :js => true) do
    DatabaseCleaner.strategy = :deletion
  end

  config.before(:each) do
    DatabaseCleaner.start
  end

  config.after(:each) do
    DatabaseCleaner.clean
  end

end

如果您没有使用database_cleaner,那么测试被包装在事务中的原因可能是在spec/spec_helper.rb中设置了use_transactional_fixtures选项为true。尝试将其设置为false。


如果您在不插入类似于DatabaseCleaner的东西的情况下禁用事务性固定装置,则您的测试数据库将继续从每次运行中积累数据。与为每个未按预期工作的规范设置js:true不同,您可以为集成规范设置它。请记住,在可行的情况下,使用:transaction而不是:deletion要慢得多,因此事务更可取。 - lobati
到目前为止,我发现与默认事务测试相比,DB清理程序增加的复杂性很大(也就是会出现更多的错误)。但我的问题在于,在同一个 it 块中有两个像 get ... 的请求。将其移出可以解决此错误。 - Ryan
我认为添加 js: true 不是解决这个问题的最佳方法。它不能解决问题,而且删除策略非常缓慢。通过切换到事务策略,我将测试速度提高了2.5倍。请参见下面的答案。 - B Seven
这对我很有用,让我确定了实际的问题。一旦问题解决了,我将use_transactional_fixtures设置为真,其他人也应该这样做。 - hummmingbear

8
你可以通过查看postgresql日志了解到实际情况。我花费了很多时间来挖掘这个问题,最终发现我们错误地使用了upsert gem导致PG错误,只有在postgresql日志中才有真实的信息。 https://github.com/seamusabshere/upsert/issues/39

1
这个链接并没有太大帮助,但是检查Postgres日志的想法确实非常有用。在我的情况下,问题出在一个未准备好的测试数据库上。谢谢! - Mat Schaffer

6

在我的规格说明中引用了一个不存在的列,导致我遇到了这个错误。请确保你的数据库是最新的,并且你的代码不会期望不存在的列。


3

问题:

  1. 程序执行了错误的SQL语句。这是问题的根本原因。
  2. 程序在错误的SQL语句之后没有立即回滚或释放保存点。
  3. 程序在错误的SQL语句之后继续执行SQL语句。
  4. PostgreSQL报错:当前事务已中止,命令将被忽略直到事务块结束

解决方案:

找出错误的SQL语句并进行修正。 如果你不想修正SQL语句,可以在错误的SQL语句之后使用ROLLBACK或RELEASE SAVEPOINT。


2
在我的情况下,Postgres的配置位于/usr/local/var/postgres/postgresql.conf,日期类型是国际格式dmy。将日期类型更改为美国格式mdy可以解决此问题。

1

在我的情况下,我收到了这个错误,只是因为我没有 rake 我的测试数据库。


0

在将Rails从4.2.2升级到4.2.5后,我遇到了类似的问题,我不得不升级pg gem,然后问题开始出现。

9) WorkPolicy#is_publicly_viewable? is publicly visible hides work if deleted
     Failure/Error: before { DatabaseCleaner.clean_with :deletion }
     ActiveRecord::StatementInvalid:
       PG::InFailedSqlTransaction: ERROR:  current transaction is aborted, commands ignored until end of transaction block
       :             SELECT tablename
                   FROM pg_tables
                   WHERE schemaname = ANY (current_schemas(false))

Teddy Widom Answer 在这方面是正确的,总结一下问题:

有时候当你使用 DatabaseCleaner.clean_with :deletion 时,可能会干扰 PostgreSQL 事务。

所以对我来说解决方案是,在测试的某些部分中将 DatabaseCleaner.clean_with :deletion 替换为 DatabaseCleaner.clean_with :truncation

还有一件事情需要提醒大家。如果你注意到了这个堆栈跟踪:

An error occurred in an `after(:context)` hook.
ActiveRecord::StatementInvalid: PG::UndefinedColumn: ERROR:  column "table_rows" does not exist
LINE 1: ...ion_schema.tables WHERE table_schema = 'test' AND table_rows...
^

...这个问题可能是由此引起的


0
为了解决这个问题,我运行了:
tail -f /usr/local/var/log/postgres.log

然后只需要复制错误并观察日志文件中的输出即可。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接