Home > database >  How to discard all changes executed on an async task when it raises an error in Sidekiq?
How to discard all changes executed on an async task when it raises an error in Sidekiq?

Time:09-23

I am using Sidekiq and Rails (6.0.3.7). I have a worker which executes an async task that creates a lot of data on my database, sequentially. So basically what it happens is, for example:

  • First, it creates an User, then it creates a PostCategory, then it creates a Post, and then it creates 10 comments.

Sometimes, this process fails midway, maybe when creating a PostCategory, or when creating Post.

What i want to happen is that if the task fails at any given point, all the data that has been already created in said task, is discarded. Another approach could be that all the data is created only once i am sure the process has not failed. So basically it would have to "check create" everything, before actually writing to the database.

An example of this would be that the User has been created, but for some reason, it failed to create a PostCategory, and the AsyncTask failed. What i want to happen is that it automatically deletes the created User, or that it was never created in the first place, because the task failed.

Is there any approach or technique i could use to do this on my current worker without messing around too much with the actual code? Some "double check" method already implemented on Sidekiq? What do you recommend i should look into?

Thanks in advance for any help you can give me with this issue.

CodePudding user response:

First of all, great, that you design your tasks to be all-or-none. The main and preferable approach is to use database transactions, as that is what they were designed for. Open transaction before starting entity creation, and commit once all the checks are done.

Account.transaction do
    balance.save!
    account.save!
end

Note bang methods (those with trailing !) their intent is to throw exceptions. An exception will automatically rollback the transaction, and that's what you need.

N.B. Try to make your task idempotent which mean returning the same result regardless of the number of calls with the same input results. This could save lots of time in the future.

  • Related