Skip to content

[사전과제] 애플리케이션 내 동시성 이슈 수정#8

Open
sgr-minkyu wants to merge 4 commits intohanghae-skillup:mainfrom
sgr-minkyu:sgr-minkyu/feat_concurrency_issue
Open

[사전과제] 애플리케이션 내 동시성 이슈 수정#8
sgr-minkyu wants to merge 4 commits intohanghae-skillup:mainfrom
sgr-minkyu:sgr-minkyu/feat_concurrency_issue

Conversation

@sgr-minkyu
Copy link

@sgr-minkyu sgr-minkyu commented Jan 4, 2025

제목(title)

[사전과제] 애플리케이션 내 동시성 이슈 수정


작업 내용

이번 PR에서 진행된 주요 변경 사항을 기술해 주세요.

코드 구조, 핵심 로직 등에 대해 설명해 주시면 좋습니다. (이미지 첨부 가능)

ex: ConcurrentOrderService에 동시 주문 요청 처리 기능 추가

  • ConcurrentOrderService를 추가했습니다.
  • 상품 정보를 저장하는 productDatabase에 ConcurrentHashMap을 적용해 여러 스레드에서 공유할 수 있도록 수정했습니다.
  • 주문 정보를 저장하는 threadLocalOrderDatabase에 ThreadLocal을 적용해 각 사용자(스레드) 요청마다 주문 정보를 관리하도록 수정했습니다.

발생했던 문제와 해결 과정을 남겨 주세요.

ex) 문제 1 - 다수의 사용자가 동시에 같은 리소스를 업데이트할 때 재고 수량이 음수로 내려가는 데이터 불일치 문제 발생

해결 방법 1 - Redis SET 명령어에 NX(Not Exists)와 PX(Expire Time) 옵션을 활용해 락을 설정했습니다. 이유는 ~

  • 문제 1 기존 제공되는 OrderService의 경우 각 스레드가 동시에 상품정보에 접근하고, 동시에 수정하기 때문에 하나씩 값이 업데이트된 재고 결과가 아닌, 다른 값이 나오게 됩니다. 테스트 환경의 경우 전체 연산 수행후 남은 재고가 92였습니다.
  • 해결 방법 1 상품정보를 ConcurrentHashMap에서 관리하도록 했습니다. ConcurrentHashMap은 원자적 연산을 제공하기 때문에 공유영역에 있는 변수를 여러 스레드에서 동시 접근할때 이를 하나씩 접근하도록 제한할 수 있습니다.
  • 문제 2 기존 제공되는 OrderService의 경우 여러 스레드가 주문정보 테이블을 공유하기 때문에 동시에 접근하면 같은 키 값으로 주문 정보를 갱신하게 되어 부정확한 주문 정보를 가지고 있습니다.
  • 해결 방법 2 기존 OrderDatabase 대신 ThreadLocal을 적용한 ThreadLocalOrderDatabase을 사용했습니다. 이렇게 되면 각 스레드마다 구분되는 OrderDatabase를 사용하게 되어 타 스레드의 주문 내용이 적용되는 것을 막을 수 있습니다.

이번 주차에서 고민되었던 지점이나, 어려웠던 점을 알려 주세요.

과제를 해결하며 특히 어려웠던 점이나 고민되었던 지점이 있다면 남겨주세요.

  • ConcurrentHashMap과 ThreadLocal의 개념과 사용법의 이해에 시간이 걸렸던 것 같습니다.

리뷰 포인트

리뷰어가 특히 의견을 주었으면 하는 부분이 있다면 작성해 주세요.

ex) Redis 락 설정 부분의 타임아웃 값이 적절한지 의견을 여쭙고 싶습니다.

  • 상품 정보 처리할 때, lambda로 if(currentStock >= amount) {...} else { .. } 를 사용하고 있는데요, java 사용시엔 else block을 가급적 자제하란 가이드를 받은 적이 있습니다. lambda사용시 else를 사용하지 않고 처리를 하는 방법이 있는지 궁금합니다. 또한 코틀린에선 else block을 사용해도 크게 이슈 사항이 아닌지 여쭙고 싶습니다.
  • 동시에 접근하는 환경에서 각 스레드별로 주문 정보가 구분해서 관리된다는 것을 보여주기 위해 주문 정보를 갱신하는 동작과 조회하는 동작을 따로, 반복해서 수행하는 식으로 구현했습니다. 또한 갱신 후 주문 정보 테이블이 이런 저런 처리 후 제거된다는 의미로 갱신과 조회를 구분했습니다. 의도했던 방향으로 구현한 건지 궁금합니다. 원래라면 이를 구분하지 않고 갱신할 때 사용하는 정보로 로깅을 했을 것 같아 여쭤봅니다.

기타 질문

추가로 질문하고 싶은 내용이 있다면 남겨주세요.

ex) 테스트 환경에서 동시성 테스트를 수행하였고, 모든 케이스를 통과했습니다. 추가할 테스트 시나리오가 있을까요?

  • 나머지 값으론 실제 수행된 횟수를 검증할 수 없는 것 같습니다. 가장 마지막 연산에서 여러 스레드가 동시에 접근해서 수행했다면 나머지는 일치하나, 수행은 그 이상 수행되게 됩니다. 다른 구조의 케이스라면 DB가 mock이나 stub으로 연결되어 있어 verify같은 방법이 가능할 것 같은데요, 이런 케이스에선 수행 횟수를 어떻게 확인할 수 있을지 궁금합니다. 또한 이 수행 횟수가 검증의 대상인지도 궁금합니다. verify는 내부 구현을 확인하는 방식이라 안티패턴이라는 의견을 들은 것 같은데요, 멘토님의 의견이 궁금합니다.
  • 추가 자료에서 synchronized는 추천하지 않는 내용이 있는데요, 프로그래머가 직접 관리하는 것을 추천하지 않는 것인지 궁금합니다. ConcurrentHashMap의 내부 구현에서도 synchronized를 사용하고 있는데 이는 이슈가 없는 지 궁금합니다.

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.

1 participant