이 방식의 ID는 시간순으로 커집니다. 그래서 정렬을 믿고 쓰기 쉽습니다. 어디까지 믿어도 되는지를 정확히 알아야 합니다.
같은 밀리초 안에서는 노드 번호가 순서를 정한다
시각 칸이 같으면 다음 칸인 노드 번호가 크기를 정합니다. 노드 9가 먼저 발급했어도 노드 2의 ID가 더 작습니다.
즉 밀리초 안 순서는 발급 순서와 무관합니다. 요구사항에서 이미 "같은 밀리초 안 순서를 보장하지 않는다" 고 적었고, 이것이 그 뜻입니다.
노드 시계가 다르면 범위가 더 넓어진다
노드마다 시계가 몇 밀리초 다를 수 있습니다. 그러면 그 오차만큼의 구간에서 순서가 뒤바뀝니다.
노드 시계 오차가 5ms 라면
5ms 안에 발급된 ID 들의 크기 순서는 발급 순서와 다를 수 있다
시계를 잘 맞추면 이 창이 좁아지지만 0이 되지는 않습니다. 정렬을 믿어도 되는 단위는 시계 오차보다 커야 합니다.
인과 관계는 보장하지 않는다
A를 만든 뒤 그 결과로 B를 만들었다면 B가 나중입니다. 그런데 두 발급이 다른 노드에서 일어났고 같은 밀리초 안이거나 시계가 어긋나 있으면 B의 ID가 더 작을 수 있습니다.
| 쓰임 | 안전한가 |
|---|---|
| 목록을 대략 시간순으로 보여주기 | 안전하다 |
| 같은 노드가 만든 두 ID의 선후 | 안전하다 |
| 서로 다른 노드의 두 ID의 선후 | 안전하지 않다 |
| 인과 관계 판정 | 안전하지 않다 |
선후가 정말 중요하면 그 사실을 따로 기록해야 합니다. ID의 크기에 기대면 드물게 뒤집히고, 그 드문 경우가 정확히 문제가 되는 순간입니다.
ID를 페이징 기준으로 쓸 때
목록을 ID 순으로 페이징하는 것은 흔한 패턴입니다. "마지막으로 본 ID 다음부터" 로 요청합니다.
여기에 함정이 있습니다. 시계가 뒤로 갔거나 노드 사이 오차가 있으면 이미 지나간 위치보다 작은 ID가 나중에 삽입될 수 있습니다. 그 항목은 페이징에서 건너뛰어집니다.
| 상황 | 결과 |
|---|---|
| 삽입이 늘 커지는 값으로 | 누락이 없다 |
| 작은 값이 뒤늦게 삽입 | 그 항목을 못 본다 |
빠진 것은 눈에 보이지 않습니다. 중복은 사용자가 알아채지만 누락은 아무도 모릅니다.
그래서 무엇을 하나
| 요구 | 방법 |
|---|---|
| 대략 시간순 목록 | ID 정렬로 충분하다 |
| 빠짐없는 순회 | 별도의 증가 값이나 시간 범위 조회를 쓴다 |
| 선후 판정 | ID 가 아니라 명시적인 순서 정보를 쓴다 |
정렬은 저장 비용을 위해 도입한 성질이고 논리의 근거로 쓰라고 만든 것이 아닙니다. 이 구분을 지키면 뒤에서 생기는 문제가 크게 줄어듭니다.