Saga 패턴 토이 프로젝트로 EDA를 구현하던 중, "Kafka가 메시지를 영속화한다"는 말이 무슨 의미인지 본격적으로 파보게 되었습니다. 이 글은 그 과정에서 정리한 학습 노트입니다.이 글에서 다룰 것Kafka가 왜 "메시지 큐"가 아닌 "분산 로그 저장소" 라고 불리는지ISR(In-Sync Replicas)이 어떻게 데이터 안전성을 만들어내는지실제 장애 상황에서 ISR이 어떻게 동작하는지 (인터랙티브 시뮬레이터 포함)운영 시 어떤 설정이 표준이고, 왜 그런지1. Kafka는 분산 로그 저장소이다Kafka는 메시지 큐가 아니라 분산 로그(distributed commit log) 시스템이다.메시지를 받으면 즉시 디스크의 append-only 로그 파일에 기록메모리(페이지 캐시)는 성능 향상용 보조, ..
@Transactional 어노테이션은 readOnly 속성을 갖고 있다.기본값 : falsetrue 로 설정하면 → "이 트랜잭션은 읽기만 하고 쓰지 않는다"는 의미 (= 읽기 전용)단순히 쓰기를 금지하는 것을 넘어, 여러 계층에서 성능 최적화 효과를 얻을 수 있다.1. JPA / Hibernate 레벨JPA를 사용할 때 효과가 가장 크다. 핵심은 Dirty Checking 생략이다.평소 동작 (readOnly = false)JPA는 엔티티를 조회할 때 "스냅샷"을 함께 저장해둔다. 트랜잭션이 끝나는 시점에 현재 엔티티와 스냅샷을 비교해, 변경된 필드가 있다면 UPDATE 쿼리를 실행한다.readOnly = true일 때스냅샷 자체를 만들지 않는다 → 메모리 절약변경 감지(Dirty Checking) ..
Java Thread는 Thread.State enum으로 정의된 6가지 상태를 가진다.1. NEWThread 객체가 생성만 된 상태. 아직 스레드가 실행 중이 아님.명시적으로 start()를 호출해야 스레드가 실행된다.Thread t = new Thread(() -> {}); // NEW스레드 실행Thread t = new Thread(() -> {});t.start();2. RUNNABLEstart()가 호출되어 실행 가능한 상태. JVM 레벨에서는 실행 중이지만, OS 레벨에서는 CPU를 할당받아 실제 실행 중이거나 실행 대기 중일 수 있다. Java는 이 둘을 구분하지 않고 RUNNABLE로 통합해서 표현한다."실행 가능"이라는 표현을 쓰는 이유OS 레벨에서는 Thread 상태가 더 세분화되어 있..
"개발에 완벽한 정답은 없다. 개발은 트레이드오프와 선택의 연속이다."수강 전의 나는 개발에 "정답이 있는데 내가 아직 그걸 모를 뿐이다"라고 생각했다.10주가 지나서야 알았다. 모든 것은 트레이드오프의 연속이라는 것을.1. 수강 전 — 어딘가에 '정답'이 있다고 믿었다원래도 코드를 짤 때 고민은 많이 하는 편이었다. 기능을 받으면 "다른 방식은 없나?", "이게 실패하면 어떻게 되지?"를 먼저 생각했다. 아마 모든 개발자가 여유가 된다면 고민을 많이 할 것이다.근데 지금 돌아보면 고민의 방식이 이상했다.문제를 만나면 가장 먼저 이런 생각을 했다."이 상황에서 맞는 답이 뭐지?""이것도 문제가 있고 저것도 문제가 있네.. 어떻게 해야하지?""내가 뭘 놓치고 있는 거지?"어딘가에 정답이 있을 거라고 믿었다..
서비스 개발자라면 반드시 알고 있어야 하는 용어가 있다. 바로 SLA, SLO, SLI다. 다만 체계가 잡힌 조직이 아니라면, 실무에서 이 개념이 명시적으로 도입되지 않은 환경도 많다. 그래서 막상 문서에서 마주치면 "이게 정확히 뭘 구분하는 거지?" 싶다. 셋 다 "서비스 수준(Service Level)"으로 시작하지만 역할이 전혀 다르다. 하나씩 정리해본다.SLA — 고객과의 약속SLA(Service Level Agreement) 는 서비스 제공자와 고객 사이에 맺는 합의다. 쉽게 말해 "우리 서비스는 이 정도 품질을 약속하겠다"는 내용을 수치로 명시한 계약이다.예를 들면 이런 항목이 들어간다.가용성 99.9% uptimeAPI 응답 200ms 이내처리량 1,000 TPS약속을 지키지 못하면 보상이나..
10주가 끝났다. 매주 주제를 받고, 과제를 하고, 멘토링을 받고, 회고를 쓰는 사이클을 반복했다. 돌이켜보면 꽤 빠듯했는데, 그 빠듯함 덕분에 남은 것도 많다. 주차별로 뭘 배웠는지보다는, 10주 동안 내 사고방식이 어떻게 바뀌었는지를 중심으로 정리해보려 한다.테스트가 설계를 이끈다는 말의 의미1주차 주제는 TDD였다. Red-Green-Refactor, 테스트 더블, MockMvc. 개념 자체는 이미 알고 있던 것들이었다.그런데 직접 테스트를 짜보니 생각과 달랐다. 테스트 코드를 작성하는 것보다, 테스트를 세팅하는 과정이 더 고됐다. 목(mock)을 준비하고, 의존성을 주입하고, 테스트 환경을 맞추는 데 시간이 오래 걸리는 코드가 있었다. 처음에는 "테스트가 원래 이렇게 번거로운 건가?" 싶었는데, ..
CountDownLatch는 하나 이상의 스레드가 다른 스레드들의 작업 완료를 기다릴 수 있게 해주는 일회성 동기화 도구(synchronization aid) 이다. "일회성"이라고 표현한 이유는 한 번 사용하면 재사용 할 수 없다는 뜻이다. 동시성 테스트에서는 스레드들의 동시 시작을 조율 하는 용도로 활용된다.핵심 동작 원리생성 시 카운트 값을 지정하고, countDown() 호출 시 카운트를 1씩 감소시킨다. await()을 호출한 스레드는 카운트가 0이 될 때까지 블로킹된다. 카운트가 0이 되는 순간 await()에서 대기하던 모든 스레드가 실행을 재개한다.카운트가 0에 도달하면 리셋할 수 없다. 카운트가 이미 0인 상태에서 countDown()을 호출하면 아무 일도 일어나지 않는다.카운트가 이미 ..
기본편에서 대기열을 만들고 효과를 검증했다. 하지만 50명이 동시에 입장하는 Thundering Herd, polling이 만드는 부하, Redis 장애 시 주문 불가라는 문제가 남아 있었다. 배치를 쪼개고, polling 주기를 차등 적용하고, Kafka로 우회하는 Graceful Degradation을 설계하면서 "대기열을 만드는 것"과 "대기열을 운영하는 것"은 다른 문제라는 걸 알게 됐다.대기열 기본편에서는 Redis 기반의 기본적인 대기열을 구축해봤다. 여기서는 대기열을 운영하면서 발생할 수 있는 추가적인 문제를 해결해보고자 한다.1. Thundering Herd — 50명이 동시에 문을 열면기본편에서 대기인원을 입장시키는 배치 사이즈는 50, 스케줄 주기를 1,000ms로 설정했다. 초당 50..
한국에서 흑백 요리사라는 프로그램이 굉장히 인기였다. 요리 서바이벌 프로그램인데, 프로그램이 끝나고 나면 서바이벌에 참가한 요리사들의 식당은 항상 사람들로 넘쳐났다. 식당을 방문하기 위해서 한참 기다려야 했다. 좌석이 한정적이었기 때문이다. 식당들은 넘쳐나는 손님들을 수용하기 위해 대기자 명단을 작성해두고, 손님들이 빠져나간 만큼 수용 가능한 인원들을 입장시킨다. 우리는 시스템에서 이것을 대기열이라 부른다.블랙프라이데이를 앞두고 주문 시스템에도 대기열이 필요했다. 트래픽이 폭발적으로 증가할 것으로 예상되는데, DB는 스케일 아웃이 제한적이고 오토스케일링은 극단적으로 짧고 높은 트래픽 앞에서 반응이 늦다. 시스템을 키우는 데 한계가 있다면, 방향을 바꿔야 했다. 들어오는 요청을 시스템이 처리할 수 있는 속..
Sorted Set이란?Sorted Set은 Redis 자료구조 중 하나로 하나의 키에 여러 score와 member로 구성된 데이터를 관리한다. 각 member에 실수값인 score를 부여하며 score를 기준으로 정렬된 상태를 유지하는 집합이다. member는 고유값이며 서로 다른 member는 동일한 score를 가질 수 있다. score가 동일한 경우 member의 사전순(lexicographical order)으로 정렬된다.문법 표기법아래 문법 설명 중 옵션 표기에 대한 설명이다.[]로 감싼 항목은 선택적(optional) 옵션이며 생략 가능하다.|는 둘 중 하나만 선택 가능함을 의미한다.학생 성적 예시keyscorememberexam_results72charlieexam_results85bob..
