FINERACT-1724: Optimizing the account locking for Loan COB
This commit is contained in:
committed by
Arnold Gálovics
parent
f0d2e9326d
commit
dbfedf5cfd
+23
-7
@@ -29,12 +29,16 @@ import lombok.extern.slf4j.Slf4j;
|
|||||||
import org.apache.fineract.cob.domain.LoanAccountLock;
|
import org.apache.fineract.cob.domain.LoanAccountLock;
|
||||||
import org.apache.fineract.cob.domain.LoanAccountLockRepository;
|
import org.apache.fineract.cob.domain.LoanAccountLockRepository;
|
||||||
import org.apache.fineract.cob.domain.LockOwner;
|
import org.apache.fineract.cob.domain.LockOwner;
|
||||||
|
import org.apache.fineract.infrastructure.businessdate.domain.BusinessDateType;
|
||||||
import org.apache.fineract.infrastructure.core.config.FineractProperties;
|
import org.apache.fineract.infrastructure.core.config.FineractProperties;
|
||||||
|
import org.apache.fineract.infrastructure.core.service.DateUtils;
|
||||||
|
import org.apache.fineract.infrastructure.core.service.ThreadLocalContextUtil;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
import org.springframework.batch.core.StepContribution;
|
import org.springframework.batch.core.StepContribution;
|
||||||
import org.springframework.batch.core.scope.context.ChunkContext;
|
import org.springframework.batch.core.scope.context.ChunkContext;
|
||||||
import org.springframework.batch.core.step.tasklet.Tasklet;
|
import org.springframework.batch.core.step.tasklet.Tasklet;
|
||||||
import org.springframework.batch.repeat.RepeatStatus;
|
import org.springframework.batch.repeat.RepeatStatus;
|
||||||
|
import org.springframework.jdbc.core.JdbcTemplate;
|
||||||
|
|
||||||
@Slf4j
|
@Slf4j
|
||||||
@RequiredArgsConstructor
|
@RequiredArgsConstructor
|
||||||
@@ -48,6 +52,8 @@ public class FetchAndLockLoanTasklet implements Tasklet {
|
|||||||
|
|
||||||
private final FineractProperties fineractProperties;
|
private final FineractProperties fineractProperties;
|
||||||
|
|
||||||
|
private final JdbcTemplate jdbcTemplate;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public RepeatStatus execute(@NotNull StepContribution contribution, @NotNull ChunkContext chunkContext) throws Exception {
|
public RepeatStatus execute(@NotNull StepContribution contribution, @NotNull ChunkContext chunkContext) throws Exception {
|
||||||
String businessDateParameter = (String) contribution.getStepExecution().getJobExecution().getExecutionContext()
|
String businessDateParameter = (String) contribution.getStepExecution().getJobExecution().getExecutionContext()
|
||||||
@@ -60,8 +66,7 @@ public class FetchAndLockLoanTasklet implements Tasklet {
|
|||||||
}
|
}
|
||||||
List<Long> remainingIds = new ArrayList<>(allNonClosedLoanIds);
|
List<Long> remainingIds = new ArrayList<>(allNonClosedLoanIds);
|
||||||
|
|
||||||
List<List<Long>> remainingIdPartitions = Lists.partition(remainingIds,
|
List<List<Long>> remainingIdPartitions = Lists.partition(remainingIds, getInClauseParameterSizeLimit());
|
||||||
fineractProperties.getQuery().getInClauseParameterSizeLimit());
|
|
||||||
List<LoanAccountLock> loanAccountLocks = new ArrayList<>();
|
List<LoanAccountLock> loanAccountLocks = new ArrayList<>();
|
||||||
remainingIdPartitions.forEach(
|
remainingIdPartitions.forEach(
|
||||||
remainingIdPartition -> loanAccountLocks.addAll(loanAccountLockRepository.findAllByLoanIdIn(remainingIdPartition)));
|
remainingIdPartition -> loanAccountLocks.addAll(loanAccountLockRepository.findAllByLoanIdIn(remainingIdPartition)));
|
||||||
@@ -87,10 +92,21 @@ public class FetchAndLockLoanTasklet implements Tasklet {
|
|||||||
return RepeatStatus.FINISHED;
|
return RepeatStatus.FINISHED;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void applySoftLock(List<Long> alreadySoftLockedAccounts) {
|
private void applySoftLock(List<Long> accountsToLock) {
|
||||||
for (Long loanId : alreadySoftLockedAccounts) {
|
LocalDate cobBusinessDate = ThreadLocalContextUtil.getBusinessDateByType(BusinessDateType.COB_DATE);
|
||||||
LoanAccountLock loanAccountLock = new LoanAccountLock(loanId, LockOwner.LOAN_COB_PARTITIONING);
|
jdbcTemplate.batchUpdate("""
|
||||||
loanAccountLockRepository.save(loanAccountLock);
|
INSERT INTO m_loan_account_locks (loan_id, version, lock_owner, lock_placed_on, lock_placed_on_cob_business_date)
|
||||||
}
|
VALUES (?, ?, ?, ?, ?)
|
||||||
|
""", accountsToLock, getInClauseParameterSizeLimit(), (ps, id) -> {
|
||||||
|
ps.setLong(1, id);
|
||||||
|
ps.setLong(2, 1);
|
||||||
|
ps.setString(3, LockOwner.LOAN_COB_PARTITIONING.name());
|
||||||
|
ps.setObject(4, DateUtils.getOffsetDateTimeOfTenant());
|
||||||
|
ps.setObject(5, cobBusinessDate);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private int getInClauseParameterSizeLimit() {
|
||||||
|
return fineractProperties.getQuery().getInClauseParameterSizeLimit();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+5
-1
@@ -46,6 +46,7 @@ import org.springframework.context.ApplicationContext;
|
|||||||
import org.springframework.context.annotation.Bean;
|
import org.springframework.context.annotation.Bean;
|
||||||
import org.springframework.context.annotation.Configuration;
|
import org.springframework.context.annotation.Configuration;
|
||||||
import org.springframework.integration.channel.DirectChannel;
|
import org.springframework.integration.channel.DirectChannel;
|
||||||
|
import org.springframework.jdbc.core.JdbcTemplate;
|
||||||
|
|
||||||
@Configuration
|
@Configuration
|
||||||
@EnableBatchIntegration
|
@EnableBatchIntegration
|
||||||
@@ -83,6 +84,9 @@ public class LoanCOBManagerConfiguration {
|
|||||||
@Autowired
|
@Autowired
|
||||||
private FineractProperties fineractProperties;
|
private FineractProperties fineractProperties;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private JdbcTemplate jdbcTemplate;
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
@JobScope
|
@JobScope
|
||||||
public LoanCOBPartitioner partitioner(@Value("#{jobExecutionContext['loanIds']}") List<Long> loanIds) {
|
public LoanCOBPartitioner partitioner(@Value("#{jobExecutionContext['loanIds']}") List<Long> loanIds) {
|
||||||
@@ -114,7 +118,7 @@ public class LoanCOBManagerConfiguration {
|
|||||||
@Bean
|
@Bean
|
||||||
@JobScope
|
@JobScope
|
||||||
public FetchAndLockLoanTasklet fetchAndLockLoanTasklet() {
|
public FetchAndLockLoanTasklet fetchAndLockLoanTasklet() {
|
||||||
return new FetchAndLockLoanTasklet(accountLockRepository, retrieveLoanIdService, fineractProperties);
|
return new FetchAndLockLoanTasklet(accountLockRepository, retrieveLoanIdService, fineractProperties, jdbcTemplate);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
|
|||||||
+10
-5
@@ -20,7 +20,9 @@ package org.apache.fineract.cob.loan;
|
|||||||
|
|
||||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
import static org.mockito.ArgumentMatchers.any;
|
import static org.mockito.ArgumentMatchers.any;
|
||||||
|
import static org.mockito.ArgumentMatchers.anyInt;
|
||||||
import static org.mockito.ArgumentMatchers.anyLong;
|
import static org.mockito.ArgumentMatchers.anyLong;
|
||||||
|
import static org.mockito.ArgumentMatchers.anyString;
|
||||||
import static org.mockito.Mockito.lenient;
|
import static org.mockito.Mockito.lenient;
|
||||||
import static org.mockito.Mockito.mock;
|
import static org.mockito.Mockito.mock;
|
||||||
import static org.mockito.Mockito.verify;
|
import static org.mockito.Mockito.verify;
|
||||||
@@ -43,6 +45,7 @@ import org.springframework.batch.core.JobExecution;
|
|||||||
import org.springframework.batch.core.StepContribution;
|
import org.springframework.batch.core.StepContribution;
|
||||||
import org.springframework.batch.core.StepExecution;
|
import org.springframework.batch.core.StepExecution;
|
||||||
import org.springframework.batch.repeat.RepeatStatus;
|
import org.springframework.batch.repeat.RepeatStatus;
|
||||||
|
import org.springframework.jdbc.core.JdbcTemplate;
|
||||||
|
|
||||||
public class FetchAndLockLoanStepDefinitions implements En {
|
public class FetchAndLockLoanStepDefinitions implements En {
|
||||||
|
|
||||||
@@ -56,6 +59,7 @@ public class FetchAndLockLoanStepDefinitions implements En {
|
|||||||
private FetchAndLockLoanTasklet fetchAndLockLoanTasklet;
|
private FetchAndLockLoanTasklet fetchAndLockLoanTasklet;
|
||||||
private String action;
|
private String action;
|
||||||
private RepeatStatus result;
|
private RepeatStatus result;
|
||||||
|
private JdbcTemplate jdbcTemplate = mock(JdbcTemplate.class);
|
||||||
|
|
||||||
public FetchAndLockLoanStepDefinitions() {
|
public FetchAndLockLoanStepDefinitions() {
|
||||||
Given("/^The FetchAndLockLoanTasklet.execute method with action (.*)$/", (String action) -> {
|
Given("/^The FetchAndLockLoanTasklet.execute method with action (.*)$/", (String action) -> {
|
||||||
@@ -97,7 +101,8 @@ public class FetchAndLockLoanStepDefinitions implements En {
|
|||||||
contribution = new StepContribution(stepExecution);
|
contribution = new StepContribution(stepExecution);
|
||||||
contribution.getStepExecution().getJobExecution().getExecutionContext().put(LoanCOBConstant.BUSINESS_DATE_PARAMETER_NAME,
|
contribution.getStepExecution().getJobExecution().getExecutionContext().put(LoanCOBConstant.BUSINESS_DATE_PARAMETER_NAME,
|
||||||
LocalDate.now(ZoneId.systemDefault()).toString());
|
LocalDate.now(ZoneId.systemDefault()).toString());
|
||||||
fetchAndLockLoanTasklet = new FetchAndLockLoanTasklet(loanAccountLockRepository, retrieveLoanIdService, fineractProperties);
|
fetchAndLockLoanTasklet = new FetchAndLockLoanTasklet(loanAccountLockRepository, retrieveLoanIdService, fineractProperties,
|
||||||
|
jdbcTemplate);
|
||||||
});
|
});
|
||||||
|
|
||||||
When("FetchAndLockLoanTasklet.execute method executed", () -> {
|
When("FetchAndLockLoanTasklet.execute method executed", () -> {
|
||||||
@@ -108,7 +113,7 @@ public class FetchAndLockLoanStepDefinitions implements En {
|
|||||||
if ("empty steps".equals(action)) {
|
if ("empty steps".equals(action)) {
|
||||||
assertEquals(RepeatStatus.FINISHED, result);
|
assertEquals(RepeatStatus.FINISHED, result);
|
||||||
} else if ("good".equals(action)) {
|
} else if ("good".equals(action)) {
|
||||||
verify(loanAccountLockRepository, Mockito.times(3)).save(Mockito.any());
|
verify(jdbcTemplate).batchUpdate(anyString(), any(), anyInt(), any());
|
||||||
assertEquals(RepeatStatus.FINISHED, result);
|
assertEquals(RepeatStatus.FINISHED, result);
|
||||||
assertEquals(3,
|
assertEquals(3,
|
||||||
((List) contribution.getStepExecution().getJobExecution().getExecutionContext().get(LoanCOBConstant.LOAN_IDS))
|
((List) contribution.getStepExecution().getJobExecution().getExecutionContext().get(LoanCOBConstant.LOAN_IDS))
|
||||||
@@ -123,7 +128,7 @@ public class FetchAndLockLoanStepDefinitions implements En {
|
|||||||
((List) contribution.getStepExecution().getJobExecution().getExecutionContext().get(LoanCOBConstant.LOAN_IDS))
|
((List) contribution.getStepExecution().getJobExecution().getExecutionContext().get(LoanCOBConstant.LOAN_IDS))
|
||||||
.get(2));
|
.get(2));
|
||||||
} else if ("soft lock".equals(action)) {
|
} else if ("soft lock".equals(action)) {
|
||||||
verify(loanAccountLockRepository, Mockito.times(2)).save(Mockito.any());
|
verify(jdbcTemplate).batchUpdate(anyString(), any(), anyInt(), any());
|
||||||
assertEquals(RepeatStatus.FINISHED, result);
|
assertEquals(RepeatStatus.FINISHED, result);
|
||||||
assertEquals(3,
|
assertEquals(3,
|
||||||
((List) contribution.getStepExecution().getJobExecution().getExecutionContext().get(LoanCOBConstant.LOAN_IDS))
|
((List) contribution.getStepExecution().getJobExecution().getExecutionContext().get(LoanCOBConstant.LOAN_IDS))
|
||||||
@@ -138,7 +143,7 @@ public class FetchAndLockLoanStepDefinitions implements En {
|
|||||||
((List) contribution.getStepExecution().getJobExecution().getExecutionContext().get(LoanCOBConstant.LOAN_IDS))
|
((List) contribution.getStepExecution().getJobExecution().getExecutionContext().get(LoanCOBConstant.LOAN_IDS))
|
||||||
.get(2));
|
.get(2));
|
||||||
} else if ("inline cob".equals(action)) {
|
} else if ("inline cob".equals(action)) {
|
||||||
verify(loanAccountLockRepository, Mockito.times(2)).save(Mockito.any());
|
verify(jdbcTemplate).batchUpdate(anyString(), any(), anyInt(), any());
|
||||||
assertEquals(RepeatStatus.FINISHED, result);
|
assertEquals(RepeatStatus.FINISHED, result);
|
||||||
assertEquals(2,
|
assertEquals(2,
|
||||||
((List) contribution.getStepExecution().getJobExecution().getExecutionContext().get(LoanCOBConstant.LOAN_IDS))
|
((List) contribution.getStepExecution().getJobExecution().getExecutionContext().get(LoanCOBConstant.LOAN_IDS))
|
||||||
@@ -150,7 +155,7 @@ public class FetchAndLockLoanStepDefinitions implements En {
|
|||||||
((List) contribution.getStepExecution().getJobExecution().getExecutionContext().get(LoanCOBConstant.LOAN_IDS))
|
((List) contribution.getStepExecution().getJobExecution().getExecutionContext().get(LoanCOBConstant.LOAN_IDS))
|
||||||
.get(1));
|
.get(1));
|
||||||
} else if ("chunk processing".equals(action)) {
|
} else if ("chunk processing".equals(action)) {
|
||||||
verify(loanAccountLockRepository, Mockito.times(2)).save(Mockito.any());
|
verify(jdbcTemplate).batchUpdate(anyString(), any(), anyInt(), any());
|
||||||
assertEquals(RepeatStatus.FINISHED, result);
|
assertEquals(RepeatStatus.FINISHED, result);
|
||||||
assertEquals(2,
|
assertEquals(2,
|
||||||
((List) contribution.getStepExecution().getJobExecution().getExecutionContext().get(LoanCOBConstant.LOAN_IDS))
|
((List) contribution.getStepExecution().getJobExecution().getExecutionContext().get(LoanCOBConstant.LOAN_IDS))
|
||||||
|
|||||||
Reference in New Issue
Block a user