Ordering vs parallelism

judgment
상황

오퍼 상태 변경 이벤트(생성 → 가격 산정 → 승인 → 만료)를 Kafka로 흘려서 다운스트림(알림, 정산)이 받아 처리한다. 트래픽이 늘어 컨슈머를 늘리고 싶은데, 파티션을 늘리면 같은 오퍼의 '만료' 이벤트가 '승인'보다 먼저 처리될까 봐 손을 못 대고 있다.

😴 Boring — Single partition / one lane (전역 순서 보장)

언제: 이벤트가 초당 수백 건 이하이고 컨슈머 하나가 여유 있게 따라잡을 때. 순서 문제를 머리에서 지울 수 있어 코드가 가장 단순하다 — 들어온 순서대로 처리하면 끝이고, 멱등·버전 방어 없이도 상태 전이가 안 꼬인다.

비용: 차선이 하나라 처리량 상한이 컨슈머 한 대 속도다. 트래픽이 늘면 랙이 쌓이기 시작하고, 파티션을 늘리는 순간 전역 순서는 깨지므로 '나중에 파티션만 늘리면 되지'가 통하지 않는다 — 키 설계 없이 늘리면 순서 버그를 그대로 산다.

✨ Novel — Per-key partitioning (offer_id/home_id 키 + 병렬 컨슈머)

언제: 처리량이 한 컨슈머 한계를 넘었는데, 순서가 정말 필요한 범위가 '같은 엔티티 안'으로 좁혀질 때. 같은 offer_id는 같은 파티션으로 보내면 오퍼 하나의 상태 전이는 순서대로, 서로 다른 오퍼끼리는 병렬로 처리된다 — 중요한 순서만 지키고 나머지로 확장한다.

비용: 핫 키(대형 지역·인기 매물)가 한 파티션을 독점하면 스큐가 생기고, 리밸런싱·파티션 증설 때 순간적으로 순서가 흔들릴 수 있어 멱등 처리와 버전 체크 방어가 같이 필요하다. 파티션 수를 바꾸면 키가 재배치되니 처음 키 선택을 되돌리기 어렵다.

⚖️ 기본 판단

기본은 단일 레인으로 시작한다 — 초당 수백 건까지는 전역 순서가 공짜다. 랙이 쌓이기 시작하면 파티션부터 늘리지 말고 '전역 순서가 진짜 필요한가'를 먼저 묻는다. 오퍼 상태 전이(생성→승인→만료)는 같은 오퍼 안에서만 순서가 중요하니 offer_id로 파티셔닝하고, 조회수 집계나 설비 센서 합산처럼 순서를 바꿔도 결과가 같은(교환법칙 성립) 작업은 순서 자체를 포기하고 그냥 병렬로 돌린다.

🚨 트립와이어 — 이 신호가 오면 바꾼다
  • 컨슈머 랙이 1만 건을 넘고 10분 이상 줄지 않는다 — 단일 레인 처리량 한계, offer_id 키 파티셔닝을 검토할 시점
  • 이벤트 발행→반영 p99가 5초를 넘는데 컨슈머 CPU는 30% 이하 — 처리 속도가 아니라 차선 수가 병목이라는 신호
  • 파티셔닝 후 한 파티션이 전체 메시지의 30% 이상을 받는다 — 핫 키 스큐, 파티션 키 재설계(복합 키) 신호
  • 순서 뒤집힘으로 FSM이 거부한 전이(만료가 승인보다 먼저 도착)가 하루 10건 이상 — 버전 체크·멱등 방어가 빠졌다는 신호

🔧Kafka keyed partitions (key = offer_id/home_id)AWS SQS FIFO (MessageGroupId per offer)Kinesis partition keyGrafana consumer-lag + per-partition throughput dashboardsaggregate version check (reject stale transitions)

🗣 영어 한 줄 평결

I almost never need global order — partitioning by offer ID keeps each offer ordered while everything else runs in parallel.

checking microphone…

⚔️ Defend it — AI 시니어가 반박한다. 영어로 버텨라.

첫 메시지로 네 입장을 영어로 선언해라 — 예: “I'd start with Single partition / one lane here, and only switch when …” 그러면 상대가 반박을 시작한다. 근거·숫자·트립와이어로 받아쳐라.

두어 번은 치고받은 뒤에 채점