728x90
반응형
SMALL

이번 포스팅은 복제본들을 복구하는 과정을 설명하는 내용이다. 아래와 같은 예시를 들어보자.

  • 3개의 Replica로 구성된 하나의 Partition
  • Producer가 4개의 메시지 (M1, M2, M3, M4)를 보낸 상황

  • Producer는 acks = all, retries = MAX_INT, enable_idempotence = false 옵션을 가지고 있다.
  • 메시지 M1, M2가 ISR 리스트 전체에 복제되었고 Commit(High Water Mark)된 상태이다. 
  • Follower Y는 M3를 복제했지만, Commit은 아직 못한 상태
  • Follower Z는 M2까지만 복제했다.

 

이 상태에서 Leader X를 포함한 Broker가 장애가 나면? 새로운 Leader가 선출된다.

  • Controller Broker가 Y를 Leader로 재선출했다고 가정하자.
  • 현재 상태는 M3, M4가 Commit되기 전에 Leader X에서 장애가 발생한 상태이다.
  • Y가 Leader로 선출되고, Leader Epoch가 0에서 1로 증가됐다.
  • Z는 Y로부터 이제 fetch를 하는데 Y에 M3가 있으니 M3를 fetch한다.
  • Y는 High Water Mark를 진행한다.
  • Z는 fetch를 다시 수행하고 High Water Mark를 수신하고 진행한다.
  • 그런데, M3는 Commit되기 전에 Leader X가 고장났는데 어떻게 Y는 M3를 가지고 있을 수 있을까? 
    • 카프카는 기본적으로 Leader의 데이터를 지우지 않는다. 비록 Y가 Follower일때 M3를 복제한 상태이고 커밋을 하지는 못했더라도 이 Y가 Leader가 된 상태라면 데이터를 지우지 않는다.
    • Leader Epoch가 일어나면, 새로운 Leader의 시대가 열렸다고 간주하고, 해당 리더의 데이터부터 시작한다.

 

그렇지만, acks = all인 상태에서 Leader X는 Producer에게 M3, M4에 대한 ack를 보내지 못한 채 죽어버렸다. 그러면 Producer는 재시도에 의해 M3, M4를 다시 보내게 된다. 누구한테? 새로운 Leader Y에게.

  • 이제 새로운 Leader Y에게 Producer는 M3, M4를 다시 보내게 된다. M3는 새로운 Leader Y가 이미 복제를 한 상태이기 때문에 중복으로 저장된다.
  • 만약, enable.idempotence 옵션이 true라면 M3를 이미 보냈다는 것을 알기 때문에 중복 저장하지 않게 된다. 

enable.idempotence

enable.idempotence 옵션은 Kafka 프로듀서에서 중복 메시지 전송을 방지하는 기능을 활성화하는 설정이다.이 옵션을 활성화하면 Kafka 프로듀서는 Producer ID (PID)와 Sequence Number를 사용하여 메시지를 추적한다.
즉, 동일한 프로듀서에서 동일한 시퀀스 번호를 가진 메시지가 중복으로 전송되면, 브로커가 이를 감지하고 중복 메시지를 무시한다.

효과

중복 방지: 같은 메시지가 여러 번 전송되더라도 Kafka가 자동으로 중복을 제거
메시지 순서 보장: 동일한 파티션에 전송되는 메시지의 순서를 유지
ACK 설정 제한: acks=all로 자동 설정됨 (데이터 손실 방지)
max.in.flight.requests.per.connection ≤ 5 제한됨 (순서 보장을 위해)

어떻게 중복을 감지하는가?

  1. Producer는 각 메시지를 보낼 때 고유한 (PID, Sequence Number)를 함께 보냄.
    • PID(Producer ID): 프로듀서가 재시작하지 않는 한 고유한 값.
    • Sequence Number: 같은 PID를 가진 메시지들에 대해 연속적으로 증가하는 숫자.
  2. Kafka의 Broker(Leader)는 (PID, Last Sequence Number)를 저장함.
    • 즉, 특정 Producer의 마지막으로 받은 메시지의 Sequence Number를 기억하고 있음.
  3. 새로운 Leader(Y)가 선출된 후에도, 이전 Leader(X)의 마지막 Sequence Number를 유지함.
    • enable.idempotence=false일 때는 Leader가 단순히 메시지를 받아들이지만,
      enable.idempotence=true이면 이미 처리한 Sequence Number 이후의 메시지만 수락함.
  4. Producer가 M3(재전송)를 보낼 때, Leader(Y)는 해당 (PID, Sequence Number)를 확인하고 이미 처리된 M3를 거부함.

 

 

다시 돌아가서, 위의 경우는 enable.idempotence 옵션이 꺼져있어서 중복으로 데이터가 저장됐다고 한들 데이터의 유실은 없었다. 그런데 만약, acks = 1 이었다면 어떻게 됐을까?

  • 새로운 Leader Y가 선출된 시점에서 M3까지는 커밋은 아니더라도 복제가 된 상태이기 때문에 M3는 날라가지 않는다. Follower Z는 새로운 Leader로부터 계속 fetch를 진행하고 M3를 받고 High Water Mark를 진행한다.
  • 그런데, Producer는 이미 M3,M4는 보내고 acks = 1 이기 때문에 기존의 Leader 였던 X에게 ack를 받은 상태다. 그럼 Producer는 M4를 보내지 않게 된다. 즉, M4라는 데이터는 영원히 잃어버리게 되는 결과를 초래한다.

여기까지 내용으로 중간 결론을 내려보면, Producer의 acks = all, enable.idempotence = true 옵션은 매우 중요하다는 것. 

 

다시 위 시나리오로 돌아가서, 죽었던 X가 복구되면 어떻게 될까?

  • X는 M1, M2까지 Commit이 된 상태였고, M3, M4를 받긴 했지만 커밋하지 못했다. 그래서 X가 복구가 된다면 M1, M2부터 다시 시작하게 된다.
  • 그리고 X는 이제 Follower 이므로, Leader로부터 데이터를 복제한다.

 

 

가용성과 내구성 중 선택?

Topic 파라미터에는 다음과 같은 옵션들이 있다.

 

⭕️ unclean.leader.election.enable

  • ISR 리스트에 없는 Replica를 Leader로 선출할 것인지에 대한 옵션 (default : false)
  • ISR 리스트에 Replica가 하나도 없으면 Leader 선출을 안 함 - 서비스 중단
  • ISR 리스트에 없는 Replica를 Leader로 선출함 - 데이터 유실

생각해보자, ISR 리스트는 High Water Mark 까지 데이터를 온전히 가지고 있는 집합이다. 물론 여기서 새로운 리더를 선출하면 더할 나위 없이 좋겠지만, 만약 ISR 리스트에 Leader를 제외한 복제본이 하나도 없으면 Leader가 고장났을 때 새 Leader를 아예 안 뽑을 것인가? 그럼 서비스가 중단될텐데? 즉 가용성 측면에서 굉장히 좋지 못할 것이다.

 

그러나, ISR 리스트에 없는 Replica는 굉장히 느린 복제본일 것이다. High Water Mark 까지 데이터를 가지고 있지 못하니까. 그런데 이 복제본으로 새 Leader를 선출하면 데이터의 유실이 생길 수 밖에 없다. 즉, 내구성이 떨어진다. 

 

서비스를 아예 중단할 것인가? 약간의 데이터 유실을 감안하고 서비스를 진행할 것인가?

 

⭕️ min.insync.replicas

  • 최소 요구되는 ISR의 개수에 대한 옵션 (default : 1)
  • 즉, 기본값으로 설정해서 사용하면 Leader만 ISR에 포함되도 문제 없다는 의미
  • ISR이 min.insync.replicas 보다 적은 경우, Producer는 NotEnoughReplicas 예외를 수신

기본적으로 이 값은 2 이상으로 설정해서 사용하면 좋다. 장애는 언제든 일어날 수 있고, 그 경우에 기본값인 1로 사용했다면 ISR 리스트에 복제본이 없어 적절한 Leader 선출이 어려워 질 것이다.

 

그리고 이 값은 Producer에서 acks = all과 함께 사용할 때 더 강력한 보장을 해준다. 무슨 말이냐면, acks = all 옵션은 모든 Followers들이 전부 다 데이터를 복제 해야 ack를 Producer에게 전달하는 옵션이다. 근데 여기서 min.insync.replicas가 2로 설정되어 있고, replication factor가 4로 설정되어 있다면 Leader 하나에 Followers 3개가 있을 것이다. 이때 Leader 포함 ISR 리스트에 Follower 중 하나만이라도 Producer가 준 데이터를 복제하면 ack를 보낼 수 있다. min.insync.replicas가 2 이므로 최소 요구되는 ISR의 개수를 만족하니 말이다. 즉, 4개 모두가 다 복제되지 않아도 ISR리스트를 만족하는 최소 요구 개수만 해당 데이터를 복제하면 ack를 보낼 수 있다는 뜻이다. 성능 면에서 더 유리함을 가져갈 수 있다. 

 

그리고, 이 옵션을 사용하면 n개의 Replica가 있고 min.insync.replicas = 2 인 경우, n-2개의 장애를 허용할 수 있다. 생각해보자. 4개의 복제본이 있는데 ISR 최소 요구 개수가 2라면, 2개까지 장애를 허용할 수 있다는 의미이다. 

 

 

정리를 하자면

 

데이터 유실과 중복이 없게 하려면, 

  • Topic : replication.factor는 최소 3 이상으로 설정, min.insync.replicas는 최소 2 이상으로 설정 
  • Producer: acks = all, enable.idempotence = true 설정

데이터 유실이 다소 있더라도 가용성을 높게 하려면,

  • Topic: unclean.leader.election.enable을 true로 설정

 

728x90
반응형
LIST

'Apache Kafka' 카테고리의 다른 글

p11. Partition Assignment Strategy  (0) 2025.03.16
p10. Consumer Rebalance  (0) 2025.03.16
p8. Replica Failure  (0) 2025.03.15
p7. Acks, Batch, Page Cache, Flush  (0) 2025.03.15
p6. Replication  (0) 2025.03.15

+ Recent posts