From dbfedf5cfdffbddfd400f51498c02a88c0551bd1 Mon Sep 17 00:00:00 2001 From: Arnold Galovics Date: Tue, 28 Mar 2023 10:15:47 +0200 Subject: [PATCH] FINERACT-1724: Optimizing the account locking for Loan COB --- .../cob/loan/FetchAndLockLoanTasklet.java | 30 ++++++++++++++----- .../cob/loan/LoanCOBManagerConfiguration.java | 6 +++- .../loan/FetchAndLockLoanStepDefinitions.java | 15 ++++++---- 3 files changed, 38 insertions(+), 13 deletions(-) diff --git a/fineract-provider/src/main/java/org/apache/fineract/cob/loan/FetchAndLockLoanTasklet.java b/fineract-provider/src/main/java/org/apache/fineract/cob/loan/FetchAndLockLoanTasklet.java index 80bb45c88..e7c758677 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/cob/loan/FetchAndLockLoanTasklet.java +++ b/fineract-provider/src/main/java/org/apache/fineract/cob/loan/FetchAndLockLoanTasklet.java @@ -29,12 +29,16 @@ import lombok.extern.slf4j.Slf4j; import org.apache.fineract.cob.domain.LoanAccountLock; import org.apache.fineract.cob.domain.LoanAccountLockRepository; 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.service.DateUtils; +import org.apache.fineract.infrastructure.core.service.ThreadLocalContextUtil; import org.jetbrains.annotations.NotNull; import org.springframework.batch.core.StepContribution; import org.springframework.batch.core.scope.context.ChunkContext; import org.springframework.batch.core.step.tasklet.Tasklet; import org.springframework.batch.repeat.RepeatStatus; +import org.springframework.jdbc.core.JdbcTemplate; @Slf4j @RequiredArgsConstructor @@ -48,6 +52,8 @@ public class FetchAndLockLoanTasklet implements Tasklet { private final FineractProperties fineractProperties; + private final JdbcTemplate jdbcTemplate; + @Override public RepeatStatus execute(@NotNull StepContribution contribution, @NotNull ChunkContext chunkContext) throws Exception { String businessDateParameter = (String) contribution.getStepExecution().getJobExecution().getExecutionContext() @@ -60,8 +66,7 @@ public class FetchAndLockLoanTasklet implements Tasklet { } List remainingIds = new ArrayList<>(allNonClosedLoanIds); - List> remainingIdPartitions = Lists.partition(remainingIds, - fineractProperties.getQuery().getInClauseParameterSizeLimit()); + List> remainingIdPartitions = Lists.partition(remainingIds, getInClauseParameterSizeLimit()); List loanAccountLocks = new ArrayList<>(); remainingIdPartitions.forEach( remainingIdPartition -> loanAccountLocks.addAll(loanAccountLockRepository.findAllByLoanIdIn(remainingIdPartition))); @@ -87,10 +92,21 @@ public class FetchAndLockLoanTasklet implements Tasklet { return RepeatStatus.FINISHED; } - private void applySoftLock(List alreadySoftLockedAccounts) { - for (Long loanId : alreadySoftLockedAccounts) { - LoanAccountLock loanAccountLock = new LoanAccountLock(loanId, LockOwner.LOAN_COB_PARTITIONING); - loanAccountLockRepository.save(loanAccountLock); - } + private void applySoftLock(List accountsToLock) { + LocalDate cobBusinessDate = ThreadLocalContextUtil.getBusinessDateByType(BusinessDateType.COB_DATE); + jdbcTemplate.batchUpdate(""" + 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(); } } diff --git a/fineract-provider/src/main/java/org/apache/fineract/cob/loan/LoanCOBManagerConfiguration.java b/fineract-provider/src/main/java/org/apache/fineract/cob/loan/LoanCOBManagerConfiguration.java index eb8b7695f..27a9cf91c 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/cob/loan/LoanCOBManagerConfiguration.java +++ b/fineract-provider/src/main/java/org/apache/fineract/cob/loan/LoanCOBManagerConfiguration.java @@ -46,6 +46,7 @@ import org.springframework.context.ApplicationContext; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.integration.channel.DirectChannel; +import org.springframework.jdbc.core.JdbcTemplate; @Configuration @EnableBatchIntegration @@ -83,6 +84,9 @@ public class LoanCOBManagerConfiguration { @Autowired private FineractProperties fineractProperties; + @Autowired + private JdbcTemplate jdbcTemplate; + @Bean @JobScope public LoanCOBPartitioner partitioner(@Value("#{jobExecutionContext['loanIds']}") List loanIds) { @@ -114,7 +118,7 @@ public class LoanCOBManagerConfiguration { @Bean @JobScope public FetchAndLockLoanTasklet fetchAndLockLoanTasklet() { - return new FetchAndLockLoanTasklet(accountLockRepository, retrieveLoanIdService, fineractProperties); + return new FetchAndLockLoanTasklet(accountLockRepository, retrieveLoanIdService, fineractProperties, jdbcTemplate); } @Bean diff --git a/fineract-provider/src/test/java/org/apache/fineract/cob/loan/FetchAndLockLoanStepDefinitions.java b/fineract-provider/src/test/java/org/apache/fineract/cob/loan/FetchAndLockLoanStepDefinitions.java index 9f893edff..3e82aef45 100644 --- a/fineract-provider/src/test/java/org/apache/fineract/cob/loan/FetchAndLockLoanStepDefinitions.java +++ b/fineract-provider/src/test/java/org/apache/fineract/cob/loan/FetchAndLockLoanStepDefinitions.java @@ -20,7 +20,9 @@ package org.apache.fineract.cob.loan; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.anyLong; +import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.Mockito.lenient; import static org.mockito.Mockito.mock; 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.StepExecution; import org.springframework.batch.repeat.RepeatStatus; +import org.springframework.jdbc.core.JdbcTemplate; public class FetchAndLockLoanStepDefinitions implements En { @@ -56,6 +59,7 @@ public class FetchAndLockLoanStepDefinitions implements En { private FetchAndLockLoanTasklet fetchAndLockLoanTasklet; private String action; private RepeatStatus result; + private JdbcTemplate jdbcTemplate = mock(JdbcTemplate.class); public FetchAndLockLoanStepDefinitions() { Given("/^The FetchAndLockLoanTasklet.execute method with action (.*)$/", (String action) -> { @@ -97,7 +101,8 @@ public class FetchAndLockLoanStepDefinitions implements En { contribution = new StepContribution(stepExecution); contribution.getStepExecution().getJobExecution().getExecutionContext().put(LoanCOBConstant.BUSINESS_DATE_PARAMETER_NAME, LocalDate.now(ZoneId.systemDefault()).toString()); - fetchAndLockLoanTasklet = new FetchAndLockLoanTasklet(loanAccountLockRepository, retrieveLoanIdService, fineractProperties); + fetchAndLockLoanTasklet = new FetchAndLockLoanTasklet(loanAccountLockRepository, retrieveLoanIdService, fineractProperties, + jdbcTemplate); }); When("FetchAndLockLoanTasklet.execute method executed", () -> { @@ -108,7 +113,7 @@ public class FetchAndLockLoanStepDefinitions implements En { if ("empty steps".equals(action)) { assertEquals(RepeatStatus.FINISHED, result); } 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(3, ((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)) .get(2)); } 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(3, ((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)) .get(2)); } 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(2, ((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)) .get(1)); } 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(2, ((List) contribution.getStepExecution().getJobExecution().getExecutionContext().get(LoanCOBConstant.LOAN_IDS))