앞 단계에서 연결을 받는 계층과 메시지를 처리하는 계층을 나눴습니다. 처리 계층을 여러 대로 늘리면 곧바로 문제가 하나 생깁니다. 같은 채널의 메시지가 서로 다른 서버에서 처리되면 순서가 뒤바뀝니다.
요구사항은 순서를 채널 안에서만 보장한다고 정했습니다. 이 절은 그 약한 보장을 어떻게 지키고, 그 대가로 무엇을 얻는지 다룹니다.
시계로는 순서를 정할 수 없다
가장 먼저 떠오르는 방법이 보낸 시각으로 정렬하는 것입니다. 이게 안 되는 이유가 두 개입니다.
| 문제 | 무슨 일이 생기나 |
|---|---|
| 서버마다 시계가 다르다 | 나중에 보낸 메시지가 더 이른 시각을 갖는다 |
| 같은 밀리초에 둘이 들어온다 | 정렬 기준이 없어 조회할 때마다 순서가 바뀐다 |
클라이언트 시각을 쓰면 더 나쁩니다. 사용자 기기의 시계는 얼마든지 틀릴 수 있습니다.
그래서 서버가 채널마다 하나씩 증가하는 번호를 붙입니다. 이 번호가 순서의 유일한 근거가 됩니다. 시각은 화면에 보여주는 용도로만 씁니다.
채널로 나누면 순서가 남는다
번호를 붙이려면 그 채널의 메시지가 한 곳을 순서대로 지나야 합니다. 모든 채널이 한 곳을 지나면 그곳이 상한이 되지만, 채널을 기준으로 나누면 채널 수만큼 병렬로 처리할 수 있습니다.
채널 번호로 어느 파티션에 넣을지 정하면 같은 채널은 늘 같은 파티션으로 갑니다. 파티션 안에서는 들어온 순서대로 처리되므로 채널 안 순서가 저절로 지켜집니다.
파티션 1의 메시지와 파티션 2의 메시지 사이 순서는 보장되지 않습니다. 요구사항이 그것을 요구하지 않기 때문에 문제가 아닙니다. 포기한 보장이 정확히 이 확장을 사 준 것입니다.
파티션 수를 바꾸는 순간이 위험하다
파티션을 늘리면 나누는 기준이 바뀌어 일부 채널이 다른 파티션으로 옮겨 갑니다. 옮겨 가는 도중에는 같은 채널의 메시지가 두 파티션에 걸치고, 그 구간에서만 순서가 깨집니다.
| 방법 | 대가 |
|---|---|
| 옮길 채널만 잠깐 멈추고 남은 것을 비운 뒤 옮긴다 | 그 채널이 몇 초 지연된다 |
| 일관 해싱으로 옮겨 가는 채널 수를 줄인다 | 구현이 복잡해진다 |
어느 쪽이든 파티션 수는 처음에 넉넉하게 잡는 편이 낫습니다. 늘리는 일이 공짜가 아니기 때문입니다.
큰 채널 하나가 파티션을 독점한다
최대 5만 명 채널은 메시지도 많습니다. 그 채널이 들어간 파티션 하나만 뜨거워지고 나머지는 한가한 상태가 됩니다. 파티션을 늘려도 한 채널은 쪼개지지 않으므로 해결되지 않습니다.
이때 쓰는 방법이 순서 보장 범위와 전달을 떼어 놓는 것입니다. 번호를 붙이는 일만 그 채널 전용 자리에서 하고, 5만 명 에게 실제로 보내는 일은 여러 일꾼이 나눠 맡습니다. 순서는 번호로 이미 정해졌으니 전달 순서는 상관없습니다. 받는 쪽이 번호로 정렬합니다.
화면은 서버 번호를 기준으로 다시 세운다
사용자 경험을 위해 보낸 메시지를 서버 응답 전에 화면에 먼저 그립니다. 그때는 번호가 없으니 임시로 맨 아래에 둡니다.
서버 응답으로 번호를 받으면 그 자리에 다시 꽂습니다. 내가 보내는 동안 남이 보낸 메시지가 먼저 번호를 받았다면 내 메시지가 한 칸 아래로 내려갑니다. 번호가 있는 것끼리는 늘 번호 순으로 두는 것이 규칙입니다.