배경
현재 SubmissionService의 DOMjudge 폴링 로직에 대한 최적화를 계획 중이며,
개선 효과를 정량적으로 증명하기 위해 현재 상태의 베이스라인 데이터를 먼저 수집해야 합니다.
현재 코드에는 성능 측정 로직이 전혀 없으며, System.out.println으로 폴링 시도 횟수만 출력하고 있습니다.
목표
개선 전/후 비교를 위한 성능 데이터 수집 인프라 구축
수집 대상 지표
시간 지표
| 지표 |
설명 |
| E2E 응답 시간 |
프론트 요청 → 응답 수신까지 총 시간 |
| DOMjudge 제출 시간 |
submitCode() API 호출 소요 시간 |
| 채점 대기 시간 |
폴링 시작 → 결과 수신까지 시간 |
| DOMjudge API 응답 시간 |
개별 getResult() / getResultOutput() 호출 소요 시간 |
횟수/자원 지표
| 지표 |
설명 |
| 폴링 횟수 |
결과를 받기까지 DOMjudge에 보낸 요청 수 |
| 타임아웃 발생 횟수 |
제한 시간 내 결과를 못 받은 건수 |
| 에러 발생 횟수 |
DOMjudge 통신 실패 건수 |
분포 통계 (수집 후 SQL/Locust 분석)
- P50 / P90 / P95 / P99 응답 시간
- 언어별 채점 시간 분포
- 시간대별 제출량 및 응답 시간 변화
- 동시 사용자 수 증가에 따른 degradation 곡선
구현 작업
1. SubmissionMetric 테이블 생성 (별도 엔티티)
@Entity
@Table(name = "submission_metrics")
public class SubmissionMetric {
@Id @GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@OneToOne
@JoinColumn(name = "submission_id")
private Submission submission;
private String domjudgeSubmissionId;
private Long submitDurationMs; // DOMjudge 제출 API 소요시간
private Long judgingDurationMs; // 채점 대기 시간
private Long e2eDurationMs; // 전체 E2E 시간
private Integer pollingAttempts; // 폴링 시도 횟수
private String language; // 비정규화 (JOIN 없이 통계 가능)
private Long problemId; // 비정규화
private Long sectionId; // 비정규화
private Boolean timedOut; // 타임아웃 여부
private String errorMessage; // 에러 발생 시 메시지
private LocalDateTime measuredAt;
}
2. SubmissionService 구간별 시간 계측 로그 추가
3. Locust 부하 테스트 시나리오 작성
Locust 선정 이유
- gevent 코루틴 기반 → 30초 블로킹 폴링 요청을 경량 처리 (JMeter의 thread-per-user 대비 유리)
- 실시간 웹 UI (localhost:8089)로 RPS, 응답시간 분포, 에러율 그래프 즉시 확인
- Python 스크립트 → git 버전관리 용이, 복잡한 시나리오(JWT 갱신 등) 작성 자유로움
pip install locust 한 줄 설치, 100 VU 규모는 단일 머신으로 충분
분석 쿼리 예시
-- 언어별 응답시간 분포
SELECT
language,
COUNT(*) AS total,
AVG(e2e_duration_ms) AS avg_ms,
AVG(polling_attempts) AS avg_polls,
SUM(CASE WHEN timed_out = true THEN 1 ELSE 0 END) AS timeout_count
FROM submission_metrics
GROUP BY language;
-- 개선 전/후 비교 (개선 적용 후 동일 쿼리에 phase 컬럼 추가)
SELECT
CASE WHEN measured_at < '2026-03-20' THEN 'before' ELSE 'after' END AS phase,
language,
COUNT(*) AS total,
AVG(e2e_duration_ms) AS avg_ms,
AVG(polling_attempts) AS avg_polls
FROM submission_metrics
GROUP BY phase, language;
영향 범위
- 신규:
SubmissionMetric 엔티티, SubmissionMetricRepository, locustfile.py
- 수정:
SubmissionService.java — 측정 로직 추가 (비즈니스 로직 변경 없음)
비고
- 이 이슈는 측정 인프라만 구축하며, 폴링 최적화/SSE 도입은 별도 이슈로 진행
- 최소 1~2주간 데이터 수집 후, 수집된 베이스라인을 기준으로 최적화 효과 측정
- 참고 자료:
배경
현재
SubmissionService의 DOMjudge 폴링 로직에 대한 최적화를 계획 중이며,개선 효과를 정량적으로 증명하기 위해 현재 상태의 베이스라인 데이터를 먼저 수집해야 합니다.
현재 코드에는 성능 측정 로직이 전혀 없으며,
System.out.println으로 폴링 시도 횟수만 출력하고 있습니다.목표
개선 전/후 비교를 위한 성능 데이터 수집 인프라 구축
수집 대상 지표
시간 지표
submitCode()API 호출 소요 시간getResult()/getResultOutput()호출 소요 시간횟수/자원 지표
분포 통계 (수집 후 SQL/Locust 분석)
구현 작업
1. SubmissionMetric 테이블 생성 (별도 엔티티)
SubmissionMetric엔티티 생성 (submission_metrics테이블)SubmissionMetricRepository생성Submission)과 측정 로직을 분리하여, 측정 코드 제거 시 스키마 영향 없도록 함2. SubmissionService 구간별 시간 계측 로그 추가
submitAndGetResult()E2E 시간 측정domjudgeService.submitCode()제출 소요시간 측정pollForResult()/pollForResultOutput()채점 대기 시간 + 폴링 횟수 측정getResult()/getResultOutput()API 응답 시간 로그 (log.debug)System.out.println→log.warn/log.debug전환SubmissionMetricDB 저장[METRIC]접두사로 grep 가능한 구조화 로그 적용3. Locust 부하 테스트 시나리오 작성
pip install locust,locustfile.py)Locust 선정 이유
pip install locust한 줄 설치, 100 VU 규모는 단일 머신으로 충분분석 쿼리 예시
영향 범위
SubmissionMetric엔티티,SubmissionMetricRepository,locustfile.pySubmissionService.java— 측정 로직 추가 (비즈니스 로직 변경 없음)비고