Single transaction across multiple threads solution

Dovmo :

As I understand it, all transactions are Thread-bound (i.e. with the context stored in ThreadLocal). For example if:

  1. I start a transaction in a transactional parent method
  2. Make database insert #1 in an asynchronous call
  3. 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:

  1. 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
  2. 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.
  3. 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)?

gpeche :

First, a clarification: if you want to speed up several inserts of the same kind, as your example suggests, you will probably get the best performance by issuing the inserts in the same thread and using some type of batch inserting. Depending on your DBMS there are several techniques available, look at:

As for your actual question, I would personally try to pipe all the work to a worker thread. It is the simplest option as you don't need to mess with either ThreadLocals or transaction enlistment/delistment. Furthermore, once you have your units of work in the same thread, if you are smart you might be able to apply the batching techniques above for better performance.

Lastly, piping work to worker threads does not mean that you must have a single worker thread, you could have a pool of workers and achieve some parallelism if it is really beneficial to your application. Think in terms of producers/consumers.

Guess you like

Origin http://10.200.1.11:23101/article/api/json?id=434966&siteId=1