简体   繁体   English

提交嵌套事务

[英]Commit a nested transaction

Let's say I have a method that provides access to an API client in the scope of a user and the API client will automatically update the users OAuth tokens when they expire. 假设我有一个方法可以访问用户范围内的API客户端,API客户端会在用户过期时自动更新用户OAuth令牌。

class User < ActiveRecord::Base

  def api
    ApiClient.new access_token: oauth_access_token,
                  refresh_token: oauth_refresh_token,
                  on_oauth_refresh: -> (tokens) {
                    # This proc will be called by the API client when an 
                    # OAuth refresh occurs
                    update_attributes({
                      oauth_access_token: tokens[:access_token],
                      oauth_refresh_token: tokens[:refresh_token]
                     })
                   }
  end

end

If I consume this API within a Rails transaction and a refresh occurs and then an error occurs - I can't persist the new OAuth tokens (because the proc above is also treated as part of the transaction): 如果我在Rails事务中使用此API并且发生刷新然后发生错误 - 我无法持久保存新的OAuth令牌(因为上面的proc也被视为事务的一部分):

u = User.first

User.transaction { 
  local_info = Info.create!

  # My tokens are expired so the client automatically
  # refreshes them and calls the proc that updates them locally.
  external_info = u.api.get_external_info(local_info.id)

  # Now when I try to locally save the info returned by the API an exception
  # occurs (for example due to validation). This rolls back the entire 
  # transaction (including the update of the user's new tokens.)
  local_info.info = external_info 
  local_info.save!
}

I'm simplifying the example but basically the consuming of the API and the persistence of data returned by the API need to happen within a transaction. 我正在简化示例,但基本上消耗API和API返回的数据持久性需要在事务中发生。 How can I ensure the update to the user's tokens gets committed even if the parent transaction fails. 即使父事务失败,如何确保提交对用户令牌的更新。

Have you tried opening a new db connection inside new thread, and in this thread execute the update 您是否尝试在新线程中打开新的数据库连接,并在此线程中执行更新

u = User.first

User.transaction { 
   local_info = Info.create!

   # My tokens are expired so the client automatically
   # refreshes them and calls the proc that updates them locally.
   external_info = u.api.get_external_info(local_info.id)

   # Now when I try to locally save the info returned by the API an exception
   # occurs (for example due to validation). This rolls back the entire 
   # transaction (including the update of the user's new tokens.)
   local_info.info = external_info 
   local_info.save!

   # Now open new thread
   # In the new thread open new db connection, separate from the one already opened
   # In the new connection execute update only for the tokens
   # Close new connection and new thread
   Thread.new do
      ActiveRecord::Base.connection_pool.with_connection do |connection|
         connection.execute("Your SQL statement that will update the user tokens")        
      end
   end.join
}

I hope this helps 我希望这有帮助

This discussion from a previous question might help you. 上一个问题的讨论可能会对您有所帮助。 It looks like you can set a requires_new: true flag and essentially mark the child transaction as a sub transaction. 看起来您可以设置requires_new: true标志,并将子事务标记为子事务。

User.transaction { 
  User.transaction(requires_new: true) { 
    u.update_attribute(:name, 'test') 
  }; 

  u.update_attribute(:name, 'test2'); 

  raise 'boom' 
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM