앞날 창창보
article thumbnail

나는 부마위키라는 부산소프트웨어마이스터고등학교의 위키 서비스를 개발하고 있다. 2021년  부마위키에서 느껴질 정도로 문제가 됐던 동시성 이슈가 있다. 

 

자주 발생하는 동시성 이슈

동시성 이슈라고 하기엔 동시에 일어나지 않아도 되서 동시성 이슈라고 불러도 되는지 모르겠다 ㅋㅋ

 

문서 수정자가 문서 수정을 요청하면, DB에 저장된 현재 문서 내용을 전달하고 수정 후 내용을 저장하는 형태이다. 문서 수정자 1, 2가 같은 버전의 문서에서 수정을 시작후, 문서 수정자 1이 수정 내용을 저장하고, 문서 수정자 2가 수정 내용을 저장한다면, 문서 수정자 1이 추가 혹은 삭제한 내용을 사라지게 된다. 

 

자신이 수정한 것을 문서 내역에서 다시 볼 수 있긴하지만, 결국 사용자의 행동을 늘리는 것이기 때문에 개발을 추가로 해야겠다고 생각했다.

 

나무위키는 어떻게 하고 있을까?


친구와 같이 나무위키의 문서에 테스트를 해보았다. 그러니 아래 사진과 같은 응답이 돌아왔다. 편집 도중에 다른 사용자가 먼저 편집을 했다고 알려주고 수동으로 수정해서 머지하는 형식을 가지고 있다. 수동으로 수정하는 것이 문서를 많이 편집한 시점에선 귀찮게 느껴질 수 있겠다고 느꼈다.

나무위키 테스트

 

깃허브의 형식을 따라가는 것도 좋아보인다.


깃허브에서 사용하는 방식

 

이렇게 머지 컨플릭트가 나고, 보통 IDE에서 왼쪽 혹은 오른쪽을 선택하여서 머지 컨플릭트를 해결한다. 깃허브의 방식이 조금 더 사용자, 개발자 친화적인 방법이라고 판단했다. 

 

비관적 락을 사용하지 않은 이유는 무엇일까?


그거는 사용자 측면에서 생각한 문제이다. 부마위키의 특징은 학교에서 일어나는 일을 빠르게 문서로 기록하는 것이 매력이다. 그러나 문서를 수정하려고 들어갔는데, "누군가가 수정하고 있는 문서입니다." 라고 에러를 발생시키면 김이 푹 빠져버릴 것이라고 생각했다.

 

부마위키 코드에 낙관적 락 설정하기


부마위키 코드에 어떤식으로 비관적 락을 설정할지 고민해보았다.

 

부마위키는 버전간의 기록을 모두 저장한다. 그 이유는 수정 기록을 조회할 수 있게 하기 위해서이다. 그래서 우리는 DOCS와 VERSIONED_DOCS가 1:N 관계로 설정되어있다. 그래서 각각의 VERSIONED_DOCS가 version 값을 INT로 가지고 있고, 버전이 증가될 때마다 1씩 증가하는게 좋겠다고 생각했다.

VERSIONED_DOCS 테이블

 

그렇다면 업데이트 요청이 들어왔을 때 요청을 보낸 VERSION이 제일 최신의 버전이 아니라면, 문서를 충돌시키면 되겠다고 생각했다.

CONFLICT를 해결하는 방법을 고민해봤다. 내가 생각한 방법은 충돌을 유발하는 업데이트가 발생이 됐다면, 일단 그 업데이트를 VERSIONED_DOCS에 저장하고, DOCS의 컬럼 중 STATUS라는 컬럼을 CONFLICTED로 바꾸는 방법이었다. (충돌이 나지 않았다면 GOOD이다.)

DOCS의 컬럼 중 STATUS라는 컬럼을 CONFLICTED로 변경

 

업데이트 후 그 문서가 조회되는 로직을 가지고 있는데, 다시 조회했을 때 문서가 CONFLICTED 상태이면 CONFLICT를 해결할 수 있도록 구현했다. 리턴하는 값에 CONFILICTED 혹은 GOOD을 넣고, 프론트에서 문서를 수정하는 로직으로 이동시키기로 하였다.

 

이렇게 코드를 작성하면, 만약 3명이 동시에 충돌을 일으킨다고 가정하면 처음 업데이트를 누른 사람의 내용은 완전히 날아가버리기 때문에, 세번째는 에러를 발생시키기는게 좋을 거라고 생각했다. (변경사항을 임시저장해주면 좋겠다고 느껴지긴합니다.)

충돌 상태에 업데이트하려고 할 때 발생하는 에러

 

CONFLICT가 발생했을 때 우리는 원본 문서, 사용자 1이 변경한 문서, 사용자 2가 발생한 문서의 내용 전체와 원본 문서와 사용자 1이 변경한 문서의 차이점, 원본 문서와 사용자 2가 변경한 문서의 차이점을 전달하고, IDE에서 깃 머지 컨플릭트 해결하듯이 구현하기로 얘기를 했다. (추가 데이터가 필요할 수도 있는 상태이다.)

 

예시 이미지 출처: https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fmhv5M%2Fbtr5PSJ3uh4%2FVmrjz0hA3KAm3dnEDRsx70%2Fimg.png
예시 응답

 

다음과 같은 값을 받은 후에 클라이언트에서 최종 결과물을 도출한 후, 별도의 API에 그 결과물을 전송하면 CONFLICT가 해결된다.

 

느낀점


 

이번에 Merge Conflict를 만들면서 동시성 이슈에 대해서 조금 알게되었다. 동시성 이슈를 해결하기 위해선 비관적 락과 낙관적 락이 있다는 것을 알게 되었고, 우리 서비스와 사용자를 고려하면서 사용할 기술을 정하는 과정이 즐거웠다. 추가할 기능을 통해서 사용자가 조금 더 편하게 우리 위키 서비스를 사용하면 좋겠다.

부마위키 구경하러 가기!

 

부마위키 - 역사의 고서

우리의 손으로 써내려 나가는 역사의 고서, 부마위키

buma.wiki

코드 살펴보러 가기!

 

Feat/#64 by jacobhboy · Pull Request #65 · Team-INSERT/BUMAWIKI_SERVER_V2

close #64

github.com

 

마지막으로 동시성 밈 재밌는게 있어서 가져와봤다

출처: https://wgx731.medium.com/the-unbelievably-simple-way-to-disable-concurrency-in-bash-c2db8d86c44a

 

검색 태그