Reputation: 67
I am trying to implement the best locking strategy for a spring boot project that takes multiple requests from a camel endpoint and process them. Every request comes twice to this system, once with half the information and then with remaining information. Whichever comes first is persisted in a database and merged with the second one whenever it comes and sent out to output endpoint.
The problem is when both comes at the same time and does a read simultaneously on the database, they don't find any entry and both go ahead to save which ends up in one overwriting the other one.
Optional<entityObj> persistedValue = repo.findById(requestID);
if (persistedValue .isPresent()) {
<Merge Both>
} else {
log.info("Entry not found. Saving intermittent state");
EntityObj entityObj = new EntityObj();
entityObj.setReqid(reqId);
<<other setters>>;
repo.save(entityObj);
}
It works fine by synchronizing this block but I would want to avoid that due to performance reasons. (There is a lot of processing in merge both). I think a better way to make this block atomic is if its only synchronized for same requestID's. Other requests should still be able to process without waiting.
I read something about database lock on a row but no idea how to implement it. Any pointers would be great.
I have implemented custom lock to achieve this. The lock takes a requestID and only locks out any thread trying to get a lock with the same requestID, but any parallel thread can get a lock with a different request ID. it looks liks this :
public class ReqIDLock {
boolean isLocked = false;
Map<String, Boolean> lockMap = new HashMap<>();
public synchronized void lock(String requestID) throws InterruptedException {
while (Objects.nonNull(lockMap.get(requestID)) && lockMap.get(requestID)) {
wait();
}
lockMap.put(requestID, true);
}
public synchronized void unlock(String requestID) {
lockMap.put(requestID, false);
notify();
}
}
I am still looking if there can be a better approach to handle this with Spring framework.
Upvotes: 1
Views: 2777
Reputation: 9102
It works fine by synchronizing this block but I would want to avoid that due to performance reasons.
Synchronizing will not work if you need to scale beyond one instance as synchronized only gives guarantee on single VM. You need a distributed locking mechanism like the one Spring Integration based distributed locking or Sherlock. Also here is another interesting library for implementing database based distributed locking.
Upvotes: 2