Rails controller - execute action only if the two

2019-09-11 02:09发布

I have a Controller with a method called 'actions'. When the app goes inside the method 'example_action', it implements a first method (1) update_user_table and then (2) another update_userdeal_table. (both will read and write database)

My issue is the following: in case of timeout in the middle of the controller, I want to avoid the case where the User table (via method 1) is updated but the UserDeal table is NOT (via method 2). In my app, for mobile users if they're in a subway with internet connection, they launch the request that goes through 'example_action' controller, performs successfully the first method (1) but then they enter a tunnel for 60 seconds with very very low (<5b/sec) or NO internet connection, so for UX reasons, I timeout the request and display to the user 'sorry too long, try again'. The problem is that the "damage" is already here in the database:) => (1) was already performed but not (2). And in my app it is totally out of question to update the User table if the Userdeal was not updated (it would create data consistency issues...)

I need the two methods (1) and(2) to be "mutually dependent": if one does not succeed, the other one should not be performed. It's the best way I can describe it.

In practice, as (1) happens first, if (1) fails, then (2) won't be performed. Perfect.

The problem is if (1) succeeds and (2) is not performed. How can I say to Rails, if (2) is not performed successfully, then I don't want to execute any of the things inside the block 'example_action'.

Is that possible ?

class DealsController < ApplicationController
  def example_action
    update_user_table
    update_userdeal_table   
  end

  private
  def update_user_table
    # update the table User so it needs to connect to internet and acces the distant User table
  end
  def update_userdeal_table
    # update the table UserDeal table so it needs to connect to internet and access the distant UserDeal table
  end
end

2条回答
ゆ 、 Hurt°
2楼-- · 2019-09-11 02:31

The best way to deal with this is to use transactions. You can go ahead and use transaction in controller itself, but it is generally considered to be bad practice.

class DealsController < ApplicationController
  def example_action
    Deal.transaction do
      update_user_table
      update_userdeal_table   
    end
  end
end

So like others mentioned you can move the methods to a common method in the Model and wrap it with a transaction block.

I found a really nice post about rails transaction. http://markdaggett.com/blog/2011/12/01/transactions-in-rails/

查看更多
Melony?
3楼-- · 2019-09-11 02:44

If you are using ActiveRecord, you could move your methods into the model and perform them in a transaction block.

class DealsController < ApplicationController
  def example_action
      // 'user' would have to be defined, or you could work with it as a class method in some way
      user.make_the_deal
  end
end

class User < ActiveRecord::Base
  def make_the_deal
    transaction do
      update_user_table
      update_userdeal_table
    end
  end

  def update_user_table
  end

  def update_userdeal_table
  end
end

You don't necessarily have to put in your model to do this, you can just do:

User.transaction do
  update_user_table
  update_userdeal_table
end

in your controller. But it's recommended to put transactions in the model.

查看更多
登录 后发表回答