Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Java - Distributed JPA Lock For Reservation

I am working on a legacy system that allows reservation scheduling. The application is stateless REST and designed to be horizontally scaled. The database, however, is shared between all instances. Before I get a lecture on design and scale, it's not mine - have to make the best of a bad situation (or codebase). Recently we have seen an issue where there are duplicate reservations. I believe it is because of the nature of request response threads. The process currently is, receive request, check database for conflicting time reservation, if none, insert. Depending on time between reads and insert it's possible that both get inserted. Scenario seems to look like this:

|------|-------|-------|
R1     C1      I1     RSP

-|--------|-------|---------|
R2       C2     I2   RSP

Where R = Request, C = DB Check, I = Insert.

So I believe I could use the @Synchronized annotation which would force all threads to be ordered. The problem comes with the fact that there are multiple instances running so that wont work overall instances. Pessimistic or Optimistic reads and writes dont seem to apply since we are trying to do a read and write combo unless I completely misunderstand. Any thoughts to handle this problem across scale? Would prefer to handle it in java via a table lock or something similar rather then add additional services (kafka, redis, etc).

EDIT: The database looks something like this and using h2 in dev and mysql in production.

 id |  start_time  | locationid | postingid | userid | durration
 --------------------------------------------------------------- 
like image 979
user3170736 Avatar asked Feb 25 '19 22:02

user3170736


People also ask

Does spring @transactional lock table?

"@Transactional" as itself on any isolation level doesn't enabling any locking. To achieve locking behaviour you should use "@Lock" annotation or use " for update" in your query.

When would you use a distributed lock?

Distributed locks provide mutually exclusive access to shared resources in a distributed environment. Distributed locks are used to improve the efficiency of services or implement the absolute mutual exclusion of accesses.

How do you implement optimistic locking in JPA?

In order to use optimistic locking, we need to have an entity including a property with @Version annotation. While using it, each transaction that reads data holds the value of the version property. Before the transaction wants to make an update, it checks the version property again.

What is locking in JPA?

In case of pessimistic locking, JPA creates a transaction that obtains a lock on the data until the transaction is completed. This prevents other transactions from making any updates to the entity until the lock is released.


1 Answers

Implementing something like this from scratch is no rocket science but maybe you may want to take a look at this GitHub project: https://github.com/alturkovic/distributed-lock

I'm neither involved in this project nor currently using it but it looks very promising. You simply need to create a Spring configuration with EnableJdbcDistributedLock:

@Configuration
@EnableJdbcDistributedLock
public class LockConfiguration {
}

And create the needed database table:

create table lock (
  id int not null auto_increment primary key,
  lock_key varchar(255) unique,
  token varchar(255),
  expireAt timestamp,
);

Once this is in place you can sychronize method calls in a distributed environment by a simple annotation (taken from the project's examples):

@JdbcLocked(expression = "#name")
public String sayHello(final String name) {
  return "Hello " + name + "!";
}
like image 174
dpr Avatar answered Oct 08 '22 05:10

dpr