FINERACT-1678: Improvements around locking
This commit is contained in:
+4
-1
@@ -32,6 +32,8 @@ 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.service.ThreadLocalContextUtil;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.context.annotation.Profile;
|
||||
import org.springframework.stereotype.Component;
|
||||
@@ -69,7 +71,8 @@ public class InternalLoanAccountLockApiResource implements InitializingBean {
|
||||
log.warn(" ");
|
||||
log.warn("------------------------------------------------------------");
|
||||
|
||||
LoanAccountLock loanAccountLock = new LoanAccountLock(loanId, LockOwner.valueOf(lockOwner));
|
||||
LoanAccountLock loanAccountLock = new LoanAccountLock(loanId, LockOwner.valueOf(lockOwner),
|
||||
ThreadLocalContextUtil.getBusinessDateByType(BusinessDateType.COB_DATE));
|
||||
loanAccountLockRepository.save(loanAccountLock);
|
||||
return Response.status(Response.Status.ACCEPTED).build();
|
||||
}
|
||||
|
||||
@@ -29,9 +29,7 @@ import javax.persistence.Table;
|
||||
import javax.persistence.Version;
|
||||
import lombok.Getter;
|
||||
import lombok.NoArgsConstructor;
|
||||
import org.apache.fineract.infrastructure.businessdate.domain.BusinessDateType;
|
||||
import org.apache.fineract.infrastructure.core.service.DateUtils;
|
||||
import org.apache.fineract.infrastructure.core.service.ThreadLocalContextUtil;
|
||||
|
||||
@Entity
|
||||
@Table(name = "m_loan_account_locks")
|
||||
@@ -63,11 +61,11 @@ public class LoanAccountLock {
|
||||
@Column(name = "lock_placed_on_cob_business_date")
|
||||
private LocalDate lockPlacedOnCobBusinessDate;
|
||||
|
||||
public LoanAccountLock(Long loanId, LockOwner lockOwner) {
|
||||
public LoanAccountLock(Long loanId, LockOwner lockOwner, LocalDate lockPlacedOnCobBusinessDate) {
|
||||
this.loanId = loanId;
|
||||
this.lockOwner = lockOwner;
|
||||
this.lockPlacedOn = DateUtils.getOffsetDateTimeOfTenant();
|
||||
this.lockPlacedOnCobBusinessDate = ThreadLocalContextUtil.getBusinessDateByType(BusinessDateType.COB_DATE);
|
||||
this.lockPlacedOnCobBusinessDate = lockPlacedOnCobBusinessDate;
|
||||
}
|
||||
|
||||
public void setError(String errorMessage, String stacktrace) {
|
||||
@@ -78,6 +76,5 @@ public class LoanAccountLock {
|
||||
public void setNewLockOwner(LockOwner newLockOwner) {
|
||||
this.lockOwner = newLockOwner;
|
||||
this.lockPlacedOn = DateUtils.getOffsetDateTimeOfTenant();
|
||||
this.lockPlacedOnCobBusinessDate = ThreadLocalContextUtil.getBusinessDateByType(BusinessDateType.COB_DATE);
|
||||
}
|
||||
}
|
||||
|
||||
-4
@@ -23,7 +23,6 @@ import lombok.AccessLevel;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.Setter;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.fineract.cob.exceptions.LoanAccountWasAlreadyLockedOrProcessed;
|
||||
import org.apache.fineract.cob.exceptions.LoanReadException;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.Loan;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanRepository;
|
||||
@@ -51,9 +50,6 @@ public abstract class AbstractLoanItemReader implements ItemReader<Loan> {
|
||||
try {
|
||||
if (remainingData.size() > 0) {
|
||||
loanId = remainingData.remove(0);
|
||||
if (alreadyLockedOrProcessedAccounts != null && alreadyLockedOrProcessedAccounts.remove(loanId)) {
|
||||
throw new LoanAccountWasAlreadyLockedOrProcessed(loanId);
|
||||
}
|
||||
return loanRepository.findById(loanId).orElseThrow(() -> new LoanNotFoundException(loanId));
|
||||
}
|
||||
} catch (Exception e) {
|
||||
|
||||
+18
-10
@@ -30,12 +30,14 @@ 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.core.config.FineractProperties;
|
||||
import org.apache.fineract.infrastructure.core.service.DateUtils;
|
||||
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.item.ExecutionContext;
|
||||
import org.springframework.batch.repeat.RepeatStatus;
|
||||
import org.springframework.jdbc.core.JdbcTemplate;
|
||||
|
||||
@Slf4j
|
||||
@RequiredArgsConstructor
|
||||
@@ -43,6 +45,7 @@ public class ApplyLoanLockTasklet implements Tasklet {
|
||||
|
||||
private final LoanAccountLockRepository accountLockRepository;
|
||||
private final FineractProperties fineractProperties;
|
||||
private final JdbcTemplate jdbcTemplate;
|
||||
|
||||
@Override
|
||||
public RepeatStatus execute(@NotNull StepContribution contribution, @NotNull ChunkContext chunkContext) throws Exception {
|
||||
@@ -61,23 +64,28 @@ public class ApplyLoanLockTasklet implements Tasklet {
|
||||
|
||||
List<Long> toBeProcessedLoanIds = new ArrayList<>(alreadySoftLockedAccountsMap.keySet());
|
||||
|
||||
for (Long loanId : toBeProcessedLoanIds) {
|
||||
upgradeToHardLock(loanId, alreadySoftLockedAccountsMap);
|
||||
}
|
||||
upgradeToHardLock(toBeProcessedLoanIds);
|
||||
|
||||
toBeProcessedLoanIds.addAll(alreadyLockedByChunkProcessingAccountIds);
|
||||
List<Long> alreadyLockedByInlineCOBOrProcessedLoanIds = new ArrayList<>(loanIds);
|
||||
alreadyLockedByInlineCOBOrProcessedLoanIds.removeAll(toBeProcessedLoanIds);
|
||||
|
||||
executionContext.put(LoanCOBConstant.ALREADY_LOCKED_BY_INLINE_COB_OR_PROCESSED_LOAN_IDS,
|
||||
new ArrayList<>(alreadyLockedByInlineCOBOrProcessedLoanIds));
|
||||
loanIds.removeAll(alreadyLockedByInlineCOBOrProcessedLoanIds);
|
||||
|
||||
return RepeatStatus.FINISHED;
|
||||
}
|
||||
|
||||
private void upgradeToHardLock(Long loanId, Map<Long, LoanAccountLock> alreadySoftLockedAccountsMap) {
|
||||
LoanAccountLock loanAccountLock = alreadySoftLockedAccountsMap.get(loanId);
|
||||
// Upgrade lock
|
||||
loanAccountLock.setNewLockOwner(LockOwner.LOAN_COB_CHUNK_PROCESSING);
|
||||
accountLockRepository.save(loanAccountLock);
|
||||
private void upgradeToHardLock(List<Long> accountsToLock) {
|
||||
jdbcTemplate.batchUpdate("""
|
||||
UPDATE m_loan_account_locks SET version= version + 1, lock_owner = ?, lock_placed_on = ? WHERE loan_id = ?
|
||||
""", accountsToLock, getInClauseParameterSizeLimit(), (ps, id) -> {
|
||||
ps.setString(1, LockOwner.LOAN_COB_CHUNK_PROCESSING.name());
|
||||
ps.setObject(2, DateUtils.getOffsetDateTimeOfTenant());
|
||||
ps.setLong(3, id);
|
||||
});
|
||||
}
|
||||
|
||||
private int getInClauseParameterSizeLimit() {
|
||||
return fineractProperties.getQuery().getInClauseParameterSizeLimit();
|
||||
}
|
||||
}
|
||||
|
||||
+6
-11
@@ -38,7 +38,6 @@ import org.springframework.batch.core.configuration.annotation.StepBuilderFactor
|
||||
import org.springframework.batch.core.configuration.annotation.StepScope;
|
||||
import org.springframework.batch.core.job.builder.FlowBuilder;
|
||||
import org.springframework.batch.core.job.flow.Flow;
|
||||
import org.springframework.batch.core.listener.ExecutionContextPromotionListener;
|
||||
import org.springframework.batch.integration.partition.RemotePartitioningWorkerStepBuilderFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
@@ -46,6 +45,7 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.integration.channel.QueueChannel;
|
||||
import org.springframework.jdbc.core.JdbcTemplate;
|
||||
import org.springframework.transaction.support.TransactionTemplate;
|
||||
|
||||
@Configuration
|
||||
@@ -78,6 +78,8 @@ public class LoanCOBWorkerConfiguration {
|
||||
|
||||
@Autowired
|
||||
private FineractProperties fineractProperties;
|
||||
@Autowired
|
||||
private JdbcTemplate jdbcTemplate;
|
||||
|
||||
@Bean(name = LoanCOBConstant.LOAN_COB_WORKER_STEP)
|
||||
public Step loanCOBWorkerStep() {
|
||||
@@ -103,13 +105,13 @@ public class LoanCOBWorkerConfiguration {
|
||||
.<Loan, Loan>chunk(propertyService.getChunkSize(JobName.LOAN_COB.name())).reader(cobWorkerItemReader())
|
||||
.processor(cobWorkerItemProcessor()).writer(cobWorkerItemWriter()).faultTolerant().skip(Exception.class)
|
||||
.skipLimit(propertyService.getChunkSize(JobName.LOAN_COB.name()) + 1).listener(loanItemListener())
|
||||
.listener(promotionListener()).listener(samplingStepExecutionListener()).build();
|
||||
.listener(samplingStepExecutionListener()).build();
|
||||
}
|
||||
|
||||
@Bean
|
||||
@StepScope
|
||||
public Step applyLockStep(@Value("#{stepExecutionContext['partition']}") String partitionName) {
|
||||
return localStepBuilderFactory.get("Apply lock - Step:" + partitionName).tasklet(applyLock()).listener(promotionListener()).build();
|
||||
return localStepBuilderFactory.get("Apply lock - Step:" + partitionName).tasklet(applyLock()).build();
|
||||
}
|
||||
|
||||
@Bean
|
||||
@@ -130,7 +132,7 @@ public class LoanCOBWorkerConfiguration {
|
||||
|
||||
@Bean
|
||||
public ApplyLoanLockTasklet applyLock() {
|
||||
return new ApplyLoanLockTasklet(accountLockRepository, fineractProperties);
|
||||
return new ApplyLoanLockTasklet(accountLockRepository, fineractProperties, jdbcTemplate);
|
||||
}
|
||||
|
||||
@Bean
|
||||
@@ -158,13 +160,6 @@ public class LoanCOBWorkerConfiguration {
|
||||
return repositoryItemWriter;
|
||||
}
|
||||
|
||||
@Bean
|
||||
public ExecutionContextPromotionListener promotionListener() {
|
||||
ExecutionContextPromotionListener listener = new ExecutionContextPromotionListener();
|
||||
listener.setKeys(new String[] { LoanCOBConstant.ALREADY_LOCKED_BY_INLINE_COB_OR_PROCESSED_LOAN_IDS });
|
||||
return listener;
|
||||
}
|
||||
|
||||
@Bean
|
||||
public SamplingStepExecutionListener samplingStepExecutionListener() {
|
||||
SamplingStepExecutionListener listener = new SamplingStepExecutionListener(samplingServiceFactory, samplingDataPrinter);
|
||||
|
||||
@@ -36,10 +36,7 @@ public class LoanItemReader extends AbstractLoanItemReader {
|
||||
@SuppressWarnings({ "unchecked" })
|
||||
public void beforeStep(@NotNull StepExecution stepExecution) {
|
||||
ExecutionContext executionContext = stepExecution.getExecutionContext();
|
||||
ExecutionContext jobExecutionContext = stepExecution.getJobExecution().getExecutionContext();
|
||||
List<Long> loanIds = (List<Long>) executionContext.get(LoanCOBConstant.LOAN_IDS);
|
||||
setAlreadyLockedOrProcessedAccounts(
|
||||
(List<Long>) jobExecutionContext.get(LoanCOBConstant.ALREADY_LOCKED_BY_INLINE_COB_OR_PROCESSED_LOAN_IDS));
|
||||
setRemainingData(new ArrayList<>(loanIds));
|
||||
}
|
||||
}
|
||||
|
||||
+5
-5
@@ -129,7 +129,7 @@ public class InlineLoanCOBExecutorServiceImpl implements InlineExecutorService<L
|
||||
}
|
||||
|
||||
private void execute(List<Long> loanIds, String jobName, LocalDate businessDate) {
|
||||
lockLoanAccounts(loanIds);
|
||||
lockLoanAccounts(loanIds, businessDate);
|
||||
Job inlineLoanCOBJob;
|
||||
try {
|
||||
inlineLoanCOBJob = jobLocator.getJob(jobName);
|
||||
@@ -167,7 +167,7 @@ public class InlineLoanCOBExecutorServiceImpl implements InlineExecutorService<L
|
||||
return loanIdAndLastClosedBusinessDates;
|
||||
}
|
||||
|
||||
private List<LoanAccountLock> getLoanAccountLocks(List<Long> loanIds) {
|
||||
private List<LoanAccountLock> getLoanAccountLocks(List<Long> loanIds, LocalDate businessDate) {
|
||||
List<LoanAccountLock> loanAccountLocks = new ArrayList<>();
|
||||
List<Long> alreadyLockedLoanIds = new ArrayList<>();
|
||||
loanIds.forEach(loanId -> {
|
||||
@@ -180,7 +180,7 @@ public class InlineLoanCOBExecutorServiceImpl implements InlineExecutorService<L
|
||||
alreadyLockedLoanIds.add(loanId);
|
||||
}
|
||||
} else {
|
||||
loanAccountLocks.add(new LoanAccountLock(loanId, LockOwner.LOAN_INLINE_COB_PROCESSING));
|
||||
loanAccountLocks.add(new LoanAccountLock(loanId, LockOwner.LOAN_INLINE_COB_PROCESSING, businessDate));
|
||||
}
|
||||
});
|
||||
if (!alreadyLockedLoanIds.isEmpty()) {
|
||||
@@ -207,13 +207,13 @@ public class InlineLoanCOBExecutorServiceImpl implements InlineExecutorService<L
|
||||
return jobParameterMap;
|
||||
}
|
||||
|
||||
private void lockLoanAccounts(List<Long> loanIds) {
|
||||
private void lockLoanAccounts(List<Long> loanIds, LocalDate businessDate) {
|
||||
transactionTemplate.setPropagationBehavior(PROPAGATION_REQUIRES_NEW);
|
||||
transactionTemplate.execute(new TransactionCallbackWithoutResult() {
|
||||
|
||||
@Override
|
||||
protected void doInTransactionWithoutResult(@NotNull TransactionStatus status) {
|
||||
List<LoanAccountLock> loanAccountLocks = getLoanAccountLocks(loanIds);
|
||||
List<LoanAccountLock> loanAccountLocks = getLoanAccountLocks(loanIds, businessDate);
|
||||
loanAccountLocks.forEach(loanAccountLock -> {
|
||||
loanAccountLock.setNewLockOwner(LockOwner.LOAN_INLINE_COB_PROCESSING);
|
||||
loanAccountLockRepository.save(loanAccountLock);
|
||||
|
||||
Reference in New Issue
Block a user