본문 바로가기

전체 글62

[Mini-Broker] 02. 100만 개 중 하나를 빠르게 찾는 법, sparse index Intro1편에서 append-only 로그 파일을 만들었다. 메시지를 바이트로 파일에 쓰고, 처음부터 읽어서 복원하는 것까지 됐다.근데 문제가 하나 있다.메시지가 100만 개 쌓인 파일에서 offset 999,999번 메시지 하나만 읽고 싶다면?지금 RecordReader.readAll() 은 파일 전체를 처음부터 읽는다. 100만 개를 전부 메모리에 올린 다음에 원하는 것 하나를 꺼내는 셈이다. 느리고, OOM 위험도 있다.2편에서는 이 문제를 해결하는 sparse index 를 구현한다.sparse index란?책 뒤에 색인이 있다. "사과 → 35페이지" 처럼. 책 전체를 안 읽고 바로 35페이지를 펼칠 수 있다.로그 파일도 마찬가지다. "offset 100 → 파일의 4,200번째 바이트" 를 .. 2026. 5. 13.
[Seatflow] 01. 프로젝트 설계와 모노레포 세팅 Intro티켓팅 서비스는 트래픽이 가장 극단적으로 몰리는 케이스 중 하나다. 공연 티켓 오픈 순간, 수만 명이 동시에 같은 좌석을 선점하려 한다. 서버는 빠르게 처리하면서도 중복 없이 정확한 결과를 보장해야 한다.단순한 구조로는 이 문제를 해결하기 어렵다. 동시성 제어, 서비스 간 데이터 일관성, 장애 격리까지 고려하면 설계 단계에서 결정해야 할 것들이 생각보다 많다.이 시리즈는 실제 사용자가 쓰는 티켓팅 플랫폼을 만든다는 가정으로 진행한다. 연습이지만 프로덕션 마인드로. 모든 결정에는 이유가 있어야 하고, 그 이유를 나중에 설명할 수 있어야 한다.MSA란 무엇인가?MSA(Microservices Architecture)는 하나의 애플리케이션을 독립적으로 배포 가능한 작은 서비스들로 나눠 구성하는 아키텍.. 2026. 5. 11.
[Mini-Broker] 01. Kafka의 심장부를 직접 만들어보자 IntroStrimzi로 3-broker KRaft Kafka 클러스터를 운영하고 있다.Kafka를 사용하다가 어느 날 문득 이런 생각이 들었다. producer.send(record) 하면 메시지가 디스크에 어떻게 저장되는 걸까?consumer.poll() 은 어떻게 그 메시지를 정확히 찾아오는 걸까?설명할 수 없었다. 운영은 하고 있었지만, 내부는 블랙박스였다.그래서 직접 만들어보기로 했다. Java로, 표준 라이브러리만 써서, Kafka-like 단일 노드 브로커를.꼭 명심해야 할 점이 있다.이 프로젝트의 목표는 성능이 아니다. "왜 Kafka가 이렇게 설계됐는지"를 체감하는 것이다.그래서 Netty, Protobuf, Spring Boot 같은 라이브러리는 의도적으로 쓰지 않는다. 그것들이 대신.. 2026. 5. 10.
[Order] 10. Spring WebFlux vs Spring MVC + Virtual Thread 부하 테스트 비교 Intro드디어 마지막 시리즈 10편이다!이 시리즈는 쿠폰 발급, 재고 처리, Kafka Consumer 등 다양한 동시성 문제를 WebFlux로 구현하면서 시작됐다. 7편부터 9편까지 WebFlux 기반에서 낙관적 락, 비관적 락, Redis Lua, Kafka Async, DLQ, Outbox Pattern까지 구현했다.우선 꼭 명심해야할 사항이 있다.지금까지 나는 테스트 결과들을 다 올리면서 비교하고 있었는데, 아무래도 로컬 + 개인서버를 조합하여 각각 다른날에 테스트를 하고있다보니, 실제 프로덕션환경에서는 GC, 네트워크 등 다양한 변수에 따라 결과가 달라질 수도있다. 절대 수치만을 너무 맹신해서는 안된다!마지막 10편에서는 동일한 시나리오를 Spring MVC + Virtual Thread로 구.. 2026. 5. 4.
[Order] 09. Redis Lua 정합성 문제 해결: 보상 트랜잭션 vs Outbox Pattern Intro8편에서 Redis Lua Consumer 방식을 테스트했을 때 정합성이 깨지는 것을 확인했다.orders: 10,000건 DB stock: 10,000 (차감 안 됨)Redis stock: 0 Redis에서 재고를 차감했는데 DB stock은 그대로였다. Redis와 DB는 서로 다른 시스템이라 하나의 트랜잭션으로 묶을 수 없기 때문이다. 이번 편에서 이 문제를 해결한다.왜 정합성이 깨지는거지?Redis Lua Consumer의 처리 흐름은 다음과 같다.1. Redis Lua로 stock 차감2. DB에 주문 저장1번이 성공해도 2번이 실패하면 Redis stock은 차감됐는데 주문은 없는 상태가 된다. Redis와 DB는 서로 다른 시스템이라 네트워크 장애, 타임아웃, 예외 등 다양한 이.. 2026. 5. 2.
[Order] 08. Kafka 심화: 파티션 전략, DLQ, Consumer 방식 비교 Intro7편에서 Kafka Async 방식이 압도적인 처리량을 보여줬다. 그런데 테스트 환경을 자세히 보면 문제가 있었다. 파티션은 3개인데 Consumer는 1개였다. 파티션 2개가 놀고 있는 셈이다.이번 편에서는 파티션을 제대로 활용하고, Consumer 내부에서 재고를 어떻게 처리하느냐에 따라 정합성과 안정성이 어떻게 달라지는지 비교한다. 그리고 실패 메시지를 어떻게 처리할지 DLQ도 구현한다.파티션 전략파티션이란Kafka 토픽은 하나 이상의 파티션으로 나뉜다. 파티션은 Kafka의 병렬 처리 단위다. 메시지는 파티션에 분산되어 저장되고, 각 파티션은 하나의 Consumer에 할당된다.order-requests 토픽├── Partition 0 → Consumer 1├── Partition 1 → .. 2026. 4. 30.