Reputation: 4787
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
Upvotes: 0
Views: 1100
Reputation: 309
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/
Upvotes: 1
Reputation: 2052
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.
Upvotes: 1