접속오류 수정

This commit is contained in:
Macbook
2026-06-17 15:41:08 +09:00
parent edac7540cd
commit afd87b10c1
3 changed files with 112 additions and 89 deletions
@@ -12,20 +12,20 @@ import org.springframework.stereotype.Component;
public class StrategyEvaluationAiVerifyJobRunner { public class StrategyEvaluationAiVerifyJobRunner {
private final StrategyEvaluationLlmService llmService; private final StrategyEvaluationLlmService llmService;
private final StrategyEvaluationAiVerifyJobService jobService; private final StrategyEvaluationAiVerifyJobStore jobStore;
@Async("aiVerifyExecutor") @Async("aiVerifyExecutor")
public void execute(String jobId, JsonNode context, Long userId, String deviceId) { public void execute(String jobId, JsonNode context, Long userId, String deviceId) {
jobService.markRunning(jobId); jobStore.markRunning(jobId);
try { try {
var result = llmService.verify(context, userId, deviceId); var result = llmService.verify(context, userId, deviceId);
jobService.markCompleted(jobId, result); jobStore.markCompleted(jobId, result);
} catch (Exception e) { } catch (Exception e) {
log.warn("[AiVerifyJob] fail jobId={}: {}", jobId, e.getMessage()); log.warn("[AiVerifyJob] fail jobId={}: {}", jobId, e.getMessage());
String msg = e.getMessage() != null && !e.getMessage().isBlank() String msg = e.getMessage() != null && !e.getMessage().isBlank()
? e.getMessage() ? e.getMessage()
: "AI 검증 중 오류가 발생했습니다."; : "AI 검증 중 오류가 발생했습니다.";
jobService.markFailed(jobId, msg); jobStore.markFailed(jobId, msg);
} }
} }
} }
@@ -3,110 +3,29 @@ package com.goldenchart.service;
import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.JsonNode;
import com.goldenchart.dto.StrategyEvaluationAiVerifyJobStartResponse; import com.goldenchart.dto.StrategyEvaluationAiVerifyJobStartResponse;
import com.goldenchart.dto.StrategyEvaluationAiVerifyJobStatusResponse; import com.goldenchart.dto.StrategyEvaluationAiVerifyJobStatusResponse;
import com.goldenchart.dto.StrategyEvaluationAiVerifyResponse;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import java.util.Iterator;
import java.util.Map;
import java.util.UUID; import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
@Service @Service
@RequiredArgsConstructor @RequiredArgsConstructor
public class StrategyEvaluationAiVerifyJobService { public class StrategyEvaluationAiVerifyJobService {
private static final long JOB_TTL_MS = 60L * 60 * 1000;
private final StrategyEvaluationAiVerifyJobRunner jobRunner; private final StrategyEvaluationAiVerifyJobRunner jobRunner;
private final StrategyEvaluationAiVerifyJobStore jobStore;
private final ConcurrentHashMap<String, JobRecord> jobs = new ConcurrentHashMap<>();
public StrategyEvaluationAiVerifyJobStartResponse startJob(JsonNode context, Long userId, String deviceId) { public StrategyEvaluationAiVerifyJobStartResponse startJob(JsonNode context, Long userId, String deviceId) {
purgeExpired();
String jobId = UUID.randomUUID().toString(); String jobId = UUID.randomUUID().toString();
JobRecord record = new JobRecord(jobId, userId, System.currentTimeMillis()); jobStore.register(jobId, userId);
jobs.put(jobId, record);
jobRunner.execute(jobId, context, userId, deviceId); jobRunner.execute(jobId, context, userId, deviceId);
return StrategyEvaluationAiVerifyJobStartResponse.builder() return StrategyEvaluationAiVerifyJobStartResponse.builder()
.jobId(jobId) .jobId(jobId)
.status(record.status) .status(jobStore.initialStatus(jobId))
.build(); .build();
} }
public StrategyEvaluationAiVerifyJobStatusResponse getJob(String jobId, Long userId) { public StrategyEvaluationAiVerifyJobStatusResponse getJob(String jobId, Long userId) {
purgeExpired(); return jobStore.getJob(jobId, userId);
JobRecord record = jobs.get(jobId);
if (record == null || !record.userId.equals(userId)) {
return null;
}
return toResponse(record);
}
void markRunning(String jobId) {
JobRecord record = jobs.get(jobId);
if (record != null) {
record.status = "RUNNING";
}
}
void markCompleted(String jobId, StrategyEvaluationAiVerifyResponse result) {
JobRecord record = jobs.get(jobId);
if (record != null) {
record.status = "COMPLETED";
record.result = result;
record.error = null;
record.completedAtMs = System.currentTimeMillis();
}
}
void markFailed(String jobId, String error) {
JobRecord record = jobs.get(jobId);
if (record != null) {
record.status = "FAILED";
record.error = error;
record.completedAtMs = System.currentTimeMillis();
}
}
private static StrategyEvaluationAiVerifyJobStatusResponse toResponse(JobRecord record) {
return StrategyEvaluationAiVerifyJobStatusResponse.builder()
.jobId(record.jobId)
.status(record.status)
.error(record.error)
.result(record.result)
.createdAtMs(record.createdAtMs)
.completedAtMs(record.completedAtMs)
.build();
}
private void purgeExpired() {
long cutoff = System.currentTimeMillis() - JOB_TTL_MS;
Iterator<Map.Entry<String, JobRecord>> it = jobs.entrySet().iterator();
while (it.hasNext()) {
Map.Entry<String, JobRecord> e = it.next();
long ts = e.getValue().completedAtMs != null
? e.getValue().completedAtMs
: e.getValue().createdAtMs;
if (ts < cutoff) {
it.remove();
}
}
}
private static final class JobRecord {
private final String jobId;
private final Long userId;
private final long createdAtMs;
private String status = "QUEUED";
private String error;
private StrategyEvaluationAiVerifyResponse result;
private Long completedAtMs;
private JobRecord(String jobId, Long userId, long createdAtMs) {
this.jobId = jobId;
this.userId = userId;
this.createdAtMs = createdAtMs;
}
} }
} }
@@ -0,0 +1,104 @@
package com.goldenchart.service;
import com.goldenchart.dto.StrategyEvaluationAiVerifyJobStatusResponse;
import com.goldenchart.dto.StrategyEvaluationAiVerifyResponse;
import org.springframework.stereotype.Component;
import java.util.Iterator;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
/** AI 검증 job 상태 저장 — Service·Runner 간 순환 참조 방지 */
@Component
public class StrategyEvaluationAiVerifyJobStore {
private static final long JOB_TTL_MS = 60L * 60 * 1000;
private final ConcurrentHashMap<String, JobRecord> jobs = new ConcurrentHashMap<>();
public void register(String jobId, Long userId) {
purgeExpired();
jobs.put(jobId, new JobRecord(jobId, userId, System.currentTimeMillis()));
}
public StrategyEvaluationAiVerifyJobStatusResponse getJob(String jobId, Long userId) {
purgeExpired();
JobRecord record = jobs.get(jobId);
if (record == null || !record.userId.equals(userId)) {
return null;
}
return toResponse(record);
}
public String initialStatus(String jobId) {
JobRecord record = jobs.get(jobId);
return record != null ? record.status : "QUEUED";
}
public void markRunning(String jobId) {
JobRecord record = jobs.get(jobId);
if (record != null) {
record.status = "RUNNING";
}
}
public void markCompleted(String jobId, StrategyEvaluationAiVerifyResponse result) {
JobRecord record = jobs.get(jobId);
if (record != null) {
record.status = "COMPLETED";
record.result = result;
record.error = null;
record.completedAtMs = System.currentTimeMillis();
}
}
public void markFailed(String jobId, String error) {
JobRecord record = jobs.get(jobId);
if (record != null) {
record.status = "FAILED";
record.error = error;
record.completedAtMs = System.currentTimeMillis();
}
}
private static StrategyEvaluationAiVerifyJobStatusResponse toResponse(JobRecord record) {
return StrategyEvaluationAiVerifyJobStatusResponse.builder()
.jobId(record.jobId)
.status(record.status)
.error(record.error)
.result(record.result)
.createdAtMs(record.createdAtMs)
.completedAtMs(record.completedAtMs)
.build();
}
private void purgeExpired() {
long cutoff = System.currentTimeMillis() - JOB_TTL_MS;
Iterator<Map.Entry<String, JobRecord>> it = jobs.entrySet().iterator();
while (it.hasNext()) {
Map.Entry<String, JobRecord> e = it.next();
long ts = e.getValue().completedAtMs != null
? e.getValue().completedAtMs
: e.getValue().createdAtMs;
if (ts < cutoff) {
it.remove();
}
}
}
private static final class JobRecord {
private final String jobId;
private final Long userId;
private final long createdAtMs;
private String status = "QUEUED";
private String error;
private StrategyEvaluationAiVerifyResponse result;
private Long completedAtMs;
private JobRecord(String jobId, Long userId, long createdAtMs) {
this.jobId = jobId;
this.userId = userId;
this.createdAtMs = createdAtMs;
}
}
}