Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

before_commit example #7

Closed
sofianeOuafir opened this issue Nov 14, 2019 · 4 comments
Closed

before_commit example #7

sofianeOuafir opened this issue Nov 14, 2019 · 4 comments

Comments

@sofianeOuafir
Copy link

sofianeOuafir commented Nov 14, 2019

Just creating an issue for giving an example that can be used in the doc for before_commit. Very useful in the case where you want a stripe payment to validate your transaction:

something like that:

ActiveRecord::Base.transaction do 
   amount = 4000
   payment = Payment.create(amount: amount)
   before_commit do
      Stripe::Charge.create(amount: amount,
                                           currency: 'cad',
                                           source: params['payment']['token'])
   end
end

payment will only be created if the stripe payment has successfully been done. If payment fail, stripe would raise an error and transaction would rollback.

I think in this case, it's better to make the stripe payment in before_commit cause in the case of a more complex transaction, you don't want to make the stripe payment inside the transaction, at the beginning, and have another piece of code to fail a bit later. Only make the payment if whole transaction went well (before_commit) and only validate the transaction if stripe payment didn't raise an error.

@Envek
Copy link
Owner

Envek commented Nov 15, 2019

Thank you very much for the example!

However, as I may guess, Stripe::Charge makes a request to an external API (Stripe), and I believe that it is an antipattern to make web requests during open transactions. There are a lot of good reasons: problems with database performance due to long transactions, inconsistency problems (what if something would fail after a call to Stripe but before the COMMIT). And this is why isolator gem was created to detect and avoid such things.

Sorry, I can't recommend such a usage for before_commit callback as a best practice.

In this particular case, it is much better to create Payment in a pending state, commit the transaction, then proceed with Stripe charge and then either mark it as completed or failed (or remove) depending on the results of API call. So it is much better to use something like Sagas pattern for such usage (for example, you can look at dirty_pipeline gem which is being created by another my colleague).

@sofianeOuafir
Copy link
Author

sounds good! I understand your concerns! Nice to have your feedback 👍

@Envek
Copy link
Owner

Envek commented Nov 15, 2019

Thanks for proposal. Feel free to open new issues if you have any other ideas, concerns, questions, complaints, etc.

@Envek Envek closed this as completed Nov 15, 2019
@axsuul
Copy link

axsuul commented Jan 26, 2020

I have an example I think. I'm using this gem in conjunction with https://github.com/leandromoreira/redlock-rb to achieve mutex functionality (e.g. updating a group of records and ensuring this only happens exclusively per organization at one time). The implementation involves acquiring a lock within a transaction and then determining if the lock should be released after the transaction has been committed (to avoid race conditions) or releasing the lock right away. This can be determined by the before_commit callback. If the before_commit callback gets called, then we know the lock needs to be released within the after_commit callback. If the before_commit callback does not get called, then we can release the lock right away.

Is this a good use-case for before_commit?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants