Rails 5.1.4 + psql,ActiveRecord 奇怪的双事务开始,当期待一个

Rails 5.1.4 + psql, ActiveRecord bizarre double transaction BEGIN, when expecting one

我认为是机架超时设置的问题实际上是 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 5.1.4

有人以前遇到过这样的问题吗?可能是 AR 设置还是从 gem 引入的?

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

更新

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

我认为这可能是一个 AR 问题的原因是第二个 BEGIN 实际上来自使用事务的 update method

堆栈跟踪来自 lib/active_record/connection_adapters/postgresql/database_statements.rb:ln 130 我在调用更新语句之前最后一次在此处添加了一个 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 运行收益(您的区块)

在我们的应用程序中,堆栈大小为零,并且它使用的是 NullTransaction。这是不可连接的,然后导致 AR 将 model.update 又名 with_transaction_returning_status 包装在一个全新的交易中。看起来这是一个线程问题或者可能是一个连接问题??

所以我想通了。我认为这是一个 puma worker 配置导致了奇怪的线程/双连接问题。

在我们的 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