broker가 죽어도 데이터가 안 사라지는 이유 — 복제와 ISR

broker 한 대에 모든 데이터를 두면 그 broker가 죽는 순간 전부 날아간다. 그래서 Kafka는 partition을 복제(replication)한다. 하지만 "복제했다"로 끝이 아니다 — 얼마나 복제해야 안전한지, 복제가 안 끝난 메시지는 consumer에게 보여야 하는지, 리더가 죽으면 누가 이어받는지를 정의해야 한다. Kafka의 답은 ISR(In-Sync Replicas)이라는 동적 쿼럼과 HW(high watermark)라는 commit 경계다.

이 글은 Kafka의 내결함성 모델 — f+1개 복제본이 f개 장애까지, 이미 commit된 메시지는 잃지 않는다는 보장 — 을 다룬다. replication.factor=1로 두다가 broker 한 대에 모든 데이터가 날아가는 사고를 막으려면 이 메커니즘을 이해해야 한다.

partition은 복제된 로그다

Kafka partition은 복제된 로그(replicated log)(design.md — Replication). 핵심 구조:

  • leader가 쓰기 순서를 결정하고, follower는 leader에서 fetch해 복사한다.
  • leader는 ISR(In-Sync Replicas) 집합을 유지한다. ISR 조건을 잃으면(liveness 세션 상실 또는 replica.lag.time.max.ms 초과 지연) ISR에서 제거된다.
  • commit = ISR의 모든 복제본이 해당 메시지를 받음. ISR 집합은 클러스터 메타데이터에 영속 → ISR 어느 것이든 leader 후보.

복제는 동기 push가 아니다. follower가 leader에서 pull(fetch) 한다 — consumer와 같은 방식. 그래서 leader가 follower를 밀어주지 않는다.

신뢰성 공식

_f+1 복제본으로 f 장애까지, *_이미 commit된 메시지는 잃지 않는다.***

복제본 수 감내 장애 수 비고
1 (단일) 0 broker 장애 = 데이터 손실
2 0 (또는 1, 가용성 한계) 권장 안 함
3 1 프로덕션 표준
5 2 고신뢰

ISR — "충분히 따라잡은" 복제본 집합

ISR은 leader를 포함해 "충분히 따라잡은" 복제본의 동적 집합이다. "충분히"의 기준은 두 가지:

  • liveness 세션이 살아있고,
  • leader와의 지연이 replica.lag.time.max.ms 이내.

follower가 이 기준을 어기면 ISR에서 제거되고, 다시 따라잡으면 복귀한다. ISR은 고정이 아니라 계속 변한다 — 그래서 "동적 쿼름"이다.

ISR이 왜 leader 후보 집합인가 — ISR에 있다는 건 "최신 데이터를 갖고 있다"는 보증이므로, ISR 안의 복제본이 leader가 되면 commit된 데이터를 잃지 않는다.

HW와 LEO — commit 경계와 로그 끝

여기서 두 offset이 등장한다:

  • LEO(log end offset): 각 복제본 로그의 끝 offset. leader와 follower 각자 자기 LEO를 갖는다.
  • HW(high watermark): 모든 ISR이 받은 마지막 offset. = min(모든 ISR의 LEO).
flowchart LR
    L["leader LEO=10"] --> HW["HW = min(ISR LEO들)"]
    F1["follower1 LEO=10"] --> HW
    F2["follower2 LEO=8"] --> HW
    HW --> C["HW=8: consumer에게 노출되는 commit 경계"]

HW가 consumer 노출 경계다: consumer는 HW 이하의 offset만 읽을 수 있다. 왜 — leader 장애로 사라질 수 있는 미commit 메시지를 consumer가 못 보게 하려고. HW 너머(HW < offset ≤ LEO)의 메시지는 leader엔 있지만 아직 모든 ISR에 복제 안 된, "아직 commit 아닌" 메시지다.

HW가 "commit"을 정의한다는 게 핵심이다. Kafka에선 "commit = ISR 전원 수신"이고, 그 경계가 HW다.

acks=all + min.insync.replicas — 신뢰성 강제

acks=all은 "ISR 모두"를 기다린다. 하지만 ISR이 1개만 남아도 그 1개만 확인하면 성공 → 사실상 신뢰성 없음. 그래서 min.insync.replicas(예: 2)를 설정하면, ISR 수가 이 미만이면 쓰기를 거부(NotEnoughReplicas)한다.

설정 ISR=3 ISR=2(min.insync=2) ISR=1
acks=all 쓰기 성공(전원 확인) 성공(최소 충족) 거부(NotEnoughReplicas)
1 broker 장애 감내 O O(replication.factor=3) X
가용성 높음 균형 낮음(쓰기 막힘)

전형적 프로덕션: replication.factor=3, min.insync.replicas=2, acks=all. → 1 broker 장애까지 쓰기 가능 + commit 데이터 안전.

leader 선출 — 리더가 죽으면 누가 이어받는가

리더가 장애 감지되면, controller(KRaft)가 새 leader를 선출한다. 기본 규칙: ISR 안의 follower만 leader 후보.

flowchart TD
    L["leader 장애 감지"] --> S1{"ISR에 살아있는 복제본?"}
    S1 -->|"있음"| EL1["ISR에서 선출 (데이터 손실 없음)"]
    S1 -->|"없음"| S2{"ELR(4.0) 활성 또는 unclean 허용?"}
    S2 -->|"ELR 활성"| EL2["ELR/마지막 leader 중 선출 (HW까지 보존)"]
    S2 -->|"unclean 허용"| EL3["ISR 밖 선출 (데이터 손실 가능)"]
    S2 -->|"불가"| W["쓰기 중단 — 가용성 포기, 신뢰성 선택"]

ELR(Eligible Leader Replicas) — Kafka 4.0의 가용성 개선

1차 출처: eligible-leader-replicas.md

  • Kafka 4.0 도입, 4.1부터 신규 클러스터 기본 활성화.
  • ISR이 min.insync.replicas 미만이라 HW가 멈춰 있을 때, ISR이 아닌 복제본도(조건 충족 시) leader가 될 수 있게 허용.
  • "엄격 min ISR" 규칙이 HW 전진을 막는 문제(가용성 손실)를 해소.
  • controller 선출 우선순위: ISR > unfenced ELR > 마지막으로 알려진 unfenced leader.

unclean leader election — 신뢰성 vs 가용성의 극단

unclean.leader.election.enable=true면 ISR이 전멸해도 ISR 밖 복제본이 leader가 된다(가용성 회복). 단, HW 이후의 commit 데이터 손실이 가능하다. unclean=false면 ISR 회복까지 쓰기 중단(신뢰성 우선). 4.x에선 기본 false(데이터 손실 방지) 경향 — 미검증: 정확 기본값은 broker 설정 실측.

follower는 어떻게 복제하는가

follower는 leader에게 fetch 요청을 보내 새 메시지를 당겨온다(pull). 사실상 consumer와 같은 방식. follower가 충분히 최신이면 ISR 유지, 뒤처지면(replica.lag.time.max.ms 초과) ISR에서 제외.

이 "비동기 pull 복제"가 중요한 함의를 갖는다 — leader가 follower의 보제 완료를 동기적으로 기다리는 게 아니라, follower가 알아서 따라온다. 그래서 acks=all의 "all"은 "현재 ISR 전원이 fetch 완료"를 의미하지, "모든 복제본이 무조건"을 의미하진 않는다(뒤처진 복제본은 ISR에서 빠져서 all 대상이 아님).

leader epoch — leader가 바뀌어도 로그가 발산하지 않는 장치

leader가 바뀔 때마다 한 가지 위험이 있다 — 옛 leader가 잠깐 살아나서(네트워크 분단) 자기 로그에 쓰다가 새 leader와 다른 내용이 생기는 "로그 발산(log divergence)"이다. Kafka는 leader epoch로 이를 막는다.

leader epoch는 단조증가 정수로, leader가 바뀔 때마다 1씩 올라간다. 각 로그 세그먼트는 (epoch, start-offset) 쌍을 메타데이터로 갖는다. 새 leader가 선출되면:

  1. 자기 epoch를 올린다.
  2. follower가 fetch할 때 자기가 본 마지막 epoch를 함께 보낸다.
  3. leader는 epoch 불일치를 감지하면 follower 로그의 잘못된(옛 leader가 쓴) 꼬리를 잘라내고(truncate) 자기 로그로 다시 맞춘다.

이게 KIP-101(Stop replicating after leader epoch)의 핵심이다. epoch가 없던 시절엔 HW 기준으로만 잘라내다가 모호한 경우가 있었는데, epoch로 "이 시점 이후는 옛 leader의 것이니 버려라"가 명확해졌다. replication 정확성의 근간이다.

복제 worked example — broker 장애 시 무슨 일이 일어나는가

추상 설명만으론 감이 안 온다. 3 broker(b1,b2,b3), partition p0(replication.factor=3, leader=b1, ISR={b1,b2,b3}, HW=100)에서 b1이 장애났다고 하자. 단계별로:

  1. 장애 감지: coordinator가 b1의 heartbeat 단절을 감지(session.timeout 또는 KRaft liveness). controller가 개입.
  2. ISR 재계산: b1이 ISR에서 제외 → ISR={b2,b3}.
  3. 새 leader 선출: ISR 안의 b2(또는 b3)가 새 leader. epoch 증가.
  4. HW 유지: HW는 min(b2.LEO, b3.LEO). b1이 빠졌지만 둘 다 HW=100까지 복제했으므로 HW=100 유지 → commit된 메시지(≤100) 손실 없음.
  5. 쓰기 가용성: min.insync.replicas=2, ISR=2 → 쓰기 계속 가능.
  6. b1 복구: b1이 돌아오면 자기 epoch가 옛이라는 걸 new leader에게 fetch하며 알게 되고, 옛 leader 시절 쓴(잘못된) 꼬리를 truncate한 뒤 새 leader에서 따라잡음 → ISR에 복귀.

이 시나리오가 f+1=3 복제본이 f=1 장애를 견디는 것을 구체적으로 보여준다. 핵심은 "commit(HW=100) 이하 데이터는 손실 없다"는 보장이 ISR·HW·leader epoch의 조합으로 실제로 지켜진다는 점이다.

동시 장애 2대면 어떻게 되나

b1, b2가 동시에 장애 → ISR={b3} 하나만. min.insync.replicas=2이면 ISR(1) < min(2) → 쓰기 거부(NotEnoughReplicas). 읽기는 HW=100까지 가능. b1이나 b2가 복구해 ISR≥2가 되어야 쓰기 재개. 이게 신뢰성이 가용성을 이긴 순간이다. unclean 허용 시 b3가 단독 leader가 되어 쓰기를 허용하되, HW 너머 b3만 가진 미commit 데이터가 손실될 수 있다.

ISR 라이프사이클 — flap과 복구

ISR은 고정이 아니라 계속 변한다. follower가 느려졌다 빨라졌다(flapping) 하는 실제 운영 시나리오를 보자:

  • follower b3가 GC·디스크 지연으로 replica.lag.time.max.ms 초과 → ISR에서 제거(shrink). 이 순간 partition은 URP가 된다.
  • b3가 따라잡아(leader의 HW까지 fetch 완료) → ISR에 복귀(expand).
  • 이 shrink/expand가 잦으면 broker 불건강(GC·디스크·네트워크) 신호. JMX ISRShrinkRate/ISRExpandRate로 잡는다(장 12).

ISR 변동이 잦은 partition은 안정적이지 않다 — min.insync.replicas에 자주 걸려 쓰기가 거부될 수 있고, 리더 선출 빈도가 올라간다. 운영에선 ISR 안정성 자체가 건강 지표다.

produce → commit 수명주기 — HW가 어떻게 전진하는가

복제가 정상 동작할 때 HW가 어떻게 자라는지 추적하면, "commit"이 동적으로 만들어지는 과정이 보인다. partition p0(leader=b1, ISR={b1,b2,b3}, 현재 HW=100)에서 producer가 batch를 보낸다고 하자:

sequenceDiagram
    participant P as producer
    participant L as leader b1
    participant F2 as follower b2
    participant F3 as follower b3
    P->>L: send batch[101..110] (acks=all)
    L->>L: append (LEO=110)
    F2->>L: fetch → b2도 101..110 수신 (b2.LEO=110)
    F3->>L: fetch → b3도 101..110 수신 (b3.LEO=110)
    Note over L: 모든 ISR의 LEO=110 → HW=110으로 전진
    L-->>P: ack (HW 반영, commit 완료)

핵심: HW는 leader가 모든 ISR의 fetch를 확인한 뒤에야 전진한다. producer의 acks=all 대기는 곧 "이 HW 전진이 끝날 때까지 기다린다"는 뜻이다. follower가 하나라도 느리면 그 follower의 LEO가 HW를 낮추므로, HW 전진이 늦어지고 producer ack도 늦어진다 → 처리량 저하로 이어진다. 그래서 ISR 안정성이 성능에도 직결된다.

복제 관련 주요 설정 — 튜닝 축

복제 동작을 다루는 핵심 설정(1차 출처: topic-configs/broker-configs):

설정 기본값 의미
replica.lag.time.max.ms 30000(30초) follower가 이 시간 내에 leader를 따라잡지 못하면 ISR 제거
min.insync.replicas 1(권장 2) acks=all 쓰기 허용 최소 ISR 수
replica.fetch.max.bytes 1MB follower fetch 한 번당 최대 바이트
num.replica.fetchers 1 broker당 follower fetch 스레드 수(증가시 복제 병렬성↑)
unclean.leader.election.enable false(권장) ISR 전멸 시 ISR 밖 leader 허용 여부

num.replica.fetchers를 늘리면 follower가 여러 스레드로 leader에서 당겨오므로, partition 수가 많고 복제가 따라잡지 못할 때 효과가 있다. 반대로 replica.lag.time.max.ms를 너무 크게 두면 느린 follower가 ISR에 오래 남아 처리량을 끌어내린다. 복제 튜닝은 ISR 안정성과 처리량의 균형을 잡는 일이다.

복제 throttle — 대규모 복구 시 디스크 보호

broker 장애 복구나 partition reassignment로 대량 복제가 몰리면, 네트워크·디스크가 포화돼 운영 트래픽까지 느려진다. kafka-reassign-partitions.sh --throttle <bps>로 복제 대역폭을 제한해 운영 영향을 막는다(장 12). 복구 속도와 운영 안정성의 균형 — "최대한 빨리 복구"가 항상 정답은 아니다.

복제 모델 비교 — 왜 Kafka는 ISR 동적 쿼럼을 택했나

Kafka의 ISR 모델이 유일한 복제 방식은 아니다. 다른 접근과 비교하면 Kafka의 선택이 왜 이런지 보인다.

접근 작동 Kafka ISR과의 차이
동기 전 복제(모든 쓰기마다 전원 동기 대기) 쓰기마다 N 복제본 모두 확인 Kafka도 acks=all이 유사하지만, ISR(동적)만 대기. ISR 밖 느린 복제본은 기다리지 않음 → 가용성 유지
정적 과반수 쿼럼(Raft/Paxos) 고정 N개 중 과반수 Kafka는 ISR이 동적(느린 복제본 자동 제외). ELR(4.0)이 ISR 부재 시 HW 범위 쿼럼 확대로 가용성 회복
chain replication leader→follower1→follower2 선형 전파 Kafka는 leader가 모든 follower에게서 pull(팬아웃). chain보다 병렬 fetch로 빠름
비동기 복제(ack 안 기다림) fire-and-forget acks=0과 같음. 가장 빠르나 신뢰성 없음

Kafka의 설계 철학: 신뢰성(acks=all + ISR)과 가용성(ISR 동적 + ELR)의 균형. 모든 복제본을 강제 동기 대기하면 느린 하나가 전체를 끌어내리고(가용성 손실), 반대로 비동기만 쓰면 장애 시 데이터 유실이다. Kafka는 "충분히 따라잡은 복제본(ISR)만 commit에 참여시키고, 뒤처진 건 제외해 처리량을 지킨다"는 중간 노선을 택했다. ELR은 이 균형을 가용성 쪽으로 더 밀어준 4.0의 개선이다.

복제 관련 자주 묻는 추가 질문

질문
"ISR이 1개면 commit이 의미 있나" 사실상 없음. leader만 확인하면 commit되므로 leader 장애 시 미commit 손실. 그래서 min.insync.replicas≥2 필수
"follower가 전부 느리면 producer는?" HW가 전진 안 해 acks=all 대기만 길어짐 → 타임아웃/처리량 저하. ISR 설정·broker 용량 점검 신호
"read도 leader만?" 기본 yes(follower는 복제만). follower read는 별도 설정이 있으나 일반적이진 않음 — 순서/일관성 복잡도
"복제본 수를 나중에 바꿀 수 있나" 가능(reassignment). 늘리는 건 온라인, 줄이는 건 즉시. 데이터 이동 동반

under-replicated partition (URP) — 운영 핵심 경보

ISR < 복제본 전체 수인 partition을 URP라 한다. 즉 일부 복제본이 따라잡지 못해 장애 내성이 떨어진 상태. 운영 핵심 경보 지표 — URP가 지속되면 복제 지연·broker 불건강 신호. 모니터링에서 URP 수를 잡는 게 Kafka 운영의 기본이다(장 12).

Kafka 4.3 실습 — 직접 확인하기

사전: 다중 broker 필요(3 broker KRaft). 단일 노드(replication.factor=1)에선 ISR/복제가 의미 없음.

복제 상태 확인

# Kafka 4.3, KRaft 다중 broker
bin/kafka-topics.sh --describe --topic rep-demo --bootstrap-server localhost:9092
Topic: rep-demo  Partition: 0  Leader: 1  Replicas: 1,2,3  Isr: 1,2,3
Topic: rep-demo  Partition: 1  Leader: 2  Replicas: 2,3,1  Isr: 1,2,3

확인할 것: Leader(partition별), Replicas(전체 복제본), Isr(동기화된 복제본). IsrReplicas와 같으면 정상, 일부 빠지면 URP.

broker 장애 시나리오 (개념)

한 broker 프로세스를 종료하고 --describeLeader/Isr 변화 관찰:

  • 종료된 broker가 leader였던 partition → ISR 내 다른 broker가 leader 선출.
  • 종료된 broker가 ISR에서 제거됨.
  • min.insync.replicas=2, 복제본 3 → 1대 종료 후에도 쓰기 유지.

단일 노드 환경에선 복제 실습 불가. 다중 broker 구성 필요(또는 Docker compose 3 broker).

broker 장애 시뮬레이션 — 단계별 관찰 가이드

다중 broker(3대) 환경에서 복제 동작을 직접 보는 절차:

  1. 준비: partition 3, replication.factor=3, min.insync.replicas=2 topic 생성. 메시지 produce.
  2. 정상 상태 캡처: kafka-topics.sh --describeLeader/Replicas/Isr가 모두 같은지(URP 아님) 확인.
  3. leader broker 종료: leader였던 broker 프로세스 kill.
  4. 즉시 describe: Leader가 ISR 안 다른 broker로 바뀌었는지, Isr에서 종료된 broker가 빠졌는지 확인. epoch 증가는 메타데이터로 직접 보긴 어려우나 리더 변경으로 간접 확인.
  5. 쓰기 계속: producer로 메시지 보내 — min.insync=2, ISR=2이므로 쓰기 성공하는지.
  6. 종료 broker 복구: broker 재기동. ISR에 복귀하는지(Isr에 다시 나타남) 확인.
  7. 데이터 무결성: --describeIsr == Replicas 복귀 + consumer로 누락·중복 없는지 최종 확인.

이 절차가 f+1 보장을 눈으로 확인하는 가장 확실한 방법이다. 핵심은 "leader 장애 → ISR 안 이관 → commit 데이터 무손실 → 복구 시 ISR 복귀" 흐름 전체를 한 번에 보는 것.

복제 건강 운영 체크리스트

복제를 프로덕션에서 운영할 때 매일 확인해야 할 것들:

  • URP 수 = 0(또는 일시적 스파이크만). 지속 URP는 복제 지연.
  • ISR shrink/expand 빈도 낮음. 잦으면 broker 불건강(GC·디스크·네트워크).
  • min.insync.replicas ≤ ISR 유지. ISR 하락 시 쓰기 거부 사전 경보.
  • replication.factor=3 (프로덕션). 1~2는 데이터 유실 위험.
  • acks=all 기본. acks=1은 미복제 손실 위험.
  • 복제 throttle 적용(reassignment/복구 시 운영 트래픽 보호).
  • leader 분산 고른 broker에. 한 broker에 leader 몰리면 핫스팟.

이 체크리스트는 장 12(운영·JMX 지표)와 직결된다.

흔히 묻는 것, 흔히 틀리는 것

오해 정정
"acks=all이면 무조건 안전" ISR이 1개 남으면 사실상 신뢰성 없음. min.insync.replicas와 세트
"복제는 동기 push다" follower가 leader에서 pull(fetch). 비동기
"replication.factor=2면 1대 장애 감내" 가용성 한계. 권장 최소 3
"leader 죽으면 데이터 손실" ISR 안 follower가 이어받으면 commit된 메시지 손실 없음. 손실 위험은 unclean 선출 시
"ELR은 ISR을 무시한다" 아님. ISR 우선, ISR 부재 시 HW 범위 내에서 안전하게 선출 확대
"min.insync.replicas 높을수록 좋다" 너무 높으면 ISR 하락 시 쓰기 전면 거부(가용성 손실)
"HW와 LEO는 같다" HW = min(ISR LEO), LEO = 각 복제본 끝. consumer는 HW까지만 읽음

더 깊이

  • unclean leader election 상세: unclean.leader.election.enable=true면 ISR 전멸 시 ISR 밖 복제본이 leader(가용성 회복, 단 HW 이후 commit 데이터 손실 가능). false면 ISR 회복까지 쓰기 중단(신뢰성 우선).
  • ELR이 "엄격 min ISR" 문제를 푸는 방식: 기존은 ISR < min.insync.replicas → HW 멈춤 → 쓰기 거부 → 가용성 손실. ELR은 ISR이 아닌 복제본도 HW 범위 내에서 leader 후보에 추가 → HW를 전진시켜 가용성 회복, 동시에 이미 commit된 데이터(HW 이하)는 보존. 신뢰성-가용성 균형의 4.0 해법.

요약 — 이 글의 결론

  • partition = 복제된 로그. leader 결정 순서, follower가 pull로 복제.
  • ISR = 충분히 따라잡은 복제본 집합(동적). leader 후보는 ISR.
  • commit = ISR 전원 수신. HW = ISR 모두의 LEO 최소 = consumer 노출 경계. consumer는 HW까지만 읽음.
  • f+1 복제본 → f 장애까지 commit 메시지 보존.
  • min.insync.replicas + acks=all = 신뢰성 강제. ISR 미달 시 쓰기 거부. 프로덕션 표준: replication.factor=3, min.insync.replicas=2, acks=all.
  • ELR(4.0, 4.1 기본): ISR 부재 시 HW 범위 내 복제본까지 leader 후보 확대(가용성 회복).
  • leader epoch: leader 교체 시 로그 발산 방지(truncate 옛 leader 꼬리). replication 정확성 근간.
  • produce→commit: HW는 모든 ISR의 fetch 확인 후 전진. acks=all은 이 HW 전진을 기다림.
  • URP(under-replicated partition) = 핵심 운영 경보. ISR shrink/expand 빈도도 건강 지표.
  • 설계 철학: ISR 동적 쿼럼으로 신뢰성(acks=all+ISR)과 가용성(ISR 제외+ELR) 균형.
  • 복제 설정: replica.lag.time.max.ms·num.replica.fetchers·min.insync.replicas로 ISR 안정성·처리량 튜닝.

생각해 볼 문제

  1. f+1 복제본이 f 장애까지 commit 메시지를 잃지 않는 이유를 ISR 관점에서 설명하라.
  2. HW와 LEO의 차이. consumer가 HW까지만 읽는 이유는?
  3. acks=all + min.insync.replicas=2 + replication.factor=3 설정에서 1 broker 장애 시 쓰기는?
  4. follower가 ISR에서 제외되는 조건 두 가지를 말하라.
  5. ELR(4.0)이 해결한 "엄격 min ISR"의 문제는?
  6. 복제가 동기 push가 아니라 비동기 pull인 것이 acks=all의 의미에 어떤 영향을 주는가?
  7. URP가 운영 경보의 핵심인 이유는? ISR shrink/expand 빈도는 무엇을 시사하는가?
  8. leader epoch가 없다면 leader 교체 시 어떤 문제가 생기는가?
  9. 동기 전 복제(모든 복제본 강제 대기) 대신 Kafka가 ISR 동적 쿼럼을 택한 이유는?
  10. broker 장애 시뮬레이션에서 "leader 이관 → 쓰기 유지 → ISR 복귀" 흐름이 f+1 보장을 어떻게 보여주는가?

참고

'Tech Artifacts > Kafka' 카테고리의 다른 글

Kafka - 08. delivery semantics  (0) 2026.07.09
Kafka - 07. log retention  (0) 2026.07.09
Kafka - 05. consumer group  (0) 2026.07.09
Kafka - 04. consumer  (0) 2026.07.09
Kafka - 03. producer  (0) 2026.07.09