postgresql 提交嵌套事务

oyxsuwqo  于 2023-04-20  发布在  PostgreSQL
关注(0)|答案(3)|浏览(180)

假设我有一个方法,它提供对用户范围内的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

如果我在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!
}

我简化了示例,但基本上API的消费和API返回的数据的持久化需要在事务中发生。如何确保即使父事务失败,也能提交对用户令牌的更新?

kyxcudwk

kyxcudwk1#

你试过在新线程中打开一个新的数据库连接,并在这个线程中执行更新吗

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
    }

希望这能帮上忙

hiz5n14c

hiz5n14c2#

Nermin的(被接受的)答案是正确的。

Thread.new do
  Rails.application.executor.wrap do
    record.save
  end
  # Note: record probably won't be updated here yet since it's async
end

此处记录:Rails指导线程和并发

bnl4lu3b

bnl4lu3b3#

上一个问题中的discussion可能对你有帮助。看起来你可以设置一个requires_new: true标志,并将子事务标记为子事务。

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

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

  raise 'boom' 
}

相关问题