PubSub 사용할 때 주의할 것
프로그래밍/데이터 엔지니어링

PubSub 사용할 때 주의할 것

이번에 차량 블랙박스의 화각이상을 탐지하는 프로젝트에 참여하게 됐다. 실시간은 아니고 배치로 돌아가게 하면 됐으며 자연스럽게 비동기 메시지 큐를 고민하게 됐다. 결국 메시징 큐를 처음 써보는 입장에서 갓구글이 매니징해주는 PubSub을 사용하게 됐다.

공식 문서나 사용 방법은 꽤 직관적이었지만, 처음 사용해본 나의 입장에서 실수를 했던 부분들이 있어 트러블슈팅기한 몇가지를 정리해봤다.

1. Ack Deadline

일반적으로 구독(Subscription)에서 메시지를 가져오는 방식은 비동기 방식과 동기 방식이 있다. 나는 동기 방식으로 메시지를 배치로 가져왔고 이후 인퍼런스 모델이 인퍼런스를 끝낸 후, 빅쿼리 적재 + 모니터링 서버에 요청까지 마친 후 acknowledge(ack라고 줄여 이야기하겠습니다)를 했다. 그런데 간혹 인퍼런스가 지연되거나 외부 의존성에서 timeout이 걸리는 이슈가 있어 ack를 하는 속도가 늦어졌고 이에 구독에서 메시지를 제대로 제거하지 못하는 문제들이 발생했다.


해결 방안

1번. ack deadline을 높인다

PubSub의 구독은 클라이언트가 메시지를 pull한 후 ack를 해줘야 한다. 기본적으로 pubsub은 메시지를 pull한 시점부터 시작해서 ack deadline을 10-600초 사이로 설정할 수 있다.

만약 클라이언트가 ack를 deadline보다 늦게 보내게 된다면, 메시지는 빠지지 않는다. 동시에 한 클라이언트가 메시지를 먼저 Pull했다면 다른 클라이언트는 deadline까지 기다려야 함을 의미하기도 한다.

Console에서 구독 수정에 들어가면 아래와 같이 확인 기한(ack deadline)에서 설정이 가능하다

2번. modify ack deadline을 사용한다

공식 문서 링크

만약 클라이언트에서 부득이하게 특정 메시지들을 ack하고 싶지 않을 수 있다. 이때 클라이언트에서 메시지의 ack deadline을 일시적으로 변경할 수 있다. 만약 그냥 ack를 하지 않는다면 다른 클라이언트가 ack deadline까지 해당 메시지를 가져오지 못하게 되어 메시지 처리 속도가 늦어진다.

나 같은 경우 다른 클라이언트가 바로 메시지를 가져갈 수 있도록 ack_deadline_seconds를 0으로 넣어줬다.

# Python Script 일부
self.client.modify_ack_deadline(
    request={
        "subscription": self.subscription_path,
        "ack_ids": nack_ids,
        "ack_deadline_seconds": 0,
    }
)

각 클라이언트(난 파이썬을 사용)마다 modify ack deadline API를 제공하니 참고해보면 된다.


2. Deadletter

일반적으로 PubSub은 오래된 메시지를 먼저 전달하려고 한다. 따라서 구독의 메시지들을 제대로 빼주지 않는다면 기존 메시지들이 Stuck되면서 메시지가 제대로 처리되지 않는 문제가 생길 수 있다.

따라서 제대로 처리되지 않는 메시지들은 별도의 공간에 저장해둘 필요가 있다.


해결방안

pubsub에서 지원해주는 deadletter를 사용한다.

지속적으로 구독이 메시지 delivery에 실패한다면 해당 메시지를 자동으로 deadletter topic으로 보내주기 때문에 굳이 클라이언트에서 Fallback 처리를 해주지 않아도 된다. 또한 메시징 서비스가 stuck될 일도 없어진다.

deadletter를 설정할 때 max delivery attempt(최대 delivery 횟수)를 설정할 수 있는데, 이를 초과해서 구독이 메시지 delivery를 하게 되면 자동으로 deadletter 토픽으로 해당 메시지를 옮겨준다.


3. Ordering Option

구독은 기본적으로 오래된 메시지를 먼저 delivery한다. 그러나 만약 메시지를 넣은 순서대로만 빠질 수 있도록 강제하려면 Message Ordering 옵션을 적용할 수 있다.

  • 그러나 메시지 ack처리를 제대로 해주지 않는다면 문제가 발생한다. 만약 앞에 메시지를 제대로 ack해주지 않으면 그동안 뒤에 있는 메시지를 가져오지 않는 이슈가 발생한다.
  • 또한 메시지를 bulk로 가져왔을 때 ack 갯수가 이와 동일하지 않으면 메시지 전체가 빠지지 않는 이슈 또한 보였다. ordering은 한다면 ack 처리를 꼭 잘해주어야 할 듯.