끄적끄적 (25) 썸네일형 리스트형 [끄적끄적] 코루틴 실패전이 트러블 슈팅 (feat. 구조화된 동시성) 최근 개발을 하던도중 코루틴동작 방식을 잘못 이해하고 사용해서 문제가 발생했었는데요.까먹지 않고자 정리를 해보았습니다.문제상황비동기보다는 멀티스레드를 편하게 사용하려고 코루틴을 사용하고 있는데요.이때, 아래 예시 코드와 같이 첫번째 async가 실패하면 두번째 async로 fallback 하는 로직이 있었습니다.runBlocking { val async1 = async { ... } val async2 = async { ... } val result = runCatching { async1.await() }.onSuccess { // log }.recoverCatching { async2... [끄적끄적] Saga에서 Exception을 Json으로 변환하다가 발생한 에러와 해결 최근 진행하고 있는 프로젝트 Gitanimals에서는 분산된 트랜잭션 간의 데이터 정합성을 맞추기 위해 redis-stream을 이용해 Saga를 구현하고 있습니다. 이때, Saga 중간에 예외가 발생하면 Saga를 호출했던 클라이언트가 예외에 따라 적절한 처리를 해주기 위해서 예외 인스턴스를 직렬화해서 저장해야 했습니다. (아래 사진 참고) 그런데 이때 예외가 발생해서 롤백은 잘 되었는데 클라이언트는 Timeout Exception 예외를 받은 적이 조금 있었어요. 처음에는 네트워크 이슈라 생각하고 넘겼었는데, 특정 상황에서 잦게 발생하기 시작해서 예외를 파악해 보기 시작했습니다.setAccess 가 안된다?!Timeout Exception이 발생한 상황은 대부분 Saga안에서 JwtException .. [끄적끄적] 44만 SAGA 를 처리하며 얻은 인사이트 안녕하세요, 제가 하고 있는 사이드 프로젝트 깃 애니몰즈 에서는 유저간의 거래, 쿠폰 사용, 포인트 지급 등등 많은 영역에 걸쳐서 SAGA를 사용하고 있는데요. SAGA를 처음 사용할때는 여러가지 문제가 발생했었지만, 최근에는 꽤나 안정화 된것 같아서 SAGA 를 사용하면서 얻은 인사이트를 정리하고자 합니다. 사용하는 SAGA 프레임워크는 아래와 같습니다. GitHub - devxb/Netx: Saga framework / Supports redis stream and blocking, reactive.Saga framework / Supports redis stream and blocking, reactive. - devxb/Netxgithub.com 사이드에서 SAGA를 하는 이유SAGA를 사이드 .. [끄적끄적] 효율적인 키 분배 및 리밸런싱 방식 최근 진행중인 프로젝트는 내부적으로 redis-stream을 사용하고 있는데요, redis-stream은 key하나에 스트림 하나가 들어가므로 stream을 하나만 사용하면 redis cluster를 사용할 수 없습니다. 따라서, 클라이언트에서 로드밸런싱을 구현해줘야 하는데요, 노드 하나당 레디스 하나가 매핑된다고 가정하면, 레디스 클러스터 노드가 추가되거나 삭제될때 각 요청을 어느 노드로 보낼지 식별해서 로드밸런싱을 해줘야 합니다. 생각이 여기까지 오니까 "키 재분배 방식을 어떻게 하면 효율적으로 할 수 있을까?" 에 대해서 궁금해졌고 공부한 내용을 정리하고자 글을 작성하기로 했습니다.모듈러 연산키 재분배를 위해서 사용할 수 있는 가장 쉬운 방법은 모듈러 연산 방식 입니다. 5개의 노드가 있고 키가 백.. [끄적끄적] 주문 Saga Isolation 부족 해결하기 Saga는 트랜잭션의 ACID특징중 Isolation을 보장하지 못하는것으로 알려져 있습니다.실제로, Isolation이 부족하기 때문에 여러 문제가 발생할 수 있는데요.이번 글 에서는 Isolation이 보장되지 않으면 발생할 수 있는 문제와, DAU 1500명 가량의 Gitanimals 의 주문 시스템에서는 어떤방식으로 해결했는지 작성해보도록 하겠습니다. Isolation이 보장되지 않으면 발생할 수 있는 문제Saga는 각 참여자들이 독립적으로 데이터를 커밋하고 Saga가 실패했을때 롤백 메시지를 받아 롤백을 진행합니다.따라서, 전체 Saga가 끝나지 않았을때 Saga 참여자들의 각 트랜잭션은 이미 커밋 되었으므로, 데이터 변경이 외부에 노출되고, 다른 트랜잭션에서는 변경된, 하지만 롤백될지도 모르는.. [끄적끄적] 결제 중복 롤백 방지하기 현재 진행중인 프로젝트에서는 분산환경에서 데이터 정합성을 맞추기 위해, 유저의 결제로직이 중간에 실패할경우, 보상트랜잭션을 발행하도록 구성되어 있는데요. 이때, 보상트랜잭션 처리중 메시지가 유실되면 해당 메시지를 일정 시간후에 재처리 하게 됩니다. 주문의 상태를 "FAILED" 처럼 바꾸는 멱등한 API의 경우 메시지를 재처리해도 문제가 되지 않습니다. 그러나, "유저가 결제한 포인트를 복구" 하는것과 같이 멱등하지 않은 API는 유저에게 포인트가 중복해서 더해질 수 있으므로 재처리 과정중 멱등성을 보장해줘야 합니다. 실제로, 주문 - 결제 로직을 spike test 해보면, 아래 사진과 같이 (서버 다운 등의 이유로) 유실된 메시지를 재처리하게 되어서, 유저가 원래 갖고있던 1000 포인트 보다 더 많.. [끄적끄적] @Transactional 안에서 retry 사용을 주의하세요 TL;DR이것외에도, 트랜잭션 안에서 retry를 할 경우 여러가지 문제가 발생할 수 있는데요. 저와 마찬가지로 jitter 방식의 retry정책을 구현할 경우, 해당 스레드는 커넥션을 든채 jitter 시간만큼 대기하게됩니다. 즉, 동시성 처리에 안좋은 영향을 끼칠 수 있어요. (이것은 Webflux와 같은 reactive 프로그래밍에서도 마찬가지입니다. 비동기환경에서도 context에 커넥션이 묶인채로 jitter시간동안 대기하게되어요.) 반면, retry로직을 로컬 트랜잭션 밖으로 빼줄경우, 해당 이벤트가 jitter 만큼 대기할때, 다른 이벤트가 커넥션을 재사용할 수 있다는 이점이 있습니다. 또한, MySQL InnoDB의 Isolation level을 REPEATABLE READ 이상으로 설정할.. [끄적끄적] ProtocolBuffer로 API 문서 작성기 최근에, 강남언니 기술블로그에 기술된 글을 재미있게 본 적이 있습니다. 강남언니 블로그에서는, 프론트엔드와 백엔드 간의 api를 protocol buffer로 정의 하면서 아래와 같은 이점을 얻을 수 있었다고 합니다. 1. API 문서를 유지보수하지 않아도 되게 되었습니다. 기존에는 api가 변경되면, api 문서도 업데이트 해줘야했는데, protocol buffer로 관리하면서, api 변경이 곧 문서의 변경으로 바로 이어졌습니다. 2. 단일 진실 공급원(Single Source Of Truth) 환경이 구축됩니다. 프론트엔드와 백엔드가 각각 똑같은 IDL(protocol buffer) 를 공유하면서, 휴먼에러가 발생할 확률이 줄어듭니다. 저 또한 이런 이점에 공감해, 새롭게 진행하고 있는 프로젝트에 .. 이전 1 2 3 4 다음