좋아요 순 정렬은 왜 7초가 걸렸을까?
·
dev/Spring
TL;DR좋아요순 정렬은 ORDER BY COUNT(likes) 집계라, 매 요청마다 좋아요 297만 건을 조인·집계·정렬한다 → 7.3초.인덱스로 풀스캔·해시조인은 잡아 14배 빨라졌지만(0.45초), 집계값 정렬(Using temporary; filesort)은 인덱스로 못 없앤다.like_count 컬럼으로 비정규화해 집계 자체를 없애고 정렬 인덱스를 붙이면 0.13ms. 대신 "어떻게 동기화·정합성을 유지하나"라는 새로운 문제가 생긴다.상품과 상품 좋아요는 테이블 정규화로 분리되어 있다. 상품 목록에 "좋아요 순" 정렬을 붙이고, 시드 데이터를 넣어 실제로 호출해봤다. 데이터는 상품 10만, 좋아요 약 297만, 재고 10만, 브랜드 50 규모다.일반 목록 조회는 1초쯤 나왔는데, 좋아요 순으로 ..
상품 재고 동시성 처리, 무엇을 골라야 할까? (feat. Pessimistic vs Optimistic vs Atomic update)
·
dev/Spring
이커머스 시스템을 개발하면서 상품 재고에 대한 동시성 처리에 대한 부분이 고민이 되었다.현재 Product와 ProductStock이 분리되어 있는 구조로 Product는 상품에 대한 메타 데이터를 관리하고 ProductStock은 ProductId를 가지며, Product에 대한 재고를 가지고 있다. 재고에 대한 동시성처리를 위해서 주로 많이 사용하는 3가지 케이스에 대해서 테스트를 진행했고, 테스트 결과는 어느정도 예상은 했지만 아래와 같았다.============= (threads=8, 요청량=8000, 재고=6000 → 품절 2000건) =============A) 비관적 락 : 전체 14,599 ms | 410 ops/s | 지연(avg) 14.595 ms | 락대기 누적 98,68..
테스트 더블 정리 (Dummy, Fake, Stub, Spy, Mock)
·
dev/Spring
TL;DR테스트 더블(Test Double)은 특정한 프레임워크나 도구에 종속된 기술이 아니라,"외부 의존성으로 인해 테스트가 어려운 상황을 해결하기 위해 진짜 객체 대신 사용하는 모든 대체재" 다.Java에서 테스트코드 작성시 Mockito를 주로 사용하는데, 대부분의 테스트 더블을 mock() 기반으로 표현할 수 있다.문제는 같은 mock() 객체라도 어떤 테스트는 반환값 제어에 집중하고,어떤 테스트는 호출 여부 검증에 집중하며, 어떤 테스트는 둘 다 사용한다는 점이다.하지만 코드만 봐서는 그 mock 객체가 어떤 의도의 테스트 더블인지 드러나지 않는 경우가 많다.들어가며회원 도메인을 개발하면서 UserService 단위 테스트 코드를 작성하던 중 UserRepository를 mock()으로 만들어 ..
Resilience4j CircuitBreaker 슬라이딩 윈도우 동작 원리(COUNT_BASED vs TIME_BASED)
·
dev/Spring
서킷브레이커를 도입하고 나서 슬라이딩 윈도우 타입에 대한 설정값들이 헷갈렸다.COUNT_BASED의 경우 사실 직관적이지만 TIME_BASED의 경우 딱 와닿지 않는 것 같다.Resilience4j를 기준으로 두 가지 윈도우 타입의 내부 동작 원리를 자세히 살펴보고, 어떤 상황에서 무엇을 골라야 하는지 정리해보려고 한다.슬라이딩 윈도우란?서킷브레이커는 세 가지 상태를 가진다.CLOSED: 정상 상태 (모든 요청이 통과한다.)OPEN: 차단 상태 (요청을 즉시 실패시킨다.)HALF_OPEN: 시험 상태 (일부 요청만 허용해서 회복 여부를 판단한다.)이때 CLOSED 상태에서 OPEN으로 전환할지를 결정하는 핵심 매커니즘이 바로 슬라이딩 윈도우다.윈도우는 두 가지 일을 한다.기록: 매 호출의 결과(성공/실패..
Ceph 장애가 발생했을 때, 왜 BE가 느려졌을까?
·
dev/Ceph
Ceph 클러스터 장애가 발생했을 때,BE가 계속 retry를 하면서 오히려 응답이 더 느려지고 있었습니다. 배경현재 Ceph API를 활용해서 Ceph 데이터를 서빙하는 백엔드를 개발하고 있습니다.FE → G/W → BE → Ceph MGR API (Active-Standby 구조)서비스 흐름 자체는 단순한데, Ceph의 Active-Standby 구조 때문에303 Redirect, Fallback, Retry, Active MGR 갱신 같은 걸 전부 BE에서 직접 처리하고 있었습니다.평소에는 별 문제 없이 돌아갔는데, Ceph 장애 상황에서는 다음과 같은 문제가 발생했습니다.평균 응답 시간이 1.15초까지 증가redirect → retry → host 순회 → 재귀 호출 구조장애 원인 파악이 어려움(..
[Ceph] Custom Container 배포를 해보자! (+Prometheus 연동)
·
dev/Ceph
RBD 이미지 -> PG -> OSD 매핑Ceph 클러스터를 운영하다 보면 장애 분석이나 성능·용량 이슈가 생겼을 때,“지금 보고 있는 RBD 이미지가 실제로 어떤 PG에 올라가 있고, 그 PG가 어떤 OSD 들에 퍼져 있는지”를 알고 싶은 순간이 생겼습니다. 예를 들어,1. 특정 OSD 장애가 발생했을 때, 그 OSD에 어떤 이미지가 얼마나 몰려 있는지 보고 싶거나2. 리밸런싱 이후 데이터가 의도대로 고르게 분산됐는지 확인하고 싶은경우 "Ceph 내부에서는 객체 이름과 풀 ID로 해시해서 PG를 계산하고 이 PG를 다시 CRUSH로 여러 OSD에 배치를 하게 됩니다."그래서 저는 go-cpeh(librados를 go 언어로 래핑한 오픈소스)를 사용하여 RBD 이미지 -> PG -> OSD 매핑 API ..
실행 파일을 생성하는 링커
·
dev/CS
2024.07.27 - [CS] - 소스코드파일부터 실행파일까지 컴파일러 과정 알아보기 소스코드파일부터 실행파일까지 컴파일러 과정 알아보기여러분들은 개발을 진행하면서 단순히 소스코드를 작성하게 되고, 개발한 소스코드를 토대로 실행파일이 생성이 됩니다.어떻게 우리가 작성한 소스코드가 실행파일이 되는지 알아가보겠습니jhost.tistory.com대상 파일(object file) 생성 과정에 대해서 궁금하신 분은 이전에 작성한 글을 참고해 주시면 됩니다. 링커여러분들이 특정 프로그램을 실행할때 해당 프로그램을 실행하기 위한 실행 파일은 하나죠?(물론 그 하나의 실행 파일을 실행 시키기 위해 부가적인 파일이 필요하기도 합니다.) 이전 글에서 말씀드린 컴파일러를 통해서 대상 파일(object file)이 생성된다..
소스 코드 파일부터 실행 파일까지 컴파일러 과정 알아보기
·
dev/CS
여러분들은 개발을 진행하면서 단순히 소스코드를 작성하게 되고, 개발한 소스코드를 토대로 실행파일이 생성이 됩니다.어떻게 우리가 작성한 소스코드가 실행파일이 되는지 알아가보겠습니다. 인간이 인식할 수 있는 단어로 코드를 작성하는 것 == 소스 파일 (source file)이 소스 파일을 컴파일러에게 전달하면 실행 파일 형태가 됩니다.소스 코드 -> 컴파일러 -> 실행파일컴파일러?특정 프로그래밍 언어로 쓰여있는 문서를 다른 프로그래밍 언어로 옮기는 언어 번역 프로그램입니다. (번역기)컴파일러는 고급 프로그래밍 언어를 저급 프로그래밍(CPU가 인식할 수 있는) 언어로 바꿔주는 역할을 하며, 컴파일된 이후의 코드를 목적코드(object code)라고 합니다. 왜 컴파일러를 사용할까?간단한 코드 예시int a =..
항해 플러스 4기 백엔드 솔직 후기
·
회고
벌써 항해 플러스 4기 10주간의 여정이 모두 마무리되었습니다.10주 동안 많은 일이 있었지만 내용을 정리할 겸 회고를 작성해보려고 합니다항해 플러스를 선택하게 된 계기저는 현재 3년 차 그룹웨어 서비스에서 웹 개발자로 재직 중이며, 개발자가 되기 전에 학부 시절 개발에 대해서 관심을 가지게 되었고 졸업 후 국비학원을 통해서 개발자로 취업하게 되었습니다. 현재 회사에서는 솔루션 회사이다 보니 기존에 이미 잘 만들어져 있는 소스코드 기반 위에서 작업을 하기 때문에 타 회사에서 사용하는 기술들에 비해서 많이 노후된 기술들을 사용하기 때문에 업무시간 외에 혼자서 공부를 하는데 어느 정도 한계가 있었습니다. 우연히 항해 플러스 광고를 보게 되었는데, 항해 플러스에 들어오기 전에 제가 가장 고민이 되었던 부분은 ..
항해 플러스 9주차 회고
·
회고
9주차 책임 분리를 통한 애플리케이션 설계 1. 문제비즈니스 로직과 트랜잭션 범위 고려@Transactionalvoid payment() { 결제_요청_검증() 유저_포인트_차감() 결제_정보_저장() 주문_정보_전달() //외부 플랫폼 API 호출 } 예를 들어서 상품을 결제하는 결제 함수가 있다.결제처리 하는 비즈니스 로직 안에 여러 가지 함수들이 존재하며, 결제 처리가 끝난 후 최종적으로 주문 정보를 외부 API를 호출하여 전송해 주는 기능까지 존재한다. 즉, payment()가 정상적으로 성공 처리 되기 위해서는 외부 API에 호출 또한 정상적으로 통신이 되어야 한다. 위에 로직은 정상적인 것 같지만 심각한 문제가 있다.- 외부 플랫폼 API 호출 시 네트워크 이슈로 ..