New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Propagation REQUIRES_NEW may cause connection pool deadlock #26250
Comments
What's the problem? |
Propagation.REQUIRES_NEW May cause all connections to be occupied.if this happens, the entire process will be blocked and unable to provide services |
When all connections in the connection pool are occupied by the transaction method that does not execute to the last newly started transaction, all threads cannot obtain new connections, and the whole process will be blocked. |
Do you know |
REQUIRES_NEW is not_ New will occupy two connections, but this transaction propagation behavior will suspend the previous transaction and restart a transaction. When a new transaction is opened, a new connection must be obtained. The previously suspended transaction connection will not be released. |
It's by design, If first connection released, the outer transaction cannot continue. |
if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRES_NEW) { |
The above is the source code. When a new transaction is opened, the previous transaction will be suspended, so that the peripheral methods will occupy multiple connections during the execution process. Suppose that there are 50 connections in the database connection pool. At this time, 50 threads are just executing to the peripheral methods. They are all preparing to open new transactions and obtain new connections, but they will never get them because the connections have been exhausted. |
After this happens, all connections in the process may be blocked.When this happens, all threads that will start the transaction will be blocked. |
My question is, this way is likely to cause the whole process to block, so what is the significance of it? |
As I said, if connection is released, the outer transaction cannot be resumed. |
I know that if the connection has been released, the external transaction cannot be resumed. But |
I explained why |
@service
} |
Nested new start transactions cannot be used in actual production environments. It is likely that all connections are occupied when executing to the peripheral transaction method, and then blocked at the method of the newly started inner transaction. |
I think there is something wrong with the design of |
It's not forum here, if you think it's a bug please paste test case, if you think it's an improvement please submit PR, end of discussion. |
Sorry, I didn't make it clear. This is the test code, where it blocks the real process. Methodb() will never execute. @slf4j
} @SpringBootTest
} spring: Here is the log: |
I think the result is expected, you should increase max-active > concurrency, so If you change max-active to 21, it will pass, waiting for your confirmation. |
I know, I just want to express that. In fact, the number of connections in the connection pool is generally not large. In the above example, only one layer is nested. If there are multiple layers of newly started transactions in the code, only a small amount of concurrency is needed to trigger the above result. I just think that when multiple threads compete for resources, each thread must grab multiple resources before ending the task, which may be a hidden danger. Because this method may cause multiple threads to rob only a part of the resources they need. They can't release what they have acquired without getting all the resources they need. By this time, however, resources have been exhausted. |
Your test blocked because you didn't set timeout for ds.getConnection(), for example |
I'm sorry to have taken you so long. I just want to put forward what I think may be hidden dangers. It's not that I have encountered problems in use. I just don't quite understand why this place is designed in this way. Even if Max wait is set here, the above situation may occur. It's just going to wait for a while, not the whole process, but it's bad enough. For this reason, I choose to avoid using REQUIRES_NEW, if I need to start a new transaction in a method, I will use asynchronous or transactionSynchronization.afterCommit ()。 You may say that the outer method will not be able to sense the exception, but why should it? These are two transactions. If I need the outer layer to sense the inner layer's anomaly, I can use NESTED. |
I admit your title "Propagation.REQUIRES_NEW May cause all connections to be occupied". |
Thank you for taking the time to listen to me describe the problem,That's what I mean. If we must use 'REQUIRES_NEW' needs to be dealt with properly. It would be great if it could be mentioned in the documentation to make other developers aware of it. |
The javadoc and reference should mention that |
This comment was marked as resolved.
This comment was marked as resolved.
This comment was marked as resolved.
This comment was marked as resolved.
Requires_new is king of side effects. When i began to wrote integration tests i understood that this propagation prevents my transactions from rollback, but i didnt expect this behaviour would make my database dirty after a test, i had transactional annotation on my test methods and i know that requires_new starts new transaction so it wouldn't be rolled-back, only outer transaction would be rolled-back. And i don't know how to avoid this behaviour in my tests, only way is to not to use requires_new. And what is weird i have never heard about this problem when read articles about testing spring applications. And why i also dont like requires_new is that it can make my program throw pessimistic lock exceptions and it's very hard to find a reason |
A method that opens a transaction calls another method that starts a new transaction,if all connections are exhausted before the last new transaction method is executed,then all threads in the process will block,this process will fail.
Pseudo code:
The text was updated successfully, but these errors were encountered: