I am new to Spring and have a question about transactions.
I know that for each http request there is a servlet thread which has its own stack. As far I know all the local variables and method reside on the stack. So if I have a method public void A(); then both the servlet thread A and thread B have a copy of a function in their stack.
Now If I annotated a method with #Transactional(propagation=Propagation.REQUIRED ,timeout=1,isolation=Isolation.READ_COMMITTED) then I would like to know the following points:
Does each thread A and thread B have their own stack and work independently?
If thread A is updating something and B is reading something because of in different stack is Isolation will work? or B will read the data with out any information about the thread A?
I want to know this by the diagram so that I can understand how end to end things work?
Each thread has its own stack, you're right here. But they don't have copies of methods. Method is just a series of operations. But they have copies of variables inside this method (local variables).
Speaking about isolation level, actually it has nothing to do with threads and stacks. It refers to a database isolation level notion. Spring asks your database for that level to be used for database transaction. PostreSQL has a great doc page explaining transaction isolation.
So it's not quite right here to ask how threads see each other, because in the sense of data they see what they get from the database. And the database returns data accordingly to current transaction isolation level. Each thread starts its own transaction, i.e. it creates a new connection to the database and tells it to start a new transaction.
Example
To feel what's going on under the hood, here is an example. Suppose you have such method:
#Transactional
public Person getPerson(int id) {
Person person = em.find(Person.class, id);
return person;
}
Here is what's going on under the Spring's hood per each line:
#Transactional
public Person getPerson(int id) {
// SQL sent to the database:
// Begin transaction
Person person = em.find(Person.class, id);
// SQL sent to the database:
// select p from person p where p.id = id
// the data from the database then gets converted to Java Person class
return person;
}
// after end of the method Spring automatically commits the transaction (not always, it depends on the `propagation` setting)
// SQL sent to the database:
// commit
Please, read PostgreSQL docs that explains transaction isolation in depth. Java threads only receive data accordingly to it.
Thread B will only see modifications in the database done by Thread A after the transaction of thread A completes.
It is still possible that thread B reads something from the database, then thread A updates something in the database and commits, and then thread B overwrites what thread A wrote.
To avoid that, you need to use some type of locking on the database, or use a stricter isolation level (but that carries high performance penalties, as often the database needs to perform a lot of locking to implement the stricter isolation level)
Brifely:
yes
B will read data with out any information about the thread A
Now Why this? in a servlet enviroment you have tipicaly one or more servlet and all dataat the class level and for this reason outside the doHttp methods share same data, servlet aren't thread safe.
But of course data inside a method are thread safe because each method callis a call function and will have a your thread for serving the method request and it has its own stack.
However if you have two concurrent method call and for this reason in two separated thread the the transaction will be not the same because the thrad local aren't the same and don't share the same immage of the db it is natural if you think about JPA the persistanceContext is a first level cache that is stored in the threadlocal. the my advice is use a higer level of transaction isolation for mitigate possible problem
I hope that it can help you
Related
I have a method in servlet that inserts tutoring bookings in database. This method has a business rule that checks if the tutor of this session is already busy in that date and hour. The code looks something like this :
class BookingService {
public void insert(Booking t) {
if(available(t.getTutor(), t.getDate(), t.getTime())) {
bookingDao.insert(t);
} else {
// reject
}
}
}
The problem is that multiple users may simultaneously try to book the same tutor on the same date and time, and there is nothing that prevents them both to pass the test and insert their bookings. I've tried making insert() synchronized and using locks, but it doesn't work. How can I prevent concurrent access to this method?
Using synchronized is an inadequate way to try to solve this problem:
First, you will have coded your application so that only one instance can be deployed at a time. This isn’t just about scaling in the cloud. It is normal for an IT department to want to stand up more than one instance of an application so that it is not a single point of failure (so that in case the box hosting one instance goes down the application is still available). Using static synchronized means that the lock doesn’t extend beyond one application classloader so multiple instances can still interleave their work in an error prone way.
If you should leave the project at some point, later maintainers may not be aware of this issue and may try to deploy the application in a way you did not intend. Using synchronized means you will have left a land mine for them to stumble into.
Second, using the synchronized block is impeding the concurrency of your application since only one thread can progress at a time.
So you have introduced a bottleneck, and at the same time cut off operations’ ability to work around the bottleneck by deploying a second instance. Not a good solution.
Since the posted code shows no signs of where transactions are, I’m guessing either each DAO creates its own transaction, or you’re connecting in autocommit mode. Databases provide transactions to help with this problem, and since the functionality is implemented in the database, it will work regardless of how many application instances are running.
An easy way to fix the problem which would avoid the above drawbacks would be to put the transaction at the service layer so that all the DAO calls would execute within the same transaction. You could have the service layer retrieve the database connection from a pool, start the transaction, pass the connection to each DAO method call, commit the transaction, then return the connection to the pool.
One way you could solve the problem is by using a synchronized block. There are many things you could choose as your locking object - for the moment this should be fine:
class BookingService {
public void insert(Booking t) {
synchronized(this) {
if(available(t.getTutor(), t.getDate(), t.getTime())) {
bookingDao.insert(t);
} else {
// reject
}
}
}
}
If you have more than one instance of the servlet, then you should use a static object as a lock.
I write a java application where different threads (each thread has an own connection object using a connection pool c3p0) call a method like that.
Pseudo code:
void example(Connection connection) {
connection.update("LOCK TABLES Test WRITE");
resultSet = connection.query("SELECT * FROM Test WHERE Id = '5'");
if (resultSet.next()) {
connection.update("UPDATE Test SET Amount = '10' WHERE Id = '5'");
} else {
connection.update("INSERT INTO Test (Id, Amount) VALUES ('5', '10')");
}
connection.update("UNLOCK TABLES");
connection.commit();
}
There are a few other similar methods which lock a table, select/update/insert something and then unlock the table. The aim is to prevent race conditions and deadlocks.
Is it possible to cause MySQL deadlocks when I call such a method from different threads? If yes, can you give me an example how that happens (timing of two transactions which cause a deadlock)? I am a noob with deadlocks and I want to get into this.
Edit: Make clear that the connection that should be used in the method is passed from the thread that calls the method.
Edit: Replace READ with WRITE
It cannot here. As there is no complex logic and the code immediately commits after update, there must be always one thread which goes through. Even in more complex scenarios it would probably require highest serialization level (repeatable reads) which I believe MySql does not support.
This would possibly create a deadlock. Actually I'm not sure if it'll even execute, because you need to acquire a "WRITE" lock, not a "READ".
Please correct me if I am wrong somewhere.
I am having an issue where my transaction are not being saved to the data base and some sort of racing is occurring which screws up the data. The app is hit in parallel by multiple instances. I have used #Transactional, which I know is to do a transaction with database and the transaction is committed when the method returns.
The question is, does hitting it through multiple instance still maintain this one transaction per hit thing, or it does not handle the situation and data will screw up because of racing?
Can a solution be suggested for the given condition?
The #Transactional is not related to synchronization. It just makes sure that your flow either succeeds or fails. Each hit has its own flow and its own success or failure.
I guess what you're experiencing is due to the use of shared data.
For example. If you have a class Foo that looks like this:
public class Foo {
private static boolean flag = true;
#Transactional
public void doSomething() {
flag = false;
}
}
In this case it doesn't matter that you have many Foo instances because they all use the same flag.
Another scenario would be if you have one instance of Foo (very common if you use something like Spring) and you have data that is changed for this instance. You can look at the same Foo example and just remove the static from flag:
public class Foo {
private boolean flag = true;
#Transactional
public void doSomething() {
flag = false;
}
}
In either of those cases you need to synchronize the data changes somehow. It has nothing to do with #Transactional.
That transactions are database transactions and behavior is database engine dependant but it usually works this way:
A thread enter the method.
A thread enter the same or any other transactional method. It does not block as #Transactional is not about synchronization.
One thread execute any query that blocks a database resource. Eg. SELECT * FROM MYTABLE FOR UPDATE;.
Another thread try to execute anything that needs the same database resource. Eg. UPDATE MYTABLE SET A = A + 1; And it blocks.
The thread that acquired the lock on step 3 completes the transactional method successfully making an implicit commit or fails making an implicit rollback.
The blocked thread wakes up and continues as it can now get the resource that was locked.
I have a web application that consists of a web service with two operations: createA and createB. An handler is registered for the endpoint. This handler opens a Session and start a transaction when the request is received. Then the code of the requested operation is executed. Before the response is sent back, the transaction is committed and the session is closed.
The code of createA consists of creating an entity of type A and persisting it using Session.save() method. In DEBUG mode, after Session.save() is called, I can see that there is one insertion in the ActionQueue of the session.
The code of createB consists of :
retrieving the previously created entity of type A
creating an Entity B that references the instance of A (B has a property that represents an associated A)
updating A to reference the new instance of B
call Session.save() for the new instance of B
call Session.update() for the new modified instance of A
However, in DEBUG mode, after calling Session.save() and Session.update(), the ActionQueue of the corresponding Session is empty. But, after the transaction commits, I can see the created entity in the database.
Operation createA and createB are invoked in this order without DEBUG. An error appears during the execution of the create B when it tries to retrieve the instance of A previously created using a criteria and the Session.list() method. The problem is that the instance of A is not found.
However, if I repeat the same sequence of operations in DEBUG or using Thread.sleep(15s) between invocations of the two operations, the instance of A can be found.
Thanks
EDIT: I forgot to precise that it works on certain machines but not on others. And I don't see any differences between these machines.
If you use the same Hibernate session for both createA and createB, then it'll work. You can store the Hibernate session in the Http session for achieving this (pay attention to synchronize the access to the session object, as requests from the same browser session can com in different threads).
Your problem is, Hibernate opens a new database connection for every session. Now your database seems not to synchronize the statements. It can happen in the database the select arrives before the insert is finished. Then it just depends of the speed of the involved computers if this condition happens or not. With the debug mode or the sleep() you make one computer slower so you don't have the problem any more.
If you want to continue with two different sessions for these two procedures, you can
look for the transaction mode of your database. Some databases have a dirty read where no correct locking or synchronization is done. Check if you accidentally used such a mode.
Check the JDBC parameters (they can be used in the hibernate connection.url) if there are parameters for your database which change timing and synchronization.
Check your connection pool (for the case you're using one).
The problem is that Hibernate does not save the entity to the database when you call Session.save(). It simply prepares the statement for execution later. This happens when you the transaction ends or when the you flush the session.
Your call to B is probably happening sometimes before the transaction ends for the A request. That is why it works if you wait a little while.
Try adding session.flush() after the save call. This will force Hibernate to persist the changes to the DB.
I have a DAO class that uses Spring JDBC to access an SQLite database. I have declared transactions on the DAO methods themselves since my service layer never combines queries in a transaction.
Since I use a few worker threads in parallel but only one thread can update an SQLite DB at the same time, I use synchronized to serialize access to the DAO.
At first, I synchronized externally from my service class, for example:
synchronized (dao) {
dao.update(...);
}
Then, I figured I might as well get rid of the external synchronization and put synchronized on the DAO method itself:
public synchronized void update(...) {
// Spring JDBC calls here
}
The strange thing is: my queries now take twice the time they used to!
Why?
Well, one difference is obvious:
synchronized (dao) {
// here you are synchronizing on the transactional proxy
}
public synchronized void update(...) {
// and here you are synchronizing on the target class, *inside* the proxy
}
What the implications of this are depends on your other code, but that's the obvious difference.
My guess is your update method or entire class is annotated with Transactional or wrapped by transactional proxy through other means. This means whenever you call dao's method, the transactional proxy retrieves db connection from the pool, opens a transaction and then calls the real method.
In your first scenario you synchronize before even reaching the proxy, thus no connection and transaction magic happens. In the second scenario you do the waiting call after that.
If there are multiple threads trying to perform simultaneous updates there will be only one doing the update and the rest will be first opening new connections and then waiting for dao access. As a consequence instead of one connection being constantly reused you will have multiple connections in use. I can only guess how this really affects the performance but you can experiment with different pool size starting with one.