Source code of transaction suspension and transaction recovery of spring transaction

When learning spring transactions, we will certainly involve a concept that cannot be avoided, that is, transaction suspension and transaction recovery
For transaction suspension and transaction recovery, it can be briefly described as follows
1. as like as two peas, we first assume that there are two classes, a and B classes, and the fields in two classes are exactly the same. Class A represents current transactions, and B class represents backup transactions.
2. If I start a transaction, the current transaction information will be stored in class A. if I want to suspend the transaction at this time
3. Transaction suspension: the information of the current transaction in class A will be assigned to class B, and then assigned to class A when creating A new transaction
4. Restore transaction: if my current transaction is completed and I need to restore the original transaction, I only need to clear class A, and then assign the data information in class B to class A. at this time, transaction a will take effect again

I think it can be understood as a toss

Transaction pending source code

org.springframework.transaction.support.AbstractPlatformTransactionManager#handleExistingTransaction
 Let's jump directly into this method. This method will enter this method for processing when the current transaction exists. The execution link is like this
org.springframework.transaction.interceptor.TransactionInterceptor#invoke
	org.springframework.transaction.interceptor.TransactionAspectSupport#invokeWithinTransaction
		org.springframework.transaction.interceptor.TransactionAspectSupport#createTransactionIfNecessary
			org.springframework.transaction.support.AbstractPlatformTransactionManager#getTransaction
				org.springframework.transaction.support.AbstractPlatformTransactionManager#handleExistingTransaction

Normally, the execution of a transaction method is this link. You can debug it yourself. However, to enter this method, there is a premise that the current transaction already exists, and then another transaction method is called
We use promotion_ REQUIRES_ Take the propagation mechanism at the new level as an example. Why take this as an example? Because this propagation mechanism will suspend the current transaction and start a new transaction when the current transaction exists. You can also see how spring suspends the transaction and creates a new transaction

if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRES_NEW) {
	if (debugEnabled) {
		logger.debug("Suspending current transaction, creating new transaction with name [" +
				definition.getName() + "]");
	}
	/**
	 * This is the operation of suspending a transaction. If a transaction is suspended, the property in the transaction manager will be set to null
	 * ,The properties in the transaction manager are then temporarily stored in the suspended resource holder
	 */
	SuspendedResourcesHolder suspendedResources = suspend(transaction);
	try {
		boolean newSynchronization = (getTransactionSynchronization() != SYNCHRONIZATION_NEVER);
		DefaultTransactionStatus status = newTransactionStatus(
				definition, transaction, true, newSynchronization, debugEnabled, suspendedResources);
		// Open transaction
		doBegin(transaction, definition);
		// Bind a transaction to a thread
		prepareSynchronization(status, definition);
		return status;
	}
	catch (RuntimeException | Error beginEx) {
		/**
		 * If an exception occurs when starting a new transaction, the transaction will be resumed in the following method (as opposed to the above suspension)
		 * ,In fact, it is to reassign the attribute in suspendResourceHolder to transaction synchronization manager
		 */
		resumeAfterBeginException(transaction, suspendedResources, beginEx);
		throw beginEx;
	}
}

Because there are a lot of codes in this method, I deleted some, leaving only the propagation_requires_new is the code of this propagation mechanism
As you can see, suspend(transaction) will be called to suspend the current transaction, then doBegin() below will be used to start a new transaction, and then the transaction related information will be put into threadLocal through preparessynchronization ()

suspend(transaction)

/**
* This is the source code of the pending transaction
 * The so-called transaction suspension is to save the relevant attributes in the current transaction manager to the suspended resource holder
 */
@Nullable
protected final SuspendedResourcesHolder suspend(@Nullable Object transaction) throws TransactionException {
	/**
	 * 1.If the current transaction is in the active state, the transaction will be suspended. The suspended operation is actually simple
	 * Temporarily store the property information of the current transaction in the SuspendedResourcesHolder, and then set the property of the current transaction to null
	 */
	if (TransactionSynchronizationManager.isSynchronizationActive()) {
		List<TransactionSynchronization> suspendedSynchronizations = doSuspendSynchronization();
		try {
			Object suspendedResources = null;
			if (transaction != null) {
				suspendedResources = doSuspend(transaction);
			}
			/**
			 * 1.1 The following is the operation of suspending a transaction. Set the property in the transaction synchronization manager to null
			 * , Then, the configuration information is stored in suspended resources so that the transaction can be restored when the transaction is restored
			 */
			String name = TransactionSynchronizationManager.getCurrentTransactionName();
			TransactionSynchronizationManager.setCurrentTransactionName(null);
			boolean readOnly = TransactionSynchronizationManager.isCurrentTransactionReadOnly();
			TransactionSynchronizationManager.setCurrentTransactionReadOnly(false);
			Integer isolationLevel = TransactionSynchronizationManager.getCurrentTransactionIsolationLevel();
			TransactionSynchronizationManager.setCurrentTransactionIsolationLevel(null);
			boolean wasActive = TransactionSynchronizationManager.isActualTransactionActive();
			TransactionSynchronizationManager.setActualTransactionActive(false);
			return new SuspendedResourcesHolder(
					suspendedResources, suspendedSynchronizations, name, readOnly, isolationLevel, wasActive);
		}
		catch (RuntimeException | Error ex) {
			/**
			 * 2.If the pending transaction fails, you need to roll back, that is, suspend the resourcesholder
			 * The properties in are reassigned to the TransactionSynchronizationManager
			 */
			// doSuspend failed - original transaction is still active...
			doResumeSynchronization(suspendedSynchronizations);
			throw ex;
		}
	}
	else if (transaction != null) {
		// Transaction active but no synchronization active.
		Object suspendedResources = doSuspend(transaction);
		return new SuspendedResourcesHolder(suspendedResources);
	}
	else {
		// Neither transaction nor synchronization active.
		return null;
	}
}

This is the source code of suspend. You can see that the attribute information of the current transaction will be obtained at the annotation position of 1.1, and then the attribute information of the current transaction will be put into new SuspendedResourcesHolder()
Next, we will judge and handle some exceptions. We can think that this method is to store the attribute information of the transaction into the SuspendedResourcesHolder object

newTransactionStatus()

This method is also very important. It will put the suspend object just created into the DefaultTransactionStatus class. I guess it is used when the transaction is resumed later

doBegin()

In the doBegin() method, it is mainly to re obtain a database connection, and then set the connection related information, such as non automatic submission, etc
The connection information is then stored in the TransactionSynchronizationManager object
We can simply think of doBegin() as reopening a transaction connection

Transaction recovery

Transaction suspension is mentioned earlier, and transaction recovery is described below. Transaction recovery is the process of transaction recovery when a transaction is committed or rolled back

A diagram is directly pasted here, which shows the processing flow of transaction submission and transaction rollback. In the end, it will call the cleanupAfterCompletion() method, which is the code of transaction recovery

private void cleanupAfterCompletion(DefaultTransactionStatus status) {
		status.setCompleted();
		if (status.isNewSynchronization()) {
			TransactionSynchronizationManager.clear();
		}
		if (status.isNewTransaction()) {
			doCleanupAfterCompletion(status.getTransaction());
		}
		if (status.getSuspendedResources() != null) {
			if (status.isDebug()) {
				logger.debug("Resuming suspended transaction after completion of inner transaction");
			}
			Object transaction = (status.hasTransaction() ? status.getTransaction() : null);
			resume(transaction, (SuspendedResourcesHolder) status.getSuspendedResources());
		}
	}

In this code, the previous logic processing should be to clear the current transaction. The last line of code, the resume() method, needs to be paid attention to

protected final void resume(@Nullable Object transaction, @Nullable SuspendedResourcesHolder resourcesHolder)
			throws TransactionException {

		if (resourcesHolder != null) {
			Object suspendedResources = resourcesHolder.suspendedResources;
			if (suspendedResources != null) {
				doResume(transaction, suspendedResources);
			}
			List<TransactionSynchronization> suspendedSynchronizations = resourcesHolder.suspendedSynchronizations;
			if (suspendedSynchronizations != null) {
				TransactionSynchronizationManager.setActualTransactionActive(resourcesHolder.wasActive);
				TransactionSynchronizationManager.setCurrentTransactionIsolationLevel(resourcesHolder.isolationLevel);
				TransactionSynchronizationManager.setCurrentTransactionReadOnly(resourcesHolder.readOnly);
				TransactionSynchronizationManager.setCurrentTransactionName(resourcesHolder.name);
				doResumeSynchronization(suspendedSynchronizations);
			}
		}
	}

Here you can see that some parameters are assigned from the resources holder to the transaction synchronization manager; Which object is SuspendedResourcesHolder? This is an object to which the current transaction parameter information is assigned when the previous transaction is suspended

Therefore, we can think that transaction suspension is to assign a transaction to a temporary object, and transaction recovery is to assign transaction attribute information to the current transaction from the temporary object

Tags: Java Spring

Posted on Sat, 25 Sep 2021 08:10:15 -0400 by kingbeastie