Rails 5.1.4 + psql,ActiveRecord奇怪的双重交易BEGIN,期待一个

时间:2018-08-20 20:21:59

标签: ruby-on-rails activerecord psql puma

我认为与机架超时设置有关的问题实际上与ActiveRecord :: Base.transaction有关。

当请求到达我们的更新端点之一并且该更新在事务中处理

甚至简单的事情

def update
  ActiveRecord::Base.transaction do
    @note.find(params[:id])
    @note.update(text: "foo")
  end
end

我们的服务器日志如下所示:

started /foo
BEGIN
BEGIN
UPDATE
COMMIT

一个期望:

started /foo
BEGIN
UPDATE
COMMIT

问题是,当发生错误时,不会回滚所有事务。我们的日志如下:

started /foo
BEGIN
BEGIN
UPDATE
COMMIT
ROLLBACK

代替:

started /foo
BEGIN
UPDATE
ROLLBACK

奇怪的部分

在开发模式下,当我们以任何方式编辑任何Rails文件时。该动作可以像预期的那样通过一个BEGIN起作用。在服务器重新启动时,有两个BEGIN。同样,只有在控制器中而不在控制台中,甚至在控制台中调用控制器时,才会发生这种情况。可以做到的:

rails c
# app#action "path"
app.put "/foo"
  • 此问题是在服务器的全新启动(rails)上出现的,但是如果我编辑代码的任何部分,并且rails重新加载代码库,则该操作将按预期进行。
  • 这仅是通过控制器发生的,它采用相同的代码块并将其粘贴在resque作业中,它按预期工作。
  • 从控制台应用程序调用端点。输入“ / note”按预期方式工作。
  • 呼叫note.update可以正常工作
  • Rspec控制器和集成测试按预期工作。
  • Note.transaction可以按预期工作,尽管日志中仍然存在双BEGIN
  • Note.last.update可以按预期工作,尽管日志中仍然存在双BEGIN

  • ActiveRecord :: Base.transaction无法正常工作。

Rails 5.1.4

有人以前见过这样的问题吗?是可能的AR设置,还是可能是宝石引入的?

在具有两个开始块的请求中,第一个开始是我们正在控制器中打开的事务。那么第二个预更新从哪里开始呢?

更新

我在ActiveRecord here上开了一个问题。我将在这里总结一下。

我认为这可能(尽管从广义上讲是一个AR问题)是第二个BEGIN实际上来自使用事务的update method

来自的堆栈跟踪 lib / active_record / connection_adapters / postgresql / database_statements.rb:ln 130 在这种情况下,我在最后一次调用update语句之前在这里添加了puts调用程序,

2对于我们的应用程序 1用于其他Rails应用程序。

我们的应用

    active_record/connection_adapters/abstract/transaction.rb:130:in `initialize'
active_record/connection_adapters/abstract/transaction.rb:156:in `new'
active_record/connection_adapters/abstract/transaction.rb:156:in `block in begin_transaction'
monitor.rb:226:in `mon_synchronize'
active_record/connection_adapters/abstract/transaction.rb:152:in `begin_transaction'
active_record/connection_adapters/abstract/transaction.rb:193:in `block in within_new_transaction'
monitor.rb:226:in `mon_synchronize'
active_record/connection_adapters/abstract/transaction.rb:191:in `within_new_transaction'
active_record/connection_adapters/abstract/database_statements.rb:235:in `transaction'
active_record/transactions.rb:210:in `transaction'
active_record/transactions.rb:381:in `with_transaction_returning_status'
active_record/persistence.rb:283:in `update'
app/controllers/debugging_transactions_controller.rb:18:in `block in update'
active_record/connection_adapters/abstract/database_statements.rb:235:in `block in transaction' <<<<<<<<
active_record/connection_adapters/abstract/transaction.rb:194:in `block in within_new_transaction'
monitor.rb:226:in `mon_synchronize'
active_record/connection_adapters/abstract/transaction.rb:191:in `within_new_transaction'
active_record/connection_adapters/abstract/database_statements.rb:235:in `transaction'
active_record/transactions.rb:210:in `transaction'
app/controllers/debugging_transactions_controller.rb:12:in `update' #ActiveRecord::Base.transaction do

新的Rails应用程序,具有重复的Gemfile和Gemfile.lock文件。

active_record/connection_adapters/abstract/transaction.rb:130:in `initialize'
active_record/connection_adapters/abstract/transaction.rb:156:in `new'
active_record/connection_adapters/abstract/transaction.rb:156:in `block in begin_transaction'
monitor.rb:226:in `mon_synchronize'
active_record/connection_adapters/abstract/transaction.rb:152:in `begin_transaction' <<<<<<<<<
active_record/connection_adapters/abstract/transaction.rb:193:in `block in within_new_transaction'
monitor.rb:226:in `mon_synchronize'
active_record/connection_adapters/abstract/transaction.rb:191:in `within_new_transaction'
active_record/connection_adapters/abstract/database_statements.rb:235:in `transaction'
active_record/transactions.rb:210:in `transaction'
app/controllers/debugging_transactions_controller.rb:11:in `update' # ActiveRecord::Base.transaction do

我们的应用正在命中persistance.rb的更新操作,该操作确实将操作包装在事务中     但是为什么呢?

更新 我想出了什么,但没有找到原因。

https://github.com/rails/rails/blob/813af4655f9bf3c712cf50205eebd337070cee52/activerecord/lib/active_record/connection_adapters/abstract/transaction.rb#L151 第一次在普通的Rails应用程序中调用它。事务@ stack.size为0,AR使用可连接的RealTransaction。然后在 https://github.com/rails/rails/blob/813af4655f9bf3c712cf50205eebd337070cee52/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb#L228

RealTransaction是可联接的,因此rails运行yield(您的代码块)

在我们的应用程序中,堆栈大小为零,并且正在使用NullTransaction。这是不可连接的,这会导致AR将model.update aka with_transaction_returning_status包装在一个全新的事务中。似乎这是线程问题或连接问题?

1 个答案:

答案 0 :(得分:0)

所以我想通了。我认为这是一种彪马工人的配置,引起了奇怪的线程/双重连接问题。

在我们的config / puma.rb文件中,

on_worker_boot do
  ApplicationRecord.establish_connection if defined?(ActiveRecord)
end

将其更改为此可以解决问题。

on_worker_boot do
  ActiveRecord::Base.establish_connection if defined?(ActiveRecord)
end

奇怪的是ApplicationRecord#establish_connection != ActiveRecord::Base#establish_connection