From 725de4d86b2604ca11f4e3d3f8538955371f53c7 Mon Sep 17 00:00:00 2001 From: ysCha Date: Wed, 11 Feb 2026 13:10:13 +0900 Subject: [PATCH] =?UTF-8?q?=EB=B0=B0=EC=B9=98=EB=8D=B0=EB=93=9C=EB=9D=BD?= =?UTF-8?q?=EB=A1=9C=EA=B7=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../qcast/batch/JobLauncherController.java | 47 +++++++++++++++---- 1 file changed, 39 insertions(+), 8 deletions(-) diff --git a/src/main/java/com/interplug/qcast/batch/JobLauncherController.java b/src/main/java/com/interplug/qcast/batch/JobLauncherController.java index 483decf8..8250c362 100644 --- a/src/main/java/com/interplug/qcast/batch/JobLauncherController.java +++ b/src/main/java/com/interplug/qcast/batch/JobLauncherController.java @@ -297,8 +297,9 @@ public class JobLauncherController { long initialDelay = 60000; // 1분 while (retryCount < maxRetries) { + JobParameters jobParameters = null; try { - JobParameters jobParameters = new JobParametersBuilder() + jobParameters = new JobParametersBuilder() .addString("jobName", jobName) .addDate("time", new Date()) .addLong("run.id", System.currentTimeMillis()) @@ -316,14 +317,12 @@ public class JobLauncherController { logExceptionDetails("Scheduled launch failed", jobName, e); // 데드락 오류인지 확인 - boolean isDeadlock = e.getCause() != null && - e.getCause().getMessage() != null && - e.getCause().getMessage().toLowerCase().contains("deadlock"); + boolean isDeadlock = isDeadlockException(e); if (isDeadlock && retryCount < maxRetries) { long delay = initialDelay * retryCount; // 1분, 2분, 3분 증가 - log.warn("Job {} 실행 중 데드락 발생. {}/{} 재시도 ({}ms 후 재시도)...", - jobName, retryCount, maxRetries, delay, e); + log.warn("Deadlock detected for job {}. retry {}/{} in {}ms. params={}", + jobName, retryCount, maxRetries, delay, jobParameters); try { Thread.sleep(delay); @@ -395,10 +394,42 @@ public class JobLauncherController { } if (root instanceof SQLException) { SQLException sqlEx = (SQLException) root; - log.error("{} for job {}: SQLState={}, errorCode={}, message={}", - context, jobName, sqlEx.getSQLState(), sqlEx.getErrorCode(), sqlEx.getMessage(), e); + log.error("{} for job {}: SQLState={}, errorCode={}, message={}, sqlChain={}", + context, jobName, sqlEx.getSQLState(), sqlEx.getErrorCode(), sqlEx.getMessage(), + buildSqlExceptionChain(sqlEx), e); } else { log.error("{} for job {}: {}", context, jobName, root.getMessage(), e); } } + + private boolean isDeadlockException(Throwable throwable) { + Throwable root = throwable; + while (root.getCause() != null) { + root = root.getCause(); + } + if (root instanceof SQLException) { + SQLException sqlEx = (SQLException) root; + return "40001".equals(sqlEx.getSQLState()) || sqlEx.getErrorCode() == 1205; + } + String message = root.getMessage(); + return message != null && message.toLowerCase().contains("deadlock"); + } + + private String buildSqlExceptionChain(SQLException sqlEx) { + StringBuilder sb = new StringBuilder(); + SQLException current = sqlEx; + int depth = 0; + while (current != null && depth < 10) { + if (depth > 0) { + sb.append(" | "); + } + sb.append("[").append(current.getClass().getSimpleName()) + .append(" sqlState=").append(current.getSQLState()) + .append(" errorCode=").append(current.getErrorCode()) + .append(" message=").append(current.getMessage()).append("]"); + current = current.getNextException(); + depth++; + } + return sb.toString(); + } }