얼쑤의 기초 학습에서 사용자는 단계를 끝낸 뒤 완료를 표시할 수 있습니다. 같은 단계를 다시 표시해도 처음 완료한 시각은 유지하기로 했습니다. 서버가 저장한 직후 응답이 끊겨 앱이 같은 요청을 다시 보낼 수 있고, 두 번 누른 요청이 거의 함께 도착할 수도 있기 때문입니다.
중복 완료는 시각 유지
완료 여부의 범위는 회원과 학습 단계의 조합입니다. DB의 유일 키가 그 조합을 하나로 제한합니다. 처음에는 회원, 단계, 완료 시각을 삽입합니다. 같은 조합이 이미 있으면 ON DUPLICATE KEY UPDATE에서 기존 ID를 자기 자신으로 대입해 값을 유지합니다. 서버가 각 요청을 다른 시각에 받더라도 저장된 시각은 한 번만 정해집니다.
중복 오류를 피하는 데 INSERT IGNORE를 쓸 수도 있었습니다. 하지만 그 명령은 중복 키 외의 삽입 오류도 경고로 낮출 수 있습니다. 완료를 이미 저장한 경우와 존재하지 않는 대상을 참조한 경우를 저장소에서 구분하려고, 중복 키에서만 값이 유지되는 분기를 선택했습니다.
저장된 최초 시각 반환
응답에 넣을 값은 이번 요청이 만든 시각보다 DB가 보존한 시각입니다. 처음 완료한 요청이 따로 있다면 그 요청의 값을 반환해야 합니다. 두 MySQL 세션으로 확인하니, 앞선 조회와 no-op 갱신 뒤 재조회를 하나의 REPEATABLE READ 트랜잭션으로 묶었을 때 경쟁 요청이 먼저 커밋한 행을 일반 조회로 볼 수 없는 경우가 있었습니다. 문장 단위로 커밋한 경우에는 재조회에서 상대가 저장한 최초 시각이 보였습니다. 이 API는 완료 표시를 한 문장으로 저장하고, 응답은 그 저장 뒤 별도로 조회하도록 뒀습니다.
실제 HTTP와 임시 MySQL을 연결한 검사에서는 완료를 다시 표시해도 최초 시각이 같았고, 동시에 표시한 경우에는 진도 행이 한 개였습니다. 재요청이 새 시각으로 덮어쓰도록 쿼리를 바꾸면 회귀 검사가 실패했습니다. 완료 해제는 별도 동작으로 두어 해당 행을 지웠습니다. 한 번의 완료 표시에서 무엇을 보존하는지와 언제 새로 시작할 수 있는지가 이 두 동작으로 구분됩니다.

댓글
댓글과 반응은 GitHub 계정으로 로그인해 남길 수 있어요.