Ethan Hur's blog

Queue, Distributed System, 그리고 Idempotency

2018-07-27

회사에서 2달간 많이 바빴다.

그 이유 대부분은 아마 내가 한번도 해보지 않았던, 분산처리에 관련된 기능을 추가하는 작업을 하기 때문이었다.

분산처리 환경에서는 그 특성상 보장되지 않는 것이 많다.

먼저 job 을 trigger 하는 서버에서 분산처리할 job을 queue 에 넣는다고 가정해보자.

At-least-once Delivery

(당연히) SQS 를 이용했는데, SQS 는 exactly-once delivery 가 아니라 at-least-once delivery 가 기본이다. (물론 저걸 지원하는 FIFO queue 도 있긴 하지만, 리전 제한 + TPS 제한이 크리티컬하다)

at-least-once delivery의 설명을 보면

1
2
3
Amazon SQS stores copies of your messages on multiple servers for redundancy and high availability. On rare occasions, one of the servers that stores a copy of a message might be unavailable when you receive or delete a message.
If this occurs, the copy of the message isn't deleted on that unavailable server, and you might get that message copy again when you receive messages. Design your applications to be idempotent (they should not be affected adversely when processing the same message more than once).

위와 같은 설명이 있음을 알 수 있다. 즉, job 을 처리할 때 message 가 2번 이상 전달되어 handler 를 2번 탈 수도 있다는 말이다.

예를 들어 특정 로그인한 유저에게 보상을 지급하는 job 을 분산처리할 경우, 보상 로직이 2번 처리되어 유저에게 여러 번 전달될 수 있다는 말이다.

그러면 이러한 문제를 어떻게 해결할까?

Idempotency

문제를 해결하기 위한 keyword 는 Idempotency 이다. Idempotency 에 대한 자세한 설명은 여기를 참고하자. HTTP REST API 를 예시로 잘 설명해놓은 거 같다.

간단하게 말하면, 1번 처리되든 n번 처리되든 같은 결과여야 한다는 뜻이다.

개념은 당연한 소리이나, 구현 레벨에서 이를 어떻게 구현할 것인가는 그리 쉬운 문제는 아니다. (혹은 내가 이해가 부족하거나)

아주 간단하게 문제를 단순화시켜, DB에 log를 집어넣는 job 을 구현한다고 가정해보자.

그러면 multiple delivery 가 발생했을 때도 log 가 1번만 쌓이게 구현할 수 있을까?

대충 생각해보면 두 가지 방법 정도 생각난다.

  1. 일단 DB에 쌓고 나중에 중복된 log 를 어떻게든 찾아내서 1번만 쌓인 것처럼 가공한다.
  2. job 을 쏠 때 parameter 로 unique key 를 줘서 1번만 DB에 쌓이게 한다.

위 두가지 방법으로 처리하면, message 가 몇 번 delivery 됐는지와 관계 없이 일정한 결과를 얻을 수 있게 된다.

결론

위의 예제는 상당히 간단한 예제였지만, 실제 구현해야 할 job 은 복잡할 수도 있다.

하지만 분산처리를 고려하게 되면 이러한 요소를 빼고 생각할 수는 없다.

여러 개의 서버, 네트워크, Scale Out 등을 고려하면 필수불가결하게 분산 환경을 고려해야 하고, 그렇기에 이런 식의 구현을 생각해 보아야 한다.

모든 게 다 trade-off 다. 인생은 쉽지 않다.

개인적으로는 일을 하면서 많은 경험과 퀀텀점프를 할 수 있었던 2달이었던 거 같다.


더 볼만한 글: You cannot have exactly once delivery