Skip to content

Commit 60499a2

Browse files
authored
refactor : 예외 처리 강화 및 클래스 분리 (#106)
1 parent 50ea0af commit 60499a2

File tree

24 files changed

+422
-274
lines changed

24 files changed

+422
-274
lines changed

src/main/java/org/ezcode/codetest/application/submission/dto/event/payload/InitTestcaseListPayload.java

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
import java.util.List;
44
import java.util.stream.IntStream;
55

6-
import org.ezcode.codetest.domain.problem.model.ProblemInfo;
6+
import org.ezcode.codetest.application.submission.model.SubmissionContext;
77
import org.ezcode.codetest.domain.problem.model.entity.Testcase;
88

99
public record InitTestcaseListPayload(
@@ -17,10 +17,10 @@ public record InitTestcaseListPayload(
1717
String status
1818

1919
) {
20-
public static List<InitTestcaseListPayload> from(ProblemInfo info) {
21-
return IntStream.rangeClosed(1, info.getTestcaseCount())
20+
public static List<InitTestcaseListPayload> from(SubmissionContext ctx) {
21+
return IntStream.rangeClosed(1, ctx.getTestcaseCount())
2222
.mapToObj(seq -> {
23-
Testcase tc = info.testcaseList().get(seq - 1);
23+
Testcase tc = ctx.getTestcases().get(seq - 1);
2424
return new InitTestcaseListPayload(
2525
seq,
2626
tc.getInput(),

src/main/java/org/ezcode/codetest/application/submission/dto/event/payload/TestcaseResultPayload.java

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
package org.ezcode.codetest.application.submission.dto.event.payload;
22

33
import org.ezcode.codetest.application.submission.model.JudgeResult;
4-
import org.ezcode.codetest.domain.submission.dto.AnswerEvaluation;
54

65
public record TestcaseResultPayload(
76

@@ -18,10 +17,10 @@ public record TestcaseResultPayload(
1817
String message
1918

2019
) {
21-
public static TestcaseResultPayload fromEvaluation(int seqId, JudgeResult result, AnswerEvaluation evaluation) {
20+
public static TestcaseResultPayload fromEvaluation(int seqId, boolean isPassed, JudgeResult result) {
2221
return new TestcaseResultPayload(
2322
seqId,
24-
evaluation.isPassed(),
23+
isPassed,
2524
result.actualOutput(),
2625
result.executionTime(),
2726
result.memoryUsage(),

src/main/java/org/ezcode/codetest/application/submission/dto/request/compile/CodeCompileRequest.java

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,10 @@
11
package org.ezcode.codetest.application.submission.dto.request.compile;
22

3+
import java.nio.charset.StandardCharsets;
4+
import java.util.Base64;
5+
6+
import org.ezcode.codetest.application.submission.model.SubmissionContext;
7+
38
public record CodeCompileRequest(
49

510
String source_code,
@@ -9,4 +14,14 @@ public record CodeCompileRequest(
914
String stdin
1015

1116
) {
17+
public static CodeCompileRequest of(int seqId, SubmissionContext ctx) {
18+
return new CodeCompileRequest(
19+
ctx.getSourceCode(),
20+
ctx.getJudge0Id(),
21+
ctx.getInput(seqId));
22+
}
23+
24+
private static String encodeBase64(String str) {
25+
return Base64.getEncoder().encodeToString(str.getBytes(StandardCharsets.UTF_8));
26+
}
1227
}

src/main/java/org/ezcode/codetest/application/submission/dto/response/compile/ExecutionResultResponse.java

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,18 @@ public record ExecutionResultResponse(
1919
ExecutionStatus status
2020

2121
) {
22+
public static ExecutionResultResponse ofCompileError() {
23+
return new ExecutionResultResponse(
24+
null,
25+
0.0,
26+
0L,
27+
null,
28+
null,
29+
"Compilation Error",
30+
1,
31+
new ExecutionResultResponse.ExecutionStatus(6, "Compilation Error")
32+
);
33+
}
2234

2335
public long getMemory() {
2436
return this.memory == null ? 0L : memory;
Lines changed: 72 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,15 @@
11
package org.ezcode.codetest.application.submission.model;
22

33
import org.ezcode.codetest.application.submission.dto.event.payload.SubmissionFinalResultPayload;
4+
import org.ezcode.codetest.domain.language.model.entity.Language;
5+
import org.ezcode.codetest.domain.problem.model.ProblemInfo;
6+
import org.ezcode.codetest.domain.problem.model.entity.Problem;
7+
import org.ezcode.codetest.domain.problem.model.entity.Testcase;
48
import org.ezcode.codetest.domain.submission.model.SubmissionAggregator;
9+
import org.ezcode.codetest.domain.user.model.entity.User;
10+
import org.ezcode.codetest.infrastructure.event.dto.submission.SubmissionMessage;
511

12+
import java.util.List;
613
import java.util.concurrent.CountDownLatch;
714
import java.util.concurrent.atomic.AtomicBoolean;
815
import java.util.concurrent.atomic.AtomicInteger;
@@ -20,48 +27,102 @@ public record SubmissionContext(
2027

2128
CountDownLatch latch,
2229

23-
AtomicBoolean notified
30+
AtomicBoolean notified,
31+
32+
User user,
33+
34+
Language language,
35+
36+
ProblemInfo problemInfo,
37+
38+
SubmissionMessage msg
2439
) {
25-
public static SubmissionContext initialize(int totalTestcaseCount) {
40+
public static SubmissionContext initialize(
41+
User user, Language language, ProblemInfo problemInfo, SubmissionMessage msg
42+
) {
2643
return new SubmissionContext(
2744
new SubmissionAggregator(),
2845
new AtomicInteger(0),
2946
new AtomicInteger(0),
3047
new AtomicReference<>("Accepted"),
31-
new CountDownLatch(totalTestcaseCount),
32-
new AtomicBoolean(false)
48+
new CountDownLatch(problemInfo.getTestcaseCount()),
49+
new AtomicBoolean(false),
50+
user,
51+
language,
52+
problemInfo,
53+
msg
3354
);
3455
}
3556

36-
public SubmissionFinalResultPayload toFinalResult(int totalTestcaseCount) {
57+
public SubmissionFinalResultPayload toFinalResult() {
3758
return new SubmissionFinalResultPayload(
38-
totalTestcaseCount,
59+
this.getTestcaseCount(),
3960
this.getPassedCount(),
4061
this.getCurrentMessage()
4162
);
4263
}
4364

4465
public void incrementPassedCount() {
45-
this.passedCount.incrementAndGet();
66+
passedCount.incrementAndGet();
4667
}
4768

4869
public void incrementProcessedCount() {
49-
this.processedCount.incrementAndGet();
70+
processedCount.incrementAndGet();
5071
}
5172

5273
public int getPassedCount() {
53-
return this.passedCount.get();
74+
return passedCount.get();
5475
}
5576

5677
public String getCurrentMessage() {
57-
return this.message.get();
78+
return message.get();
5879
}
5980

6081
public void updateMessage(String message) {
6182
this.message.set(message);
6283
}
6384

6485
public void countDown() {
65-
this.latch.countDown();
86+
latch.countDown();
87+
}
88+
89+
public List<Testcase> getTestcases() {
90+
return problemInfo.testcaseList();
91+
}
92+
93+
public int getTestcaseCount() {
94+
return problemInfo.getTestcaseCount();
95+
}
96+
97+
public String getSourceCode() {
98+
return msg.sourceCode();
99+
}
100+
101+
public long getJudge0Id() {
102+
return language.getJudge0Id();
103+
}
104+
105+
public String getInput(int seqId) {
106+
return getTestcases().get(seqId - 1).getInput();
107+
}
108+
109+
public String getExpectedOutput(int seqId) {
110+
return getTestcases().get(seqId - 1).getOutput();
111+
}
112+
113+
public long getTimeLimit() {
114+
return problemInfo.getTimeLimit();
115+
}
116+
117+
public long getMemoryLimit() {
118+
return problemInfo.getMemoryLimit();
119+
}
120+
121+
public String getSessionKey() {
122+
return msg.sessionKey();
123+
}
124+
125+
public Problem getProblem() {
126+
return problemInfo.problem();
66127
}
67128
}

src/main/java/org/ezcode/codetest/application/submission/port/ExceptionNotifier.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,6 @@
22

33
public interface ExceptionNotifier {
44

5-
void sendEmbed(String title, String description, String exception, String methodName);
5+
void notifyException(String methodName, Throwable t);
66

77
}
Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
package org.ezcode.codetest.application.submission.service;
2+
3+
import java.util.concurrent.CompletableFuture;
4+
import java.util.concurrent.Executor;
5+
import java.util.concurrent.TimeUnit;
6+
import java.util.stream.IntStream;
7+
8+
import org.ezcode.codetest.application.submission.dto.event.SubmissionErrorEvent;
9+
import org.ezcode.codetest.application.submission.dto.event.SubmissionJudgingFinishedEvent;
10+
import org.ezcode.codetest.application.submission.dto.event.TestcaseEvaluatedEvent;
11+
import org.ezcode.codetest.application.submission.dto.event.TestcaseListInitializedEvent;
12+
import org.ezcode.codetest.application.submission.dto.event.payload.InitTestcaseListPayload;
13+
import org.ezcode.codetest.application.submission.dto.event.payload.TestcaseResultPayload;
14+
import org.ezcode.codetest.application.submission.dto.request.compile.CodeCompileRequest;
15+
import org.ezcode.codetest.application.submission.model.JudgeResult;
16+
import org.ezcode.codetest.application.submission.model.SubmissionContext;
17+
import org.ezcode.codetest.application.submission.port.ExceptionNotifier;
18+
import org.ezcode.codetest.application.submission.port.JudgeClient;
19+
import org.ezcode.codetest.application.submission.port.ProblemEventService;
20+
import org.ezcode.codetest.application.submission.port.SubmissionEventService;
21+
import org.ezcode.codetest.domain.submission.exception.SubmissionException;
22+
import org.ezcode.codetest.domain.submission.exception.code.SubmissionExceptionCode;
23+
import org.ezcode.codetest.domain.submission.model.SubmissionResult;
24+
import org.ezcode.codetest.domain.submission.model.TestcaseEvaluationInput;
25+
import org.ezcode.codetest.domain.submission.service.SubmissionDomainService;
26+
import org.springframework.stereotype.Service;
27+
import org.springframework.transaction.annotation.Transactional;
28+
29+
import lombok.RequiredArgsConstructor;
30+
import lombok.extern.slf4j.Slf4j;
31+
32+
@Slf4j
33+
@Service
34+
@RequiredArgsConstructor
35+
public class JudgementService {
36+
37+
private final SubmissionDomainService submissionDomainService;
38+
private final SubmissionEventService submissionEventService;
39+
private final ProblemEventService problemEventService;
40+
private final JudgeClient judgeClient;
41+
private final Executor judgeTestcaseExecutor;
42+
private final ExceptionNotifier exceptionNotifier;
43+
44+
public void publishInitTestcases(SubmissionContext ctx) {
45+
submissionEventService.publishInitTestcases(
46+
new TestcaseListInitializedEvent(ctx.getSessionKey(), InitTestcaseListPayload.from(ctx))
47+
);
48+
}
49+
50+
public void runTestcases(SubmissionContext ctx) throws InterruptedException {
51+
IntStream.rangeClosed(1, ctx.getTestcaseCount())
52+
.forEach(seqId -> runTestcaseAsync(seqId, ctx));
53+
54+
if (!ctx.latch().await(100, TimeUnit.SECONDS)) {
55+
throw new SubmissionException(SubmissionExceptionCode.TESTCASE_TIMEOUT);
56+
}
57+
}
58+
59+
@Transactional
60+
public void finalizeAndPublish(SubmissionContext ctx) {
61+
SubmissionResult submissionResult = submissionDomainService.finalizeSubmission(ctx);
62+
63+
publishFinalResult(ctx);
64+
publishProblemSolve(submissionResult);
65+
}
66+
67+
public void publishSubmissionError(String sessionKey, Exception e) {
68+
submissionEventService.publishSubmissionError(new SubmissionErrorEvent(sessionKey, e));
69+
}
70+
71+
private void runTestcaseAsync(int seqId, SubmissionContext ctx) {
72+
CompletableFuture.runAsync(() -> {
73+
try {
74+
log.info("[Judge RUN] Thread = {}", Thread.currentThread().getName());
75+
String token = judgeClient.submitAndGetToken(CodeCompileRequest.of(seqId, ctx));
76+
JudgeResult result = judgeClient.pollUntilDone(token);
77+
78+
TestcaseEvaluationInput input = TestcaseEvaluationInput.from(result, ctx, seqId);
79+
80+
boolean isPassed = submissionDomainService.handleEvaluationAndUpdateStats(input, ctx);
81+
82+
publishTestcaseUpdate(seqId, ctx, isPassed, result);
83+
} catch (Exception e) {
84+
if (ctx.notified().compareAndSet(false, true)) {
85+
publishSubmissionError(ctx.getSessionKey(), e);
86+
exceptionNotifier.notifyException("runTestcaseAsync", e);
87+
}
88+
} finally {
89+
ctx.countDown();
90+
}
91+
}, judgeTestcaseExecutor);
92+
}
93+
94+
private void publishTestcaseUpdate(int seqId, SubmissionContext ctx, boolean isPassed, JudgeResult result) {
95+
submissionEventService.publishTestcaseUpdate(new TestcaseEvaluatedEvent(
96+
ctx.getSessionKey(), TestcaseResultPayload.fromEvaluation(seqId, isPassed, result))
97+
);
98+
}
99+
100+
private void publishFinalResult(SubmissionContext ctx){
101+
submissionEventService.publishFinalResult(
102+
new SubmissionJudgingFinishedEvent(ctx.getSessionKey(), ctx.toFinalResult())
103+
);
104+
}
105+
106+
private void publishProblemSolve(SubmissionResult submissionResult) {
107+
problemEventService.publishProblemSolveEvent(submissionResult);
108+
}
109+
}

0 commit comments

Comments
 (0)