【问题标题】:Rails 5.1.4 + psql, ActiveRecord bizarre double transaction BEGIN, when expecting oneRails 5.1.4 + psql,ActiveRecord 奇怪的双重事务开始,当期待一个
【发布时间】:2018-08-20 20:21:59
【问题描述】:

我认为机架超时设置的问题实际上是 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 s) 时,但如果我编辑代码的任何部分并且 rails 重新加载代码库,操作将按预期运行。
  • 此外,这仅发生在控制器中,采用相同的代码块并将其粘贴到 resque 作业中,它可以按预期工作。
  • 从控制台 app.put "/note" 调用端点按预期工作。
  • 调用 note.update 按预期工作
  • Rspec 控制器和集成测试按预期工作。
  • Note.transaction 按预期工作,但双 BEGIN 仍然存在于日志中
  • Note.last.update 按预期工作,但日志中仍然存在双 BEGIN

  • ActiveRecord::Base.transaction 未按预期工作。

Rails 5.1.4

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

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

更新

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

我认为这可能是一个 AR 问题的原因,虽然松散地说是一个 AR 问题,但事实上第二个 BEGIN 实际上来自使用事务的update method

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

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 又名 with_transaction_returning_status 包装在一个全新的事务中。似乎这是一个线程问题或连接问题?

【问题讨论】:

    标签: ruby-on-rails activerecord psql puma


    【解决方案1】:

    所以我想通了。这是一个 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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-08-30
      • 1970-01-01
      • 2012-01-11
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多