실행흐름 개괄

  1. 클라이언트에서 작성한 코드에 대한 제출 요청을 API 서버로 보냅니다 (웹소켓)

  2. API 서버에서는 제출에 대한 정보를 DB에 기록합니다.

  3. API 서버에서는 Redis message queue로 채점 요청 정보를 삽입합니다.

  4. 채점 서버에서는 message queue에서 가장 먼저 삽입되었던 메세지를 pop하여, 해당 채점 요청 정보에 해당하는 코드를 실행할 준비를 합니다.

    {
      submissionId: number,
    	problemId: number, // 필요 없음. API 서버에서 주지 않아도 됨
      sessionId: number
    }
    
  5. 채점 서버에서는 도커 서버로 코드 실행 request를 보냅니다.

    1. 채점서버에서 위와 같이 코드 실행 준비를 마치게 되면, 채점 서버에서는 도커 서버로 코드 실행 요청을 보냅니다.
    2. 이때, timeout 값이 10초로 설정되어 있으므로, 최악의 경우 채점 서버는 도커 서버가 응답할 때까지 10초를 기다려야만 합니다. 이 경우에는 커넥션을 계속 유지해야 하므로, 오버헤드가 있을 수 있습니다. 이후 병목 현상이 생기는지 파악해본 뒤, 만약 병목 현상이 있으면 아래와 같이 구현을 바꿀 것을 고려하고 있습니다.
      1. 채점 서버에서는 도커 서버로 코드 실행 요청을 보냄
      2. 도커 서버는 응답 이후 제출 코드 실행
      3. 실행 완료 이후 도커 서버는 채점 서버로 완료 되었다는 request 발송
      4. 채점 서버가 완료 응답을 받음
  6. 도커 서버는 제출 코드를 실행합니다.

    1. 이때, timeout은 도커 서버에서 잽니다. 10초가 지나면 코드 실행 결과나 시간에 상관 없이 TIMEOUT 응답을 냅니다.
    2. https://stackoverflow.com/questions/30233302/promise-is-it-possible-to-force-cancel-a-promise 다만, TIMEOUT 응답이 나올 경우 위와 같이 프로미스가 취소되지 않아 리소스를 계속 점유하는 문제가 생길 수 있으므로, 채점 서버에서 도커 서버를 재실행해주어야 할 것입니다.
    3. 도커 서버는 제출 코드를 실행한 뒤, 다음과 같은 파일들을 생성하여 저장합니다. 디렉토리 위치는 제출 코드가 저장된 디렉토리 위치와 동일합니다.
      • 코드 실행 결과: <디렉토리>/<문제번호>.result
      • 출력 (console.log 등): <디렉토리>/<문제번호>.stdout
      • 오류 (console.error, node 에러 등): <디렉토리>/<문제번호>.stderr
      • (현재 구현 안됨) 소요 시간 (ms): <디렉토리>/<문제번호>.time
      • (현재 구현 안됨) 소요 메모리 (KB): <디렉토리>/<문제번호>.memory
  7. 채점 서버는 도커 서버의 응답을 바탕으로 채점을 시작합니다.

    1. 채점 서버는 도커 서버로부터 응답을 받습니다.
    2. 위 도커 서버가 쓴 파일들을 읽어 옵니다.
    3. DB로부터 테스트케이스 정답 파일을 읽어옵니다.
    4. 만약 <문제번호>.result 파일의 값과 테스트케이스 정답 파일의 값이 일치하면 정답 처리합니다.
  8. 채점 결과를 테스트 케이스 별로 API 서버에게 전송

  9. API 서버는 채점 결과를 이용해 DB 갱신 및 websocket을 통해 테스트 케이스별 결과 전송

채점서버

101.101.208.240:4000~

채점서버는 Redis 이벤트 큐에서 가져온 뒤, 도커 서버에게 코드 실행을 요청합니다. 코드 실행 결과를 통해 제출한 코드의 정답 여부를 판단한 뒤, API 서버에게 그 판단한 결과를 전송합니다.

채점서버는 Redis 이벤트 큐로부터 메세지를 스스로 받은 이후 작업들을 수행합니다. 그러므로, 외부에서 요청이 오지 않으므로 가지고 있는 API가 없습니다.

도커서버