[FINERACT-1937] refactor isCatchUpRunning SQL

This commit is contained in:
taskain7
2023-06-09 09:27:50 +02:00
committed by Adam Saghy
parent 22a3ca4fd8
commit 244febbb2c
2 changed files with 80 additions and 50 deletions
@@ -20,7 +20,6 @@ package org.apache.fineract.cob.service;
import com.google.gson.Gson;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.List;
import lombok.RequiredArgsConstructor;
import org.apache.fineract.cob.data.IsCatchUpRunningDTO;
@@ -34,7 +33,6 @@ import org.apache.fineract.infrastructure.core.serialization.GoogleGsonSerialize
import org.apache.fineract.infrastructure.core.service.ThreadLocalContextUtil;
import org.apache.fineract.infrastructure.jobs.domain.CustomJobParameterRepository;
import org.apache.fineract.infrastructure.jobs.domain.JobExecutionRepository;
import org.springframework.batch.core.JobExecution;
import org.springframework.batch.core.explore.JobExplorer;
import org.springframework.stereotype.Service;
@@ -77,15 +75,9 @@ public class LoanCOBCatchUpServiceImpl implements LoanCOBCatchUpService {
@Override
public IsCatchUpRunningDTO isCatchUpRunning() {
List<Long> runningCatchUpExecutionIds = jobExecutionRepository.getRunningJobsIdsByExecutionParameter(LoanCOBConstant.JOB_NAME,
LoanCOBConstant.LOAN_COB_CUSTOM_JOB_PARAMETER_KEY, LoanCOBConstant.IS_CATCH_UP_PARAMETER_NAME, "true");
return runningCatchUpExecutionIds //
.stream() //
.findFirst() //
.map(jobExplorer::getJobExecution) //
.map(JobExecution::getExecutionContext) //
.map(executionContext -> executionContext.getString(LoanCOBConstant.BUSINESS_DATE_PARAMETER_NAME)) //
.map(result -> new IsCatchUpRunningDTO(true, LocalDate.parse(result, DateTimeFormatter.ISO_DATE))) //
.orElse(new IsCatchUpRunningDTO(false, null));
LocalDate runningCatchUpBusinessDate = jobExecutionRepository.getBusinessDateOfRunningJobByExecutionParameter(
LoanCOBConstant.JOB_NAME, LoanCOBConstant.LOAN_COB_CUSTOM_JOB_PARAMETER_KEY, LoanCOBConstant.IS_CATCH_UP_PARAMETER_NAME,
"true", LoanCOBConstant.BUSINESS_DATE_PARAMETER_NAME);
return new IsCatchUpRunningDTO(runningCatchUpBusinessDate != null, runningCatchUpBusinessDate);
}
}
@@ -24,34 +24,23 @@ import static org.springframework.batch.core.BatchStatus.STARTED;
import static org.springframework.batch.core.BatchStatus.STARTING;
import static org.springframework.batch.core.BatchStatus.UNKNOWN;
import com.google.gson.Gson;
import java.time.LocalDate;
import java.util.List;
import java.util.Map;
import lombok.RequiredArgsConstructor;
import org.apache.fineract.infrastructure.core.config.FineractProperties;
import org.apache.fineract.infrastructure.core.serialization.GoogleGsonSerializerHelper;
import org.apache.fineract.infrastructure.core.service.database.DatabaseSpecificSQLGenerator;
import org.apache.fineract.infrastructure.core.service.database.DatabaseTypeResolver;
import org.apache.fineract.infrastructure.jobs.data.JobParameterDTO;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.dao.EmptyResultDataAccessException;
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;
import org.springframework.stereotype.Component;
@Component
@RequiredArgsConstructor
public class JobExecutionRepository implements InitializingBean {
public class JobExecutionRepository {
private final NamedParameterJdbcTemplate namedParameterJdbcTemplate;
private final FineractProperties fineractProperties;
private final DatabaseTypeResolver databaseTypeResolver;
private final DatabaseSpecificSQLGenerator sqlGenerator;
private final GoogleGsonSerializerHelper gsonFactory;
private Gson gson;
@Override
public void afterPropertiesSet() throws Exception {
this.gson = gsonFactory.createSimpleGson();
}
public List<String> getStuckJobNames(NamedParameterJdbcTemplate jdbcTemplate) {
int threshold = fineractProperties.getJob().getStuckRetryThreshold();
@@ -165,30 +154,79 @@ public class JobExecutionRepository implements InitializingBean {
""", Map.of("status", FAILED.name(), "jobExecutionId", stuckJobId));
}
public List<Long> getRunningJobsIdsByExecutionParameter(String jobName, String jobCustomParamKeyName, String parameterKeyName,
String parameterValue) {
final StringBuilder sqlStatementBuilder = new StringBuilder();
String jsonString = gson.toJson(new JobParameterDTO(parameterKeyName, parameterValue));
sqlStatementBuilder.append(
"SELECT bje.JOB_EXECUTION_ID FROM BATCH_JOB_INSTANCE bji INNER JOIN BATCH_JOB_EXECUTION bje ON bji.JOB_INSTANCE_ID = bje.JOB_INSTANCE_ID INNER JOIN BATCH_JOB_EXECUTION_PARAMS bjep ON bje.JOB_EXECUTION_ID = bjep.JOB_EXECUTION_ID"
+ " WHERE bje.STATUS IN (:statuses) AND bji.JOB_NAME = :jobName AND bjep.PARAMETER_NAME = :jobCustomParamKeyName AND "
+ sqlGenerator.castInteger("bjep.PARAMETER_VALUE") + " IN (" + getSubQueryForCustomJobParameters()
+ ") AND bje.JOB_INSTANCE_ID NOT IN (SELECT bje.JOB_INSTANCE_ID FROM BATCH_JOB_INSTANCE bji INNER JOIN BATCH_JOB_EXECUTION bje ON bji.JOB_INSTANCE_ID = bje.JOB_INSTANCE_ID"
+ " WHERE bje.STATUS = :completedStatus AND bji.JOB_NAME = :jobName)");
return namedParameterJdbcTemplate.queryForList(
sqlStatementBuilder.toString(), Map.of("statuses", List.of(STARTED.name(), STARTING.name()), "jobName", jobName,
"completedStatus", COMPLETED.name(), "jobCustomParamKeyName", jobCustomParamKeyName, "jsonString", jsonString),
Long.class);
}
private String getSubQueryForCustomJobParameters() {
if (databaseTypeResolver.isMySQL()) {
return "SELECT cjp.id FROM batch_custom_job_parameters cjp WHERE JSON_CONTAINS(cjp.parameter_json,:jsonString)";
} else if (databaseTypeResolver.isPostgreSQL()) {
return "SELECT cjp.id FROM (SELECT id,json_array_elements(parameter_json) AS json_data FROM batch_custom_job_parameters) AS cjp WHERE (cjp.json_data ::jsonb @> :jsonString ::jsonb)";
} else {
throw new IllegalStateException("Database type is not supported for json query " + databaseTypeResolver.databaseType());
public LocalDate getBusinessDateOfRunningJobByExecutionParameter(String jobName, String jobCustomParamKeyName, String parameterKeyName,
String parameterValue, String dateParameterName) {
try {
if (databaseTypeResolver.isPostgreSQL()) {
return namedParameterJdbcTemplate.queryForObject("""
SELECT
J2->>'parameterValue'
FROM
BATCH_JOB_INSTANCE BJI
INNER JOIN BATCH_JOB_EXECUTION BJE ON BJI.JOB_INSTANCE_ID = BJE.JOB_INSTANCE_ID
INNER JOIN BATCH_JOB_EXECUTION_PARAMS BJEP ON BJE.JOB_EXECUTION_ID = BJEP.JOB_EXECUTION_ID
inner join batch_custom_job_parameters CJP ON cast(BJEP.parameter_value as bigint) = CJP.id
AND BJEP.parameter_name = :jobCustomParamKeyName
CROSS JOIN LATERAL json_array_elements(CJP.parameter_json) J
CROSS JOIN LATERAL json_array_elements(CJP.parameter_json) J2
WHERE
J ->> 'parameterName' = :filterParameterName
AND J ->> 'parameterValue' = :filterParameterValue
AND J2 ->> 'parameterName' = :dateParameterName
AND BJE.STATUS IN (:statuses)
AND BJI.JOB_NAME = :jobName
AND BJE.JOB_INSTANCE_ID NOT IN (
SELECT
IBJE.JOB_INSTANCE_ID
FROM
BATCH_JOB_INSTANCE IBJI
INNER JOIN BATCH_JOB_EXECUTION IBJE ON IBJI.JOB_INSTANCE_ID = IBJE.JOB_INSTANCE_ID
WHERE
IBJE.STATUS = :completedStatus
AND IBJI.JOB_NAME = :jobName)
""",
Map.of("jobCustomParamKeyName", jobCustomParamKeyName, "filterParameterName", parameterKeyName,
"filterParameterValue", parameterValue, "statuses", List.of(STARTED.name(), STARTING.name()),
"completedStatus", COMPLETED.name(), "jobName", jobName, "dateParameterName", dateParameterName),
LocalDate.class);
} else if (databaseTypeResolver.isMySQL()) {
return namedParameterJdbcTemplate.queryForObject(
"""
SELECT
J2.parameter_value
FROM
BATCH_JOB_INSTANCE BJI
INNER JOIN BATCH_JOB_EXECUTION BJE ON BJI.JOB_INSTANCE_ID = BJE.JOB_INSTANCE_ID
INNER JOIN BATCH_JOB_EXECUTION_PARAMS BJEP ON BJE.JOB_EXECUTION_ID = BJEP.JOB_EXECUTION_ID
inner join batch_custom_job_parameters CJP ON BJEP.parameter_value = CJP.id
AND BJEP.parameter_name = :jobCustomParamKeyName
CROSS JOIN json_table(CJP.parameter_json, '$[*]' COLUMNS(parameter_name VARCHAR(100) PATH "$.parameterName", parameter_value VARCHAR(100) PATH "$.parameterValue")) J
CROSS JOIN json_table(CJP.parameter_json, '$[*]' COLUMNS(parameter_name VARCHAR(100) PATH "$.parameterName", parameter_value VARCHAR(100) PATH "$.parameterValue")) J2
WHERE
J.parameter_name = :filterParameterName
AND J.parameter_value = :filterParameterValue
AND J2.parameter_name = :dateParameterName
AND BJE.STATUS IN (:statuses)
AND BJI.JOB_NAME = :jobName
AND BJE.JOB_INSTANCE_ID NOT IN (
SELECT
IBJE.JOB_INSTANCE_ID
FROM
BATCH_JOB_INSTANCE IBJI
INNER JOIN BATCH_JOB_EXECUTION IBJE ON IBJI.JOB_INSTANCE_ID = IBJE.JOB_INSTANCE_ID
WHERE
IBJE.STATUS = :completedStatus
AND IBJI.JOB_NAME = :jobName);
""",
Map.of("jobCustomParamKeyName", jobCustomParamKeyName, "filterParameterName", parameterKeyName,
"filterParameterValue", parameterValue, "dateParameterName", dateParameterName, "statuses",
List.of(STARTED.name(), STARTING.name()), "jobName", jobName, "completedStatus", COMPLETED.name()),
LocalDate.class);
} else {
throw new IllegalStateException("Database type is not supported for json query " + databaseTypeResolver.databaseType());
}
} catch (EmptyResultDataAccessException e) {
return null;
}
}
}