해결된 질문
작성
·
142
0
안녕하세요 쿠케님, 강의를 복습하다 새로운 궁금증이 생겨 질문 남깁니다.
본론부터 말씀드리자면, 카프카는 큐에 이벤트가 담기고, 동일 토픽 및 동일 파티션의 경우 동일한 offset을 쓰기 때문에 순서가 지켜져서 소모가 된다고 이해했습니다. 즉, 컨슈머가 몇개든 동일 토픽, 파티션인 경우 큐에 담겨진 순서대로 명확하게 소비된다고 이해했습니다. 그런데 만약 큐에 담겨진 순서가 올바른 순서가 아니라면 어떻게 되는 것일까요?
예를 들어, 2명의 클라이언트가 거의 같은 시간에 게시글 A에 대해 댓글을 생성했고 각 클라이언트는 서로 다른 댓글 서버에서 요청이 처리됐다고 가정하겠습니다. 이때 유저 A가 게시글 A에 대한 댓글을 먼저 생성했고, 유저 B가 간발의 차로 같은 게시글에 대한 댓글을 생성했을 때, 유저 A가 생성했을 때의 게시글 댓글 수는 4였고 B가 뒤 이어서 생성했다면 5가 될 것입니다. 백엔드 서버가 전송한 유저 A의 이벤트가 알 수 없는 문제로 카프카로 늦게 도착했고, 그 사이에 유저 B의 이벤트가 먼저 큐에 담겨졌습니다. 그렇다면 제가 첨부한 그림과 같이 됩니다. (이벤트는 지연 전송 방식이 아닌 트랜잭션 After Commit 이후에 즉시 전송됐다고 가정.)
이때 컨슈머인 article-read 서비스에서 이벤트를 순서대로 소비하며 레디스에 저장된 게시글 A의 ArticleQueryModel 댓글 수를 최신화하는데 먼저 최신 값인 5로 최신화하고 다시 과거 값인 4로 최신화 할 것입니다. 이렇게 되면 게시글 A의 댓글 수 데이터의 일관성이 깨진다고 볼 수 있을 것 같습니다.
그래서 이에 대한 해결책으로 이벤트 payload에 댓글의 스노우플레이크 id가 담겨 있고, 스노우 플레이크는 시간 순으로 생성되기 때문에 레디스에 게시글 A에 대해 가장 마지막으로 처리된 이벤트의 스노우플레이크 id를 저장하고 다음으로 소비될 이벤트가 가장 마지막으로 처리된 이벤트 id보다 과거에 생성된 이벤트면 이 이벤트를 무시하면 되지 않을까? 생각했지만, 게시글에 대한 이벤트 타입이 여러가지이기 때문에 다른 타입의 이벤트가 무시될 수도 있겠다는 생각이 듭니다.
쓰고 나니까 너무 길게 질문 드린 것 같은데, 쿠케님의 강의가 정말 도움이 많이 돼서 이렇게 염치 불구하고 길게 질문드립니다. 이에 대한 의견 주시면 너무 감사하겠습니다. 강의 항상 잘 듣고있습니다.
답변 2
2
chlghksdyd31님, 안녕하세요!
꼼꼼하게 복습도 하고 계시는군요, 훌륭한 자세입니다!
말씀주신 내용의 해결 방법은 아주 다양할 수 있을 것 같네요.
프로듀서/컨슈머에 따라 달라질 수도 있고, 상황에 따라, 도메인에 따라 달라질 수도 있습니다.
그렇다고 꼭 정답이 있는건 아니고, 트레이드오프 감안해서 적절히 채택하는 과정이 필요할 것 같습니다.
예시로 몇 가지 말씀 드려보겠습니다!
컨슈머 측에서 최신에 처리한 이벤트보다 과거 이벤트가 수신된다면 무시
말씀주신대로, 가장 최신에 처리한 이벤트보다 과거의 이벤트가 수신된다면 무시할 수도 있습니다.
이벤트를 세분화하고 타입으로 구분할 수 있다면, 타입별로 최신 이벤트를 관리할 수도 있겠네요.
또는, 각 이벤트 타입에서 더 세분화해서 구분할 수도 있고요. (예를 들어, 게시글 ID 단위로 최신 이벤트 관리)
최신/과거라는 기준은 반드시 타임스탬프를 사용하지 않더라도, 프로듀서 측에서 시퀀스를 채번할 수도 있습니다.
컴퓨터가 생성해주는 시간은 엄밀히 따지면 과거로 되돌아갈수도 있기 때문에(clock drift) 가볍게 언급드려봅니다.
다만, 무시되는 이벤트가 반드시 처리되어야 했다면, 유실될 수 있다는 위험성이 있네요.
컨슈머 측에서 논리적인 관계를 이용
이벤트 간에 논리적 관계를 이용할 수도 있습니다.
예를 들어, 게시글 삭제 이벤트를 수신했다면, 게시글 생성 이벤트는 반드시 이미 수신된 상태여야 합니다.
하지만 게시글 생성 이벤트를 아직 컨슈머가 수신하지 못했을 수도 있습니다.
삭제 이벤트를 임시 버퍼에 저장해두고, 생성 이벤트가 수신될 때까지 처리를 지연할 수도 있습니다.
임시 버퍼는 DB를 활용해도 되지만, 별도의 재처리 큐를 활용할 수도 있을 것 같네요.(카프카의 dead letter queue 기능 등)
컨슈머 측 DB에 이벤트 적재 후 주기적으로 폴링해서 사용
컨슈머 측 DB에 이벤트를 저장하고, 과거 이벤트도 충분히 다 수신했을 시점이 온다면,
주기적으로 오래된 이벤트부터 폴링해서 처리할 수도 있을 것 같습니다.
프로듀서가 After Commit 이후에 즉시 전송하지 않고, 이벤트를 db에 넣고 순서대로 처리
지금 강의에서는 트랜잭션에 묶어서 이벤트를 rdb에 저장하고 있는데요,
rdb에 저장이 완료된 이벤트에 대해서 오래된 이벤트를 순차적으로 전송할 수도 있습니다.
확실하게 전송이 완료되었을 때에만 다음 이벤트를 전송하는 것입니다.
다만, 이러면 비동기에 대한 지연이나 폴링 비용은 더욱 커질 수 있겠네요.
CDC를 사용할 수도 있습니다.
강의에서는 outbox 패턴으로 직접 구현했지만, CDC로 운영하는게 더욱 쉬울 수도 있습니다.
트랜잭션 로그 기반으로 처리하기 때문에, 데이터베이스 삽입된 순서대로 알아서 잘 처리해줄 것 같네요.
순서 보장이 반드시 필요한 도메인이라면 분산 시스템을 구축하지 않는 것도 방법입니다.
분산 시스템에서 트랜잭션/순서보장을 완벽하게 구축하는건 쉽지 않고 큰 비용이 듭니다.
이벤트가 유실되고 잘못된 데이터가 들어가는게 엄청나게 큰 문제로 다가올 수 있다면,
이러한 상황을 감안할 바에야 그냥 모놀리틱한 시스템을 구축하는게 편할 수도 있습니다.
항상 모든 상황에 분산 시스템으로 만들어야하는 것도 아니고, 장비 성능도 생각보다 좋습니다.
특히 금융처럼 돈이 걸려있으면 데이터 깨지는 것 보다 시스템 운영 비용을 더 들여서라도 신뢰성을 보장하는게 더 좋은 선택일 것 같네요.
정말로 엄격한 순서 보장이 필요한지 고민해봐도 좋습니다.
순서가 엄격하게 보장되어야하는 도메인이라면, 당연히 해결책이 필요합니다.
그런데 대부분의 도메인에서 순서 보장이 그렇게 엄격하게 필요하지 않을 수 있습니다.
예시로, 게시글의 실제 댓글 수와 댓글수 데이터가 다르다고 해서, 이게 얼마나 문제가 될 수 있을까요?
말씀주신 내용은 아주 극단적인 예외 상황일때 발생할 수 있는 문제지만, 이를 해결하기 위한 시스템 구축 비용은 이점에 비해 훨씬 클 수 있습니다.
충분히 감안할 수 있는 상황이라면, 사용자 문의가 들어올 때에나 수동 대응하는게 오히려 유리할 수 있을 것 같네요.
꼭 한 가지 방법을 채택할 필요는 없고, 각 방법들을 적절히 혼용할 수도 있습니다.
혹시 더 궁금한 점 있으면 편히 문의 주세요!
0
안녕하세요, 인프런 AI 인턴입니다. chlghksdyd31님.
질문 주신 내용은 Kafka와 이벤트 순서의 일관성에 대한 중요한 주제입니다. Kafka는 이벤트를 시간 순서대로 관리하며, 각각의 파티션에서 엄격한 순서 보장이 가능합니다. 하지만, 이벤트가 들어오는 순서가 반드시 사건이 발생한 순서와 일치하지 않을 수 있습니다. 아래에 설명드릴 해결책을 고려해 보세요:
스노우플레이크 ID 사용: 스노우플레이크 ID는 순차적으로 부여되므로, 이벤트의 순서를 비교하는 데 유용합니다. 소비자가 이벤트를 처리할 때, 현재 저장된 최대 ID보다 낮은 ID의 이벤트를 무시함으로써 일관성을 유지할 수 있습니다.
이벤트 유형 식별: 이벤트에 속한 데이터의 유형을 명확히 식별하여 무시할지 여부를 결정합니다. 즉, 댓글 수에 직접적인 영향을 미치는 이벤트만 순서로 관리하고 나머지 이벤트는 별도로 처리합니다.
시간 기반 이벤트 관리: 모든 이벤트에 시간 정보를 추가하고, 소비자는 시간 정보를 참조하여 과거의 시간 값 이벤트는 무시하거나 저장된 최신 상태로 갱신하지 않도록 처리합니다.
Event Sourcing 패턴 적용: 특정 시점에서 발생한 모든 상태 변경을 기록하여 상태를 재구축하는 방식으로, 이전 이벤트가 최신 상태를 덮어쓰지 않도록 합니다.
데이터 수집과 검증: Redis와 같은 캐시 시스템에 저장하기 전에 데이터의 일관성을 정기적으로 확인하고 재조정하는 검증 로직을 추가합니다.
위와 같은 해결 방법들을 조합하여 시스템의 요구 사항과 제한 사항에 맞춰 설계하신다면, 이벤트 처리의 일관성을 보다 잘 유지할 수 있습니다.
현재 제가 소개해드린 방법들은 이 문제를 해결하는 다양한 방식 중 일부입니다. 비슷한 질문과 답변을 제공하는 링크를 다음에 참고해 주세요:
저는 질문자님의 이해를 돕고자 지식공유자님의 기존 답변을 참고하여 유사한 답변 링크를 추천해드리고 있습니다.
현재 베타 기능으로 답변이 만족스럽지 않을 수 있는 점 양해 부탁드립니다. 🙏
추가적으로 궁금한 점이 있으시면, 이어서 질문해 주세요. 곧 지식공유자께서 답변해 주실 것입니다.
오.. 생각했던 것 보다 다양한 방식이 있다는 점이 신기하네요. 정성스러운 답변 정말 감사드립니다. 😊😊