벡터 시계: 숫자 하나로 표현할 수 없는 인과 관계
벡터 시계는 분산 시스템의 사건들이 인과적으로 앞뒤 관계인지, 서로 독립적으로 발생했는지 표현하는 논리 시계다. 각 노드가 하나의 숫자를 가진 대신 노드별 카운터를 묶은 벡터를 유지한다. 실제 날짜나 초 단위 시각을 측정하는 장치는 아니다.
벡터 시계는 물리적 시각보다 메시지로 전달되는 인과 관계를 기록한다.
사건과 메시지가 인과 관계를 만든다
같은 노드에서 먼저 발생한 사건은 뒤 사건에 선행한다. 메시지를 보내는 사건은 그 메시지를 받는 사건에 선행하며 이 관계는 추이적이다. 이 happened-before 관계가 없으면 두 사건을 동시적이라고 부른다. 실제 벽시계 시간이 다를 수도 있다.
두 노드 A,B는 [0,0]으로 시작한다. A가 사건을 수행하면 자기 성분을 늘려 [1,0]을 기록한다. B가 A의 벡터를 메시지로 받으면 각 성분의 최댓값을 취한 뒤 자기 성분을 늘려 [1,1]이 된다. B의 사건에 A의 사건 정보가 전달되었다는 뜻이다.
def receive(local_clock, incoming_clock, own_index):
combined = [max(local, incoming)
for local, incoming in zip(local_clock, incoming_clock)]
combined[own_index] += 1
return combined
def happens_before(first, second):
return (all(a <= b for a, b in zip(first, second))
and any(a < b for a, b in zip(first, second)))
코드는 벡터 길이가 같고 성분별 노드 순서가 고정되어 있다고 가정한다. zip은 길이가 다르면 짧은 쪽까지만 처리하므로 실제 API에서는 길이와 노드 식별을 검사해야 한다. 로컬 사건과 송신에서도 해당 성분을 증가시키는 일관된 규칙이 필요하다.
비교할 수 없는 벡터는 동시 변경이다
[2,1]과 [1,2]는 한쪽은 첫 성분이 크고 다른 쪽은 둘째 성분이 크다. 어느 벡터도 다른 벡터보다 모든 성분에서 작거나 같지 않아 인과 순서를 정할 수 없다. [2,1]과 [3,2]는 첫쪽이 성분별로 작고 하나 이상 엄격히 작아 선행 관계를 나타낸다.
Lamport의 단일 논리 시계는 인과 선행이면 숫자가 증가하지만 숫자가 작다는 사실의 역방향이 항상 성립하지 않는다. 벡터 시계는 표준 사건 모델에서 인과 관계와 성분별 비교를 더 정확히 대응시키려는 구조다.
동시 수정된 문서 버전을 찾거나 분산 추적에서 관계를 분석할 때 유용하다. 다만 동시임을 알려 준다고 어느 변경이 옳은지나 어떻게 병합할지 결정해 주지는 않는다. 애플리케이션의 충돌 해결이 따로 필요하다.
노드 수에 비례하는 메타데이터와 비교 비용이 들고, 참여 노드가 늘고 줄면 식별자·벡터 정리 정책이 복잡해진다. 데이터 버전용 version vector와 사건별 vector clock은 관련되지만 관리 대상이 다를 수 있다. 시계의 의미, 증가 사건, 메시지 규칙을 먼저 정의해야 벡터 비교를 올바르게 해석할 수 있다.