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
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.
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/
If you are using
ActiveRecord
, you could move your methods into the model and perform them in atransaction
block.You don't necessarily have to put in your model to do this, you can just do:
in your controller. But it's recommended to put transactions in the model.