what does suspending a transaction means? - spring

If we are using Propagation Requires_new then it suspends existing transaction and creates a new transaction. So what does it mean to suspends a transaction? what happens to the suspended transaction? what exactly happens behind the scene?
update
what happens to the resource held by suspended transaction?

First of all the question here is a duplication of the thread: How
does transaction suspension work in Spring?. However, I will try
to answer this in a different way.
To understand the working of Spring #Transaction API, we must look inside the transaction propagation mechanism.
Spring managed transaction has physical and logical transactions depending upon the configuration.
PROPAGATION_REQUIRES_NEW uses a completely independent transaction
for each affected transaction scope. The underlying physical
transactions are different and hence can commit or roll back
independently. Here the outer transaction is not affected by an
inner transaction’s rollback status.
When a transaction is suspended, it waits until it can pick up where it left off. This means, the changes that happened while the transaction is suspended are NOT part of the same atomic unit. In other words, the things that happened while the transaction is suspended won’t be rolled back if the suspended transaction (after it comes back to life) fails to commit.
Spring transaction does not expose any API for developers to control this directly, other than the transaction configurations. However if you are using JTA to manage transaction then you can call the suspend and resume methods as below:
Transaction tobj = TransactionManager.suspend();
..
TransactionManager.resume(tobj);
I hope this helps you!

Related

Calling saveAndFlush within a JPA Transaction

I have an application that persists data within a transaction using JPA 'save'. A second application accesses this data asynchronously on receiving a message from the first application. However, depending on timing the data is sometimes not accessible by the second application as the main transaction has not yet completed (or is delayed). I am considering changing 'save' to 'saveAndFlush'. Will this ensure that the objects are saved contemporaneously? Will the objects persisted with 'saveAndFlush' be rolled back on a transaction failure?
Objects persisted with saveAndFlush will be rolled back but this will not solve your real problem as changes wont be visible to a request from the second application until the transaction is committed.

Spring Boot DB connection release mode

Is there a way to control database connection release strategy in Spring Boot? Something like Hibernate's ConnectionReleaseMode?
My test code would be rougly following:
INSERT query
HTTP call
UPDATE query
INSERT and UPDATE queries are their own methods in a repository bean (either extending CrudRepository, or as a Mybatis #Mapper). HTTP call is in it's own bean.
Now my service bean is where I experiment with different #Transactional settings as my ultimate goal is to have these three steps executed within a single transaction.
The problem is, the HTTP call can take hundreds of millis and Spring is holding the database connection during that time. This quickly leads to an empty connection pool, while the connections themselves are idle.
I have done the same experiments with default configuration using spring-boot-starter-data-jpa and also using mybatis-spring-boot-starter.
The only thing which got me closer to my goal was setting spring.jpa.open-in-view=false for data-jpa, which would release db connections in case of not using #Transactional at all or with propagation set to NEVER. It doesn't work if it's all wrapped in a transactin though.
I'm feeling like I'm missing some vital part of the transaction concept in Spring. Though the Spring reference docs mentions release mode only in relation to JTA transaction manager + JEE container + Hibernate.
Transactions are bound to a connection/session so you can't release the connection in the middle of a transaction. You have to finish the transaction to close/release the connection. Keep in mind that a transaction could abort at any time, and since the HTTP call is not "part of" the transaction, but just runs while the transaction is in progress, it won't help you to keep the transaction open while the HTTP call is running. I'd suggest you go with one of the following solutions:
Use a circuit breaker to cancel the HTTP call if it hits a timeout to have an upper bound for how long a connection/transaction can be open/held
Move the HTTP call out of the transaction, either before or after the transaction
Use two database transactions
Note that you could use a transactional Job Scheduler to schedule a job in the first TX. The job could then, with at least once semantics, try to invoke the HTTP call and then continue with the second transaction or do some compensation if something fails.

Transacted Route and Transactional Endpoints, Transaction commit order

My route looks like below
from("jms:queue:IN_QUEUE) //(A) Transactional Endpoint
.transacted("required") //(B) TX Policy with PROPAGATION_REQUIRED and JPATxManager
.bean("someBean", "readFromDB()") //(C) Read from DB
.bean("someBean", "writeToDB()") //(D) Write to DB
.to("file:/home/src?fileName=demo_${id}.txt")
I know the JMS consumer at (A) will fork out JMS Transaction on each poll and attaches to the
thread.
Also the transacted node in (B) will fork out JPA transaction after an exchange reaches there
and attaches to the thread.
Please find my questions below:
Can two different transactions get attached to a single thread (like the one above) ?
If Yes, which one should get suspended ?
What should be the commit and rollback order of the above mentioned route ?
Note: I didn't find any obvious answer from Camel In Action 2nd Ed book, So please guide me
Good afternoon,
This is a variation on your other question.
The:
from("jms:queue:IN_QUEUE) //(A) Transactional Endpoint
endpoint is transacted, meaning that you have marked the JMS component as transacted and the JMS sessions will be managed by a JmsTransactionManager.
.transacted("required") //(B) TX Policy with PROPAGATION_REQUIRED and
JPATxManager
This should not be a JPA transaction manager, but a JTA transaction manager (like Arjuna). As in your other question, you now have a JMS local transaction for reading you message, and local JPA transacted sessions for your DB access. You want the PlatformTransactionManager (a JTA transaction manager) to synchronize the local transactions for you.
As to your questions:
Can two different transactions get attached to a single thread (like the one above) ?
That really doesn't make any sense.
If Yes, which one should get suspended ?
Nothing will get suspended.
What should be the commit and rollback order of the above mentioned route ?
The DB read is not transactional and does not need to be committed. The file write will actually happen as the JTA transactional context is closed. This leaves the DB write. If that fails, then the DB read does not matter, the message will get put back on the source destination and the file write will not be called.
Enabling the DEBUG logging for the various transaction managers is very helpful.
I could go on about this with this in painful detail. This is more for burki. I think that you will really appreciate this. Very subtle, and it happens a lot.
from("jms:queue:SRC_QUEUE")
.transacted("required")
.to("jms1:queue:DEST_QUEUE")
What happens if the two endpoints are marked as transacted ... but... you do not have the 'transacted' line? Well, a JMS local transaction was started on the message listener. This will be committed as the route ends. There are two independent local JMS transactions. These are not synchronized by a JTA transaction manager.
What actually happens is that the commit for the message 'get' is called. There is no actual commit for the message 'put'. The message 'put' is committed when the JMS session is closed. This is in the JMS spec, that closing the connection inherently commits any transaction. So, because there is no linkage between the two components, the 'get' is committed, and then the 'put' session is closed.
This means that you can lose messages if there is an outage between the commit for the message 'get' and the session close for the message 'put'.
Does that make sense? There is no linkage between the local transactions, so Camel closes them in order, starting with committing the 'get' before calling the 'put'.
JTA transaction synchronization is the key. You still have local transaction resources (not XA), but they can be very well managed in a pretty lightweight JTA transactional context.
from("jms:queue:SRC_QUEUE")
.transacted("required")
.to("DB:transactedwrite")
.to("jms1:queue:DEST_QUEUE")
I couldn't be bothered to look up the correct syntax for a database insert, but you get the idea. In this case, you can get duplicate DB inserts if the JMS 'put' fails. This is not 'all or nothing' XA transactions. The transactions are committed in order. If one in the middle succeeds, then the next transaction fails, well the 'get' will get rolled back and you will get duplicates up to the point of failure.
Sorry, I can't answer your specific questions, but I can give some specific infos about the transactions of your route.
You've got 3 different "systems" with different transaction "scopes"
A JMS broker from which you consume
A database you read and write from and you configured a JPA TxManager for
A file system (no transactions at all) as destination
First of all, if you want to have transaction safety across JMS and the database you have to use XA transactions.
Then, it is unclear if you expect the JMS consumer to be transacted (because of the transacted() in your route) or if you really configured a JMS connection with local JMS transactions. I assume that you really consume transacted.
Let's talk about what you got without line B of your route:
You consume transacted from the broker
Camel processes the message through your route
When any error occurs during route processing, the message is not committed on the broker and therefore redelivered to your route
The transaction that is opened by a transactional consumer is kept open by Camel until the route is successfully processed.
So the only obvious problem would be an error after the database write, that triggers a redelivery and the database write is done once again. Probably the write is not idempotent and therefore must not happen twice.
So to solve these problems, you either have to use XA transactions or you simply use local JMS transactions and implement compensation logic for the "gaps" like the one described above.
The database transaction on the other hand has no benefit unless the read and write operation must be done in a transaction (but I have doubts that this is the case with two individual bean calls and a JMS consumer).

JTA Timeout with nothing to commit

I have a very basic JTA question.
We are using Spring's AOP to apply WebLogicJtaTransactionManager pointcuts on any method in a service class...and we set the tx:method timeout="60".
What is interesting is within that service, we run a select statement to retrieve records from the database (using Hibernate/JPA) and then go do some work that is not database/transactional related. That method takes longer than 60 seconds to run and a timeout exception is never thrown and the transaction is never rolled back. We don't update or create new objects to persist.
To add to that, we have JTA configured to timeout after 180 seconds on WebLogic Server...and the application still doesn't timeout.
The WLS docs say "The transaction timeout in seconds. If the transaction is still in the "active" state after this time (counting from begin()), it is automatically rolled back. Once the transaction moves on to the prepared state, however, this timeout parameter does not apply; the transaction is retried until all the resources are committed."
Would this behavior be related to the fact that there is nothing for the JTA to commit and therefore the timeout doesn't necessarily apply?
Or does it related to the fact that because there is no real database (insert/update/deletes) so the transaction is never in a true active state?
The JTA transaction timeout would only apply to XA based datasources. Can you confirm that that the datasource is setup with an XA database driver ?

Spring JMS Testing and Transactional Rollback

I have a test harness using Spring's SpringJUnit4ClassRunner (which automatically rolls back DB updates at the end of each JUnit test). Now I want to test my Spring-based JMS code:
Some DB item is created
A JMS message is sent to do something
The MDP picks up the message and does something else in the DB
What is actually happening in the Test is the following:
Spring begins the transaction (tx1) on thread 1
The DB item is created in the tx1 context
The JMS message is sent
Transction tx1 is rolled back and thread 1 exits
Thread 2 spins up the MDP, begins transaction tx2, reads the message, and does DB work
Thread 2 commits tx2, as its not spun up by the test harness, and exits
What I want to have happen is for tx1 and tx2 to both be controlled by SpringJUnit4ClassRunner so I get the automatic rollback. Even better would be if they could share the same overall transactional context; right now, if something is saved in tx1, even if Thread 1 hasn't rolled back that tx, tx2 doesn't see the DB changes.
You need to use a transaction manager that supports XA, and is thus able to do a two-phase commit on the database and the JMS engine. There are several such transaction managers (Bitronix, Atomikos, etc.) freely available.
If you already use such a transaction manager (because your app runs in an application server providing it), but you don't want to configure something as heavy just for the unit tests, then implement two unit tests
one where the JMS part is mocked up (to test the DB item creation only),
and one where only the JMS message handling is tested
Note that without XA, you'll probably have to handle bizarre situations where the JMS message is handled whereas the first transaction is not committed yet, or where the database transaction is comitted but the sending of the message fails.

Resources