As I understand it, all transactions are Thread-bound (i.e. with the context stored in ThreadLocal). For example if:
- I start a transaction in a transactional parent method
- Make database insert #1 in an asynchronous call
- Make database insert #2 in another asynchronous call
Then that will yield two different transactions (one for each insert) even though they shared the same "transactional" parent.
For example, let's say I perform two inserts (and using a very simple sample, i.e. not using an executor or completable future for brevity, etc.):
@Transactional
public void addInTransactionWithAnnotation() {
addNewRow();
addNewRow();
}
Will perform both inserts, as desired, as part of the same transaction.
However, if I wanted to parallelize those inserts for performance:
@Transactional
public void addInTransactionWithAnnotation() {
new Thread(this::addNewRow).start();
new Thread(this::addNewRow).start();
}
Then each one of those spawned threads will not participate in the transaction at all because transactions are Thread-bound.
Key Question: Is there a way to safely propagate the transaction to the child threads?
The only solutions I've thought of to solve this problem:
- Use JTA or some XA manager, which by definition should be able to do this. However, I ideally don't want to use XA for my solution because of it's overhead
- Pipe all of the transactional work I want performed (in the above example, the
addNewRow()
function) to a single thread, and do all of the prior work in the multithreaded fashion. - Figuring out some way to leverage InheritableThreadLocal on the Transaction status and propagate it to the child threads. I'm not sure how to do this.
Are there any more solutions possible? Even if it's tastes a little bit of like a workaround (like my solutions above)?