Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Test] Category 데드락 관련 이슈를 찾기 위한 테스트 및 낙관적 락 적용 진행. #254

Open
wants to merge 42 commits into
base: test
Choose a base branch
from

Conversation

sss4920
Copy link
Contributor

@sss4920 sss4920 commented Dec 3, 2024

🚩 관련 이슈

📋 구현 기능 명세

  • 동시성 문제를 일으켰을 때 우선순위 필드에 대해서 데이터 정합성이 이뤄지는지 확인하는 테스트 작성.
  • 동시성 문제를 일으키지 않았을 때는 정상적으로 작동하는지 확인.

📌 PR Point

  • 무슨 이유로 어떻게 코드를 변경했는지

토스터에서 데드락이 로그에서 발견 되었던 상황이었습니다.

11/16일 기점으로 불규칙적으로 데드락이 발생하던 상황이 없어진 것을 보면 응답이 올 때까지 클라쪽에서 기다려주는 로직으로 변경을 가했는지 더 이상 데드락 로그가 뜨지는 않고있습니다만 근본적인 원인은 해결되지 않았던 것 같아서 시도들을 기록하고 고민하는 PR입니다.

🍞 문제상황 정의

image

위의 로그를 까본 결과 충돌한 결과는 위처럼 되어있었고 한 카테고리의 우선순위를 변경하면서 변경 범위에 들어온 카테고리 로직에 락을 걸고 변경을 하고 커밋을 하기전에 요청이 추가로 들어오면 발생하는 것 같았습니다.

🍞 테스트를 통한 검증.

테스트 코드를 진행해본 결과 이 로직에서의 허점은 동시성이슈에서 발생되었습니다.

카테고리 4개를 만들고 priority를 1,2,3,4 id도 1,2,3,4로 초기설정을 진행합니다.

테스트1과 테스트2:
id 1의 우선순위를 -> 3으로 이동
id 1의 우선순위를 -> 4로 수정

예상 결과 : id순으로 가정하면 -> //2,3,1,4 -> 1을 4로 옮기니까 단순히 2,3,4,1 순으로 Id가 만들어져야합니다.

api 요청을 다음으로 보내면 다음과 같이 결과가 나왔습니다.

🥪 테스트 결과

image

간단한 테스트였음에도 결과는 같았지만, 테스트 디비에 롤백을 적용하지 않고 진행했을 때
우선순위 필드에 같은 값이 생겨버린 것 을 확인할 수 있었습니다.

-> 정합성 깨짐이 확인.

🍞 고민한 방법들

  1. 비관적락
  2. 낙관적락
  3. named 락
  4. 구현방식 변경

비관적락은 사실상 제외 시켰습니다. 생각보다 카테고리 우선순위를 변경하는 경우는 꽤나 자주있는 편이었고, 그때마다 비관적락을 걸자니 속도에서 너무 걸리며, 현재 로직은 어떤 카테고리 레코드를 변경하면 해당 카테고리에 잇따른 다른 카테고리의 우선순위도 모두 변경되는 로직이기에 락이 걸리는 범위가 커짐에 따라 카테고리를 주로 참조하는 타이머 쪽에서 사이드 이펙트가 일어날 수 있다고 판단했습니다.

낙관적락은 고려해볼만 했습니다. 애초에 충돌이 일어나는 경우는 매우 적은 횟수이기도 했고, 서비스 특성상 해당 로직에 많은 유저가 몰려서 변경할 일이 거의 없으며, 카테고리는 유저들간의 공유도 없는 터라 고려하기에 좋을 수 있다고 판단했습니다.

named락을 이용하는 방식도 고려해봤습니다. 파사드로 적용하고 싶은 레포지터리, 메서드를 따로 빼고 적용하는 방식이 확장성에도 좋을 것 같아 고려해볼만 하긴해서 현재 낙관적락 처리와 비교해볼까합니다.

🥪 구현방식에 대한 접근들.

구현방식에도 여러가지 방법들이 있었습니다.

  1. 동시수정 CRDT -> 알고리즘적 접근
  2. linked list 사용 -> 자료구조적 접근

대표적으로 이 2가지를 고려했고, 고민했습니다.

CRDT알고리즘은 동시수정을 할 때 사용하는 알고리즘입니다.
현재 상황에서는 priority라는 필드에 대해서 이를 비슷하게 적용시킬 수 있지않을까?라는 발상에서 시작되었습니다.
쉽게 설명해둔 레퍼런스 추가해놓겠습니다.

CRDT 참고 url

예를 들어 어떤 값을 수정해야할 때 1번과 2번 id 인덱스 사이로 어떤 값을 이동시키면 1.5라는 인덱스를 만들어 넣어버리는 방법으로
요즘 고려되는 알고리즘이나 계속 절반 절반 나눠지는 부분에 대해서도 고려해야하는 단점들도 존재합니다.
이를 개선한 알고리즘들이 나오고 있으나 이에 대해서는 추가적인 학습이 필요한 상황이라 추후에 고려해보는 것도 좋을 것이라고 생각했습니다.

자료구조 쪽으로 접근했을 때는 LinkedList를 사용하는 방식을 고려했습니다. (미혜와 함께 진행할 대공사..)
하지만 현재 production과 다르게 순환참조형식과 같이 고려해야할 엔티티 변경사항이 크기 때문에 새로운 카테고리 엔티티를 하나 만들어 테스트를 충분히 한 후 여러 pr들과 미혜 쪽에서 작업할 pr을 대조해보고 더 좋은 방식으로 결정할 예정입니다.

현재 이 pr에서는 @Version이라는 필드를 카테고리에 추가하여 낙관적락에 대한 테스트를 올리는 pr입니다.

Q. 낙관적 락인데 왜 OptimisticLockingFailureException에 대한 처리가 없나요?

사실 이 부분에 대해서 해결 중입니다만.. 추가 커밋으로 고민해볼 예정입니다.

이 부분에 대해서 고려한 것이 두 가지 방법이 추가적으로 있었습니다.

먼저 고려했던 방식은 요청이 그렇게 많이 들어오지는 않지만 한번 들어올 때 연속적으로 들어오는 것이 문제였기 때문에 스핀락을 고려하더라도 유저 한명에 대해 재요청을 하는 것이었기에 고려할 수 있다고 생각했습니다.

하지만 테스트 코드를 짤 때 어려움이 있었는데요.

테스트 코드에서 멀티 스레드에 대해서는 @Transactional의 롤백이 적용되지 않았습니다.
롤백되는 스레드와 내부에서 동작하게 만들어진 스레드풀에서 사용하는 스레드가 다른 애들이라 제대로 롤백이 적용되지 않았고, 이에 대해서
고민해본 결과 애초에 테스트 데이터 베이스에 대해서 롤백을 시킬 수 있는 단위의 새로운 트랜잭션을 만들어서 데이터를 집어넣고 지우는 것을 수동으로 구현해야할 것 같은데 자칫해서 프로필을 잘못설정하면 프로덕션 디비가 날라가는 아찔한 상황이 만들어질 수 있기에 방어 시스템을 고려하는 것을 고민해봤습니당..

어찌됐든 간단하게라도 실행을 시켰을 때 count방식으로 재시도 스핀락을 구현했을 때, 3번 정도의 재시도 후 성공하는 로직을 기도했는데 이에 대해서도 실패했고,

한번의 Jpa 레포지터리 로직으로 변경을 가할 때 300Ms 정도가 걸려서 이를 고려하여 한번 재시도를 할 때마다 300Ms정도 쉬고 재시도 하는 로직으로도 실패하길래 이에 대해 해결을 하지못해서 먼저 다음과 같이만 테스트 해봤습니다. 성공하면 추가 커밋으로 올려두겠습니다!

🛠️ 테스트

  • 테스트

sss4920 and others added 30 commits September 15, 2024 14:38
[Feat/#242] 링크 이동, 최근 본 링크 구현
[#245] refactor: 토스트 제목 수정 validation 수정 및 그룹설정
[Feature/#243] 디스코드 알림 연동
[#245] fix: crawler 방지 exception 추가
@sss4920 sss4920 requested a review from mmihye December 3, 2024 08:03
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants