codedang
codedang copied to clipboard
feat(be): judge by testcase in iris
Description
Motivation
현재 iris는 한 문제에 대한 채점 요청이 들어오면 문제에 속한 모든 테스트 케이스에 대하여 일괄적으로 채점하고 그 결과를 반환합니다. 만약, 사용자가 테스트 케이스 별로 채점 요청을 보내고 그에 맞춰 iris가 테스트 케이스 별로 채점한다면 채점 상황을 표시 할 수 있는 등 사용자에게 더 큰 자유도를 부여할 수 있습니다.
Background
채점 과정
Iris가 채점 요청을 처리하는 과정은 다음과 같습니다.
- 요청 validation
- 채점을 위한 저장소(디렉토리) 생성
- 소스 파일 저장
- 테스트케이스 로딩 및 소스파일 컴파일 (Go Routine)
- 순차적으로 채점
- 저장소 삭제
Provide-Consume
Overview
Iris는 connector 패키지를 이용해 rabbitMQ(mq)에서 메세지를 가져오거나 게시합니다. connector의 factory.go에서 Provider 객체와 Consumer 객체, Connector객체를 생성합니다. main.go에서는 Connector의 Connect 메서드를 실행시킴으로써 작업을 시작하게 됩니다.
Connector
Connector는 Consumer를 통해 mq에서 채점 요청 메세지를 수신합니다. 수신한 메세지를 Router의 Route메서드에 넘겨 처리를 시키는데 이때 go routine을 사용하여 많은 요청을 동시에 처리하게 합니다. 최종적으로 Route 에게 반환받은 결과 메세지를 producer를 이용하여 mq에 전달하게 됩니다.
Connector는Consumer의Subscribe메서드를 통해 메세지를 수신할 수 있는 go 채널(ch-0)을 넘겨 받습니다.Consumer는 mq에서 메세지를 가져오고 이를 ch-0에 넘겨줍니다.for message := range messageCh { go c.handle(message, connectorCtx) }와 같이 작성하면 무한 반복문이 실행되는 것과 비슷한 효과를 냅니다.message에는 채널에 들어온 값들이 계속 들어가게 됩니다.
Router
Router은 메세지의 타입에 따라 메세지 핸들러를 호출하여 메세지를 처리합니다. Router의 Route는 Connector의 handle 메서드에 의해 호출되는데, handle메서드가 위 코드에서 관찰할 수 있듯이 go routine으로 처리되고 있기에 각각의 Route는 비동기적으로 처리되고 있습니다.
Route는 메세지의 타입을 확인하고 그에 맞는 핸들러를 호출합니다. 현재는 judge 메세지만 구현되어 있기에 judgeHandler의 Handle을 호출합니다. Handle의 결과를 반환 받아 자신을 호출한 Connector에게 반환합니다.
JudgeHandler
문제를 채점하는 서비스 계층입니다. 앞서 소개한 바와 같이 테스트 케이스들을 로딩하고 샌드박스에서 채점한 후 결과를 반환합니다.
캐시
현재 모든 테스트 케이스 정보는 s3 컨테이너에 보관되어 있습니다. Iris와는 분리되어 있기에 웹 요청을 통해 정보를 받아와야 합니다. 이러한 과정의 latency가 길기 때문에 iris는 한번 받아온 정보를 in-memory 캐시에 저장하고 재사용합니다. 이 캐시는 file-manager 계층에 구현되어 있습니다.
Implementation
과거에는 한 문제에 대한 요청이 오면 해당 문제에 대한 테스트 케이스를 모두 로딩하고 각 채점 결과를 수합하여 하나의 응답을 구성해 mq에 등록하였습니다. 요청 부분은 수정하지 않고 유지하였습니다. 문제 단위로 요청이 이루어집니다. 다만, 각 테스트 케이스들에 대하여 비동기적인 채점을 하도록 수정했습니다. 각 테스트 케이스별 채점이 마무리 되는 대로 테스트 케이스 단위의 응답이 가도록 수정했습니다.
Submission 모듈의 경우, 테스트케이스 단위로 submissionResult를 저장합니다. 이전에는 테스트케이스가 모두 하나의 응답으로 묶여 전달되었습니다. 따라서 테스트케이스 채점 결과 배열을 해체해 각각 submissionResult로 저장하게끔 작성되었습니다. 수정 후에는, 응답 하나당 테스트 케이스 한 개의 채점 결과가 전달되기 때문에 배열 처리하는 부분을 전부 수정하였습니다.
Submission은publish메서드를 통해서 mq에 채점 요청 메세지를 전달합니다. mq에 응답 메세지가 등록이 되면, 이를 가져와서validateJudgerResponse를 통해 validation을 수행하고,updateSubmissionResult를 통해submissionResult를 등록합니다. 이때, compile 에러와 같이 하나의 테스트 케이스에 연결되기 어려운 submissionResult들은 testcaseid 값을 null로 설정하게끔 수정했습니다.
이와 같이 수정했기 때문에 문제 단위로 정보를 저장하던 것을 테스트 케이스 단위로 저장하도록 수정할 필요가 없어졌습니다.
~~테스트 케이스별 채점을 지원하기 위한 구현 사항은 다음과 같습니다.~~
~~- 요청에 테스트케이스 아이디 포함.~~
~~- 테스트케이스를 1개만 로딩.~~
~~- 이미 컴파일한 실행파일을 재활용.~~
~~### 요청에 테스트 케이스 아이디 포함.~~
~~테스트 케이스 아이디를 선택적으로 포함할 수 있도록 수정하였습니다. 테스트 케이스 아이디가 요청에 포함되어 있으면, 요청된 테스트 케이스만 채점하고, 포함되어 있지 않은 경우 전체 테스트 케이스를 채점 하게 끔 수정했습니다.~~
~~### 테스트 케이스 개별 로딩.~~
~~Iris의 jude-handler는 getTestcase()를 사용하여 테스트 케이스를 로딩합니다. testcaseId 매개변수를 추가하여 해당하는 테스트 케이스만 로딩할 수 있도록 수정했습니다. 0으로 설정할 경우 기존과 마찬가지로 전체 테스트 케이스들을 로딩하도록 하였습니다.~~
~~### 실행 파일 재활용.~~
~~실행파일을 재활용하는 것은 컴파일 시간을 절약하는데 있어 큰 도움이 됩니다. 따라서 동일한 소스 파일에 대하여 미리 컴파일 해둔 실행 파일을 활용하도록 구현했습니다. 소스 파일 버전은 앞서 언급한 저장소를 기준으로 구분합니다. 저장소의 이름을 소스 파일마다 고유하게 지정하여 구분하도록 합니다. -구현 중 (checksum)-~~
Additional context
~~#1163 적용되면 최적화 가능.~~
Closes #878
Before submitting the PR, please make sure you do the following
- [x] Read the Contributing Guidelines
- [x] Read the Contributing Guidelines and follow the Commit Convention
- [x] Provide a description in this PR that addresses what the PR is solving, or reference the issue that it solves (e.g.
fixes #123). - [x] Ideally, include relevant tests that fail without this PR but pass with it.
The latest updates on your projects. Learn more about Vercel for Git ↗︎
| Name | Status | Preview | Updated (UTC) |
|---|---|---|---|
| codedang | ✅ Ready (Inspect) | Visit Preview | Jul 24, 2024 3:42am |
PR 제목 수정 부탁드려요!