Single transaction for solving multiple threads

As I understand it, all transactions are connected by a thread (i.e., with the context stored in ThreadLocal). For example, if:

  • I start a transaction in a transactional parent method
  • Make database insert # 1 into an asynchronous call
  • Make database insert # 2 into another asynchronous call

This will then lead to two different transactions (one for each insertion), even if they share the same "transactional" parent.

For example, suppose I perform two inserts (and using a very simple example, i.e. not using an executor or a terminating future for brevity, etc.):

@Transactional public void addInTransactionWithAnnotation() { addNewRow(); addNewRow(); } 

Performs both inserts, if required, as part of the same transaction.

However, if I wanted to parallelize these inserts for performance:

 @Transactional public void addInTransactionWithAnnotation() { new Thread(this::addNewRow).start(); new Thread(this::addNewRow).start(); } 

Then, each of these generated threads will not participate in the transaction at all, since the transactions are flow-related.

Key Question : Is there a way to safely propagate a transaction to child threads?

The only solutions that I decided to solve this problem:

  • Use a JTA or some XA manager, which by definition should be capable of this. However, I ideally do not want to use XA for my solution because of this overhead
  • Combine all the transactional work that I want to do (in the above example, the addNewRow() function) into one thread and do all the previous work in multithreaded mode.
  • Define how InheritableThreadLocal is used in transaction status and distribute it to child threads. I am not sure how to do this.

Are there any other solutions? Even if it looks a bit like a workaround (like my solutions above)?

+5
source share
2 answers

First, a clarification: if you want to speed up multiple inserts of the same type, as your example shows, you are likely to get better performance by releasing inserts in the same thread and using some type of batch insert . Depending on your DBMS, there are several methods available, see:

As for your actual question, I will personally try to transfer all the work to a workflow. This is the easiest option since you don't have to bother with ThreadLocal or transaction completion / tort. In addition, if you have your units in the same stream, if you are smart, you can apply the above dosing methods to increase productivity.

Finally, working with pipelines for workflows does not mean that you have to have one workflow, you can have a pool of workers and achieve some parallelism if it is really beneficial for your application. Think about manufacturers / consumers.

+2
source

The JTA API has several methods that implicitly affect the current transaction of threads, but this does not stop you from moving or copying a transaction between threads or performing certain operations on a transaction that are not related to the current (or any other) Thread. It does not cause headaches, but it is not the worst part ...

For raw JDBC, you do not have a JTA transaction at all. You have a JDBC connection that has its own ideas about a transaction context. In this case, the transaction is connected to the connection, not to the stream. Pass the connection and tx goes with it. But Connections are not necessarily thread safe and are probably a performance bottleneck, so sharing multiple parallel threads won't really help you. You will probably need several connections that think they are in the same transaction, which means you need XA, since db identifies such cases. At this point, you are back in the JTA, but now with the JCA in the picture to properly manage the connection. In short, you have reinvented the JavaEE application server.

For frameworks that overlap with JDBC, for example. ORMs such as Hibernate, you have an additional complication: their abstractions are not necessarily thread safe. Therefore, you cannot have a session associated with multiple threads at the same time. But you can have multiple concurrent sessions, each of which participates in a single XA transaction.

As usual, it comes down to Amdahl’s law. If the acceleration you get from using multiple connections on tx to allow multiple parallel threads to share db I / O is great compared to what you get from batch processing, then there is XA overhead. If acceleration is in local computing, and db I / O is a minor issue, then one thread that processes the JDBC connection and offloads the work of computing non-IO into the thread pool is the way to go.

+1
source

Source: https://habr.com/ru/post/1272933/


All Articles