I am learning micro services - Spring Cloud now. During the study, I am thinking how to deal with the race condition in the following cases.
Let's say I have a "ProductOrder" service, which has 2 replicas. Now there are two order requests coming and the load balancer assigns the api request one for each replica. Note that they are sharing the same db (so I am not talking about the data synchronization. Or maybe I am?). So how can the service make sure the storage still have enough products to meet the ordering requests.
I know key word "synchronized" in java is for multi threading. However, the replicas are not multi-threading but multi process. Am I correct? Any ideas?
The database will have a table like Product(id, amount)
. Placing order will need to updat e the record for that product.
And the implementation for that will look something like this. First the application will read the product amount from the DB:
SELECT id, amount
FROM Product
WHERE id = {some_id}
FOR UPDATE;
Then application code will check if there is a requested amount. And then it will update the value in the DB:
UPDATE Product
SET amount = {new_amount}
WHERE id = {some_id}
Note that:
FOR UPDATE
clause to prevent other concurrent transactions from modification of this product while our transaction is running. Think of it as a synchronized
but not on JVM level but on DB level.This approach uses pessimistic lock, that is we lock the record we want to update for the whole duration of the transaction.
There is another approach - optimistic.
Optimistic lock requires that you have a version column in the Product table. Then the first query does not lock the record in the DB but we get the version of the record:
SELECT id, amount, version
FROM Product
WHERE id = {some_id}
The check in application works the same way as in pessimistic scenario. But the final update is different:
UPDATE Product
SET amount = {new_amount}
WHERE id = {some_id}
and version = {version_we_read_initially}
Update operation always returns the number of updated records. If we got 0 it means that the record was changed while our transaction was run. It means that the amount we saw initially (in the first query) may have been changed. In order to proceed we need to retry the whole operation from step 1, that is read the amount and version for the product again, check that there is the required amount and try to update the amount again.
Of cause there should be some limit on how many times we are going to retry. On busy system for the product that is in demand this may become a problem.