요약 )

  1. 동작 중 서버 다운에 대한 대책. exactly once가 가능하려면...
    • 동작 수행과 수행 결과에 대한 저장이 transaction이나 atomic으로 이루어 질 수 있어야 한다
  2. 원격지 서버 API 요청 동작 시, 네트워크 장애에 대한 대책. exactly once가 가능하려면...
    • 처리 요청 보내기 전, 원격지에서 기존 처리 목록을 확인 할 수 있어야 한다.
    • 또는, 원격지에서 hash나 id를 보고 동일한 요청이 들어오면 처리제외하는 방어 로직이 들어가 있어야 한다.

exactly once delivery는 불가능.

exactly once processing은 가능.

 

 

메시지 중복이나 누락이 발생하는 상황?

크게 2가지 어려운 포인트를 고민해야 한다. 서버 다운과 네트워크 장애다.

kafka에서 알림 목록을 가져와 알림처리 하는 시스템이 있다고 가정해보자.

  1. kafka에서 알림 A를 가져와 로직을 수행한 뒤, 수행 결과를 저장(및 offset 증가)하기 전에 서버가 다운되면, 서버 정상화 이후 알림 A를 다시 가져와 중복으로 수행하게 된다.
  2. kafka에서 알림 A를 가져와 원격지 API 서버에 요청 보낸 뒤, 네트워크 장애로 ack를 받지 못한다면, 원격지 서버에서 A는 처리 완료, 우리 서버에서 A는 처리 미완료로 정상화 이후 알림 A를 다시 가져와 중복으로 수행하게 된다.

 

확률은 낮지만 발생 확률이 0%는 아니기 때문에... exactly once가 아니라 at least once, at most once라고 말하고, 대부분의 시스템이 이 수준으로 구성되어 있다.

현실적으로 exactly once를 만족하는 것이 매우 까다롭고 비용이 크기 때문에, 일반적으로 exactly once는 불가능이라고 말한다.

(어떤 한 시스템 구성 요소가 exactly once를 지원하더라도, 시스템 전반에 걸쳐 이를 만족해야 진정한 exactly once가 되는데 이는 매우 어렵다)

하지만 가능한 방법이 아예 없는 것은 아니다.

 

exactly once를 만족하는 방법?

  1. 동작 중 서버 다운에 대한 대책. exactly once가 가능하려면...
    • 동작 수행과 수행 결과에 대한 저장이 transaction이나 atomic으로 이루어 질 수 있어야 한다
  2. 원격지 서버 API 요청 동작 시, 네트워크 장애에 대한 대책. exactly once가 가능하려면...
    • 처리 요청 보내기 전, 원격지에서 기존 처리 목록을 확인 할 수 있어야 한다.
    • 또는, 원격지에서 hash나 id를 보고 동일한 요청이 들어오면 처리제외하는 방어 로직이 들어가 있어야 한다. (idempotent)
      • 내용이 동일한 알림이 2번 이상 발송 되는 상황도 정상이므로, hash 계산 시 알림에 대한 id를 넣는게 안전한데,
      • 이 때 ID는 원격지 서버가 API 요청을 받을 때 요청자 측에서 채번해서 API param으로 함께 받는 것이 좋아보인다.

 

이 것이 진짜 exactly once인가? - 관점에 따라 다르다

시스템 전반이 위 조건을 만족한다면, 유저가 알림 A를 중복해서 수신하는 경우는 없다. 누락도 없다. 정확히 한 번만 수신하게 된다. 이 관점에서는 exactly once다. 

- 즉 어떤 건이 처리 완료(process)되는 관점에서는 exactly once

 

그러나 알림 A가 알림 처리 로직 자체를 여러번 탈 수는 있으므로, 로직을 타는 관점에서는 exactly once가 아니라고 말할 수도 있다. (여러번 타도 idempotent 하다면 문제는 생기지 않는다.)

- 즉 어떤 건을 전송(delivery)하는 관점에서는 exactly once가 아니라 at least once 이다.