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