FINERACT-2181: Batch API - Fix read-only connection handling
This commit is contained in:
committed by
Arnold Gálovics
parent
0752106742
commit
bc4b3555f6
+16
-6
@@ -58,6 +58,8 @@ import org.apache.fineract.infrastructure.core.exception.ErrorHandler;
|
||||
import org.apache.fineract.infrastructure.core.filters.BatchCallHandler;
|
||||
import org.apache.fineract.infrastructure.core.filters.BatchFilter;
|
||||
import org.apache.fineract.infrastructure.core.filters.BatchRequestPreprocessor;
|
||||
import org.apache.fineract.infrastructure.core.persistence.ExtendedJpaTransactionManager;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.dao.ConcurrencyFailureException;
|
||||
import org.springframework.dao.NonTransientDataAccessException;
|
||||
import org.springframework.lang.NonNull;
|
||||
@@ -74,9 +76,9 @@ import org.springframework.transaction.support.TransactionTemplate;
|
||||
* CommandStrategy from CommandStrategyProvider.
|
||||
*
|
||||
* @author Rishabh Shukla
|
||||
* @see org.apache.fineract.batch.domain.BatchRequest
|
||||
* @see org.apache.fineract.batch.domain.BatchResponse
|
||||
* @see org.apache.fineract.batch.command.CommandStrategyProvider
|
||||
* @see BatchRequest
|
||||
* @see BatchResponse
|
||||
* @see CommandStrategyProvider
|
||||
*/
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
@@ -85,7 +87,6 @@ public class BatchApiServiceImpl implements BatchApiService {
|
||||
|
||||
private final CommandStrategyProvider strategyProvider;
|
||||
private final ResolutionHelper resolutionHelper;
|
||||
private final PlatformTransactionManager transactionManager;
|
||||
private final ErrorHandler errorHandler;
|
||||
|
||||
private final List<BatchFilter> batchFilters;
|
||||
@@ -94,6 +95,7 @@ public class BatchApiServiceImpl implements BatchApiService {
|
||||
|
||||
private final RetryConfigurationAssembler retryConfigurationAssembler;
|
||||
|
||||
private PlatformTransactionManager transactionManager;
|
||||
private EntityManager entityManager;
|
||||
|
||||
/**
|
||||
@@ -152,6 +154,9 @@ public class BatchApiServiceImpl implements BatchApiService {
|
||||
try {
|
||||
TransactionTemplate transactionTemplate = new TransactionTemplate(transactionManager);
|
||||
transactionTemplate.setIsolationLevel(TransactionDefinition.ISOLATION_REPEATABLE_READ);
|
||||
if (transactionManager instanceof ExtendedJpaTransactionManager extendedJpaTransactionManager) {
|
||||
transactionTemplate.setReadOnly(extendedJpaTransactionManager.isReadOnlyConnection());
|
||||
}
|
||||
transactionConfigurator.accept(transactionTemplate);
|
||||
return transactionTemplate.execute(status -> {
|
||||
BatchRequestContextHolder.setEnclosingTransaction(status);
|
||||
@@ -175,8 +180,8 @@ public class BatchApiServiceImpl implements BatchApiService {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the response list by getting a proper {@link org.apache.fineract.batch.command.CommandStrategy}.
|
||||
* execute() method of acquired commandStrategy is then provided with the separate Request.
|
||||
* Returns the response list by getting a proper {@link CommandStrategy}. execute() method of acquired
|
||||
* commandStrategy is then provided with the separate Request.
|
||||
*
|
||||
* @param requestList
|
||||
* @param uriInfo
|
||||
@@ -395,4 +400,9 @@ public class BatchApiServiceImpl implements BatchApiService {
|
||||
public void setEntityManager(EntityManager entityManager) {
|
||||
this.entityManager = entityManager;
|
||||
}
|
||||
|
||||
@Autowired
|
||||
public void setTransactionManager(PlatformTransactionManager transactionManager) {
|
||||
this.transactionManager = transactionManager;
|
||||
}
|
||||
}
|
||||
|
||||
+47
-4
@@ -19,11 +19,14 @@
|
||||
package org.apache.fineract.batch.service;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.anyString;
|
||||
import static org.mockito.ArgumentMatchers.argThat;
|
||||
import static org.mockito.Mockito.doThrow;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.spy;
|
||||
import static org.mockito.Mockito.times;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
@@ -51,6 +54,8 @@ import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.ValueSource;
|
||||
import org.mockito.InjectMocks;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.Mockito;
|
||||
@@ -86,8 +91,8 @@ class BatchApiServiceImplTest {
|
||||
@InjectMocks
|
||||
private RetryConfigurationAssembler retryConfigurationAssembler;
|
||||
|
||||
private final ResolutionHelper resolutionHelper = Mockito.spy(new ResolutionHelper(new FromJsonHelper()));
|
||||
private final List<BatchRequestPreprocessor> batchPreprocessors = Mockito.spy(List.of());
|
||||
private final ResolutionHelper resolutionHelper = spy(new ResolutionHelper(new FromJsonHelper()));
|
||||
private final List<BatchRequestPreprocessor> batchPreprocessors = spy(List.of());
|
||||
|
||||
@InjectMocks
|
||||
private BatchApiServiceImpl batchApiService;
|
||||
@@ -96,8 +101,9 @@ class BatchApiServiceImplTest {
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
batchApiService = new BatchApiServiceImpl(strategyProvider, resolutionHelper, transactionManager, errorHandler, List.of(),
|
||||
batchPreprocessors, retryConfigurationAssembler);
|
||||
batchApiService = new BatchApiServiceImpl(strategyProvider, resolutionHelper, errorHandler, List.of(), batchPreprocessors,
|
||||
retryConfigurationAssembler);
|
||||
batchApiService.setTransactionManager(transactionManager);
|
||||
batchApiService.setEntityManager(entityManager);
|
||||
request = new BatchRequest();
|
||||
request.setRequestId(1L);
|
||||
@@ -227,6 +233,43 @@ class BatchApiServiceImplTest {
|
||||
Mockito.verifyNoInteractions(entityManager);
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@ValueSource(booleans = { true, false })
|
||||
void testCallInTransactionReadOnlyFlag(boolean isReadOnly) {
|
||||
// Given
|
||||
ExtendedJpaTransactionManager extendedJpaTransactionManager = mock(ExtendedJpaTransactionManager.class);
|
||||
|
||||
// Create a transaction status with the correct read-only flag
|
||||
DefaultTransactionStatus transactionStatus = new DefaultTransactionStatus("txn_name", null, true, true, false, isReadOnly, false,
|
||||
null);
|
||||
|
||||
// Mock getTransaction to return our status when the read-only flag matches
|
||||
when(extendedJpaTransactionManager.isReadOnlyConnection()).thenReturn(isReadOnly);
|
||||
when(extendedJpaTransactionManager
|
||||
.getTransaction(argThat(definition -> definition != null && definition.isReadOnly() == isReadOnly)))
|
||||
.thenReturn(transactionStatus);
|
||||
|
||||
// Mock other required dependencies
|
||||
when(strategyProvider.getCommandStrategy(any())).thenReturn(commandStrategy);
|
||||
when(commandStrategy.execute(any(), any())).thenReturn(response);
|
||||
|
||||
batchApiService.setTransactionManager(extendedJpaTransactionManager);
|
||||
|
||||
// Set up a request that will trigger the read-only behavior we want to test
|
||||
BatchRequest testRequest = new BatchRequest();
|
||||
testRequest.setRequestId(1L);
|
||||
testRequest.setMethod(isReadOnly ? "GET" : "POST"); // Use GET for read-only, POST for read-write
|
||||
testRequest.setRelativeUrl("/test/endpoint");
|
||||
|
||||
// When
|
||||
List<BatchResponse> responses = batchApiService.handleBatchRequestsWithEnclosingTransaction(List.of(testRequest), uriInfo);
|
||||
|
||||
// Then
|
||||
assertFalse(responses.isEmpty());
|
||||
verify(extendedJpaTransactionManager)
|
||||
.getTransaction(argThat(definition -> definition != null && definition.isReadOnly() == isReadOnly));
|
||||
}
|
||||
|
||||
private static final class RetryException extends RuntimeException {}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user