Different ways for solving Race condition in distributed environment in Rest Services in java

Viewed 644

Here is a description of a problem that I am facing right now

  1. I want to give user some promotion based on whether he has done a transaction in which he has been given a promotional discount
  2. Promotional discount is some percent off on the transaction
  3. So the condition is, before processing transactions, it will be checked whether he as done a transaction using a promotion or not, and based on that amount will be calculated.

Problem is if two request comes at the same time and reads that a transaction has not been done, and on both the transaction promotion is applied.

Found a solution, https://dzone.com/articles/synchronized-by-the-value-of-the-object-in-java, but not valid for distributed environment.

What are can be different ways to solve such a program. Was just very curious on this problem >

2 Answers

Distributed environments require distributed locks. And there are a lot of options from here, we have used zookeeper for this, some other team of mine used redis. I also know (theoretically) that hazelcast has such a principle also; I bet there are many more.

There are some things you need to consider in a distributed environment that slightly complicate things. What if the service that "provides" the locking mechanism dies, what if the client that acquired a lock never releases it (it might die or block internally forever), etc. There are fault tolerant policies for the first and there are mechanisms for the second (a timeout for release or an "auto-disconnect" of the lock).

Some time ago I worked for a company that did this "by hand" against a MSSQL database. What they did (I was only a consumer of that service), is create a java-agent that would instrument byte-code and, at some points of execution would connect to a database and try to "CAS" (compare-and-swap) a certain row in a table. I don't know the faith of that project now, but I still love the idea to this day.

Bottom line is that there are many options, really. It highly depends on your infrastructure and team that you are involved in.

There are different ways this can be achieved. Accessing the table using serializable transaction isolation level is one way. However, since you mentioned the process involves potentially long running external calls as well, then you can do a two-phase approach like this:

  • Start a transaction
  • Use "select for update" to search and lock the record that entitles the user with a promotion.
  • Check if the promotion is used already. If yes, commit the transaction, promotion is not available. If no, commit the transaction. Promotion is available.
  • Do your external calls
  • Start a transaction and do the promotion check again, with "select for update", but this time, insert the record for using the promotion as well
Related