gpt4 book ai didi

ruby-on-rails - Rails 的模型 Hook 会等到数据库事务完成吗?

转载 作者:行者123 更新时间:2023-12-04 08:56:25 25 4
gpt4 key购买 nike

我有一个带有 after_save 的模型和 after_commit钩。 after_save钩子(Hook)在数据库中存储了after_commit所需的一些信息。钩。
假设所有数据库事务在 after_commit 之前完成是否安全?钩子(Hook)着火了?
这里有一个小例子来说明这个问题:

class TicketComment < ApplicationRecord
after_save :extract_mentions
after_commit :notify

def extract_mentions
current_mentions = mentions.map(&:user).map(&:username)
new_mentions = description.scan(/(?<=^|(?<=[^a-zA-ZÀ-ž0-9_-]))@([a-zA-ZÀ-ž]+[a-zA-ZÀ-ž0-9_]+)/).flatten

mentions_to_add = new_mentions - current_mentions
mentions_to_remove = current_mentions - new_mentions

users_to_add = User.select(:id).where("username IN (?)", mentions_to_add.flatten).map(&:id)
users_to_remove = User.select(:id).where("username IN (?)", mentions_to_remove.flatten).map(&:id)

users_to_add&.each do |id_to_add|
mentions.create(user_id: id_to_add)
end

mentions.where(user_id: users_to_remove).destroy_all
end

def notify
user_ids = ticket.participants.pluck(:id) - [Current.user.id]
TicketCommentMailer.comment_added(id, user_ids).deliver_later
end
end
请注意,此示例已简化。 extract_mentions -part 是跨多个模型使用的模型问题,因此我无法运行 notify extract_mentions 中的代码-钩。

最佳答案

用一些引用来扩展我的评论,是的,您可以假设所有数据库事务都在您的 #after_commit 之前完成。回调运行。这是 active_record/transaction.rb 中的评论(github):

#after_commit callbacks are called on every record saved or destroyed within atransaction immediately after the transaction is committed. #after_rollback callbacksare called on every record saved or destroyed within a transaction immediately after thetransaction or savepoint is rolled back.

These callbacks are useful for interacting with other systems since you will be guaranteedthat the callback is only executed when the database is in a permanent state.


事务代码有点复杂,因为它处理嵌套事务、保存点等。但最终,在 TransactionManager.commit_transaction 中( active_record/connect_adapters/abstract/transaction.rb ),管理器确实在运行 #after_commit 之前执行了特定于数据库的提交操作回调与父事务关联的所有唯一记录 ID。
def commit_transaction
@connection.lock.synchronize do
...
transaction.commit # executes database-specific commit action
transaction.commit_records # executes after_commit callbacks
end
end
所有保存和销毁操作,包括它们的持久性回调,都隐式包装在事务 block 中。根据 Rails 中的另一条评论 active_record/transaction.rb :

#transaction calls can be nested. By default, this makes all databasestatements in the nested transaction block become part of the parenttransaction.


因此, #create 生成的数据库语句和 #destroy_all在您的 #after_save回调与原始父事务同时提交,并且可以依赖于 #after_commit打回来。一个简化的例子来说明:
class Foo < ApplicationRecord
after_save :do_stuff
after_commit :check

def do_stuff
Bar.create!(name: 'bar')
end

def check
Rails.logger.info "Bar count: #{Bar.count}"
Rails.logger.info "name: #{Bar.first.name}"
end
end
输出日志:
> Foo.create!(name: "Jerry")
(0.0ms) begin transaction
Foo Create (0.3ms) INSERT INTO "foos" ("name", "created_at", "updated_at") VALUES (?, ?, ?) [["name", "Jerry"], ["created_at", "2020-09-16 01:49:57.480367"], ["updated_at", "2020-09-16 01:49:57.480367"]]
Bar Create (0.2ms) INSERT INTO "bars" ("name", "created_at", "updated_at") VALUES (?, ?, ?) [["name", "bar"], ["created_at", "2020-09-16 01:49:57.485953"], ["updated_at", "2020-09-16 01:49:57.485953"]]
(2.3ms) commit transaction
(0.1ms) SELECT COUNT(*) FROM "bars"
Bar count: 1
Bar Load (0.1ms) SELECT "bars".* FROM "bars" ORDER BY "bars"."id" ASC LIMIT ? [["LIMIT", 1]]
bar
请注意,当然任何 #after_commit对于在事务中创建、更新或销毁的任何模型,都会调用回调。所以,如果 Bar还定义了 #after_commit回调,那些也会运行。

关于ruby-on-rails - Rails 的模型 Hook 会等到数据库事务完成吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/63807263/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com