dev #397
@ -110,8 +110,19 @@ public class JobLauncherController {
|
||||
|
||||
// return "Job " + jobName + " started";
|
||||
return resultMap;
|
||||
} catch (JobExecutionAlreadyRunningException e) {
|
||||
log.warn("Job {} 이 다른 인스턴스에서 이미 실행 중입니다.", jobName);
|
||||
resultMap.put("code", "ALREADY_RUNNING");
|
||||
resultMap.put("message", "Job " + jobName + " is already running on another instance.");
|
||||
return resultMap;
|
||||
} catch (Exception e) {
|
||||
logExceptionDetails("Manual launch failed", jobName, e);
|
||||
if (isDeadlockException(e)) {
|
||||
logDeadlockDetails(jobName, e);
|
||||
resultMap.put("code", "DEADLOCK");
|
||||
resultMap.put("message", "Deadlock detected while executing job " + jobName + ". Please retry.");
|
||||
return resultMap;
|
||||
}
|
||||
resultMap.put("code", "FAILED");
|
||||
resultMap.put("message", e.getMessage());
|
||||
return resultMap;
|
||||
@ -291,54 +302,29 @@ public class JobLauncherController {
|
||||
//
|
||||
// return jobName+ " executed successfully";
|
||||
|
||||
// 재시도 로직 추가
|
||||
int maxRetries = 3;
|
||||
int retryCount = 0;
|
||||
long initialDelay = 60000; // 1분
|
||||
JobParameters jobParameters = new JobParametersBuilder()
|
||||
.addString("jobName", jobName)
|
||||
.addDate("time", new Date())
|
||||
.toJobParameters();
|
||||
|
||||
while (retryCount < maxRetries) {
|
||||
JobParameters jobParameters = null;
|
||||
try {
|
||||
jobParameters = new JobParametersBuilder()
|
||||
.addString("jobName", jobName)
|
||||
.addDate("time", new Date())
|
||||
.addLong("run.id", System.currentTimeMillis())
|
||||
.toJobParameters();
|
||||
|
||||
log.info("Job {} 실행 시도 (재시도 {}/{})", jobName, retryCount + 1, maxRetries);
|
||||
JobExecution jobExecution = jobLauncher.run(job, jobParameters);
|
||||
log.info("Job {} started with execution id {}", jobName, jobExecution.getId());
|
||||
|
||||
log.info("Job {} 완료 상태: {}", jobName, jobExecution.getExitStatus().getExitCode());
|
||||
return jobName + " executed successfully";
|
||||
|
||||
} catch (Exception e) {
|
||||
retryCount++;
|
||||
logExceptionDetails("Scheduled launch failed", jobName, e);
|
||||
|
||||
// 데드락 오류인지 확인
|
||||
boolean isDeadlock = isDeadlockException(e);
|
||||
|
||||
if (isDeadlock && retryCount < maxRetries) {
|
||||
long delay = initialDelay * retryCount; // 1분, 2분, 3분 증가
|
||||
log.warn("Deadlock detected for job {}. retry {}/{} in {}ms. params={}",
|
||||
jobName, retryCount, maxRetries, delay, jobParameters);
|
||||
|
||||
try {
|
||||
Thread.sleep(delay);
|
||||
} catch (InterruptedException ie) {
|
||||
Thread.currentThread().interrupt();
|
||||
log.error("Job {} 재시도 중 인터럽트 발생", jobName, ie);
|
||||
return "Job " + jobName + " interrupted during retry";
|
||||
}
|
||||
} else {
|
||||
log.error("Job {} 실행 실패 (재시도 {}/{})", jobName, retryCount, maxRetries, e);
|
||||
return "Error executing job " + jobName + ": " + e.getMessage();
|
||||
}
|
||||
try {
|
||||
log.info("Job {} 실행 시작", jobName);
|
||||
JobExecution jobExecution = jobLauncher.run(job, jobParameters);
|
||||
log.info("Job {} completed with execution id {} and status {}",
|
||||
jobName, jobExecution.getId(), jobExecution.getExitStatus().getExitCode());
|
||||
return jobName + " executed successfully";
|
||||
} catch (JobExecutionAlreadyRunningException e) {
|
||||
log.warn("Job {} 이 다른 인스턴스에서 이미 실행 중입니다. 스킵합니다.", jobName);
|
||||
return "Job " + jobName + " is already running on another instance. Skipped.";
|
||||
} catch (Exception e) {
|
||||
logExceptionDetails("Scheduled launch failed", jobName, e);
|
||||
if (isDeadlockException(e)) {
|
||||
logDeadlockDetails(jobName, e);
|
||||
return "Deadlock detected while executing job " + jobName + ". Manual retry required.";
|
||||
}
|
||||
log.error("Job {} 실행 실패", jobName, e);
|
||||
return "Error executing job " + jobName + ": " + e.getMessage();
|
||||
}
|
||||
|
||||
return "Job " + jobName + " failed after " + maxRetries + " retries";
|
||||
}
|
||||
|
||||
/**
|
||||
@ -402,6 +388,38 @@ public class JobLauncherController {
|
||||
}
|
||||
}
|
||||
|
||||
private void logDeadlockDetails(String jobName, Exception e) {
|
||||
log.error("===== [DEADLOCK] Job: {} 데드락 상세 정보 =====", jobName);
|
||||
|
||||
Throwable current = e;
|
||||
int depth = 0;
|
||||
while (current != null && depth < 10) {
|
||||
log.error("[DEADLOCK] Exception chain [{}]: {} - {}",
|
||||
depth, current.getClass().getName(), current.getMessage());
|
||||
|
||||
if (current instanceof SQLException) {
|
||||
SQLException sqlEx = (SQLException) current;
|
||||
log.error("[DEADLOCK] SQLState: {}, ErrorCode: {}, Message: {}",
|
||||
sqlEx.getSQLState(), sqlEx.getErrorCode(), sqlEx.getMessage());
|
||||
|
||||
// SQLException 체인의 next exception도 확인
|
||||
SQLException nextEx = sqlEx.getNextException();
|
||||
int sqlDepth = 0;
|
||||
while (nextEx != null && sqlDepth < 10) {
|
||||
log.error("[DEADLOCK] SQL NextException [{}]: SQLState={}, ErrorCode={}, Message={}",
|
||||
sqlDepth, nextEx.getSQLState(), nextEx.getErrorCode(), nextEx.getMessage());
|
||||
nextEx = nextEx.getNextException();
|
||||
sqlDepth++;
|
||||
}
|
||||
}
|
||||
current = current.getCause();
|
||||
depth++;
|
||||
}
|
||||
|
||||
log.error("[DEADLOCK] Full stack trace:", e);
|
||||
log.error("===== [DEADLOCK] Job: {} 데드락 상세 정보 끝 =====", jobName);
|
||||
}
|
||||
|
||||
private boolean isDeadlockException(Throwable throwable) {
|
||||
Throwable root = throwable;
|
||||
while (root.getCause() != null) {
|
||||
@ -409,10 +427,16 @@ public class JobLauncherController {
|
||||
}
|
||||
if (root instanceof SQLException) {
|
||||
SQLException sqlEx = (SQLException) root;
|
||||
return "40001".equals(sqlEx.getSQLState()) || sqlEx.getErrorCode() == 1205;
|
||||
if ("40001".equals(sqlEx.getSQLState()) || sqlEx.getErrorCode() == 1205) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
String message = root.getMessage();
|
||||
return message != null && message.toLowerCase().contains("deadlock");
|
||||
if (message == null) return false;
|
||||
String lowerMessage = message.toLowerCase();
|
||||
return lowerMessage.contains("deadlock")
|
||||
|| message.contains("デッドロック")
|
||||
|| message.contains("교착 상태");
|
||||
}
|
||||
|
||||
private String buildSqlExceptionChain(SQLException sqlEx) {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user