Время в распределённой системе — штука хитрая. Даже если на всех узлах крутится NTP, погрешность в локальной сети легко даёт 1–50 мс. В облаке может быть хуже. И это не баг, а особенность: clock drift накапливается. Проблема в том, что
time.time() и datetime.now() — это по сути случайные числа, когда нужно упорядочить события с разных машин. Сравнил два timestamp с разных узлов и получил бороду.Логические часы Лэмпорта
Первый вариант — каждый узел хранит счётчик, инкрементит на каждом событии. Просто, дёшево, но от физического времени отрываешься полностью. Иногда это ок, но для многих сценариев (например, метрик с временными метками) нужна привязка к реальному времени.
Гибридные логические часы (HLC)
Более живой вариант — скрещиваешь физическое время с логическим счётчиком.
class HLC:
def __init__(self, node_id, max_drift=50e-3):
self.node_id = node_id
self.max_drift = max_drift
self.l = 0
self.pt = time.time()
def now(self):
now_pt = time.time()
if now_pt > self.pt + self.max_drift:
self.l = 0
self.pt = now_pt
else:
self.l += 1
return (self.pt, self.l, self.node_id)
def receive(self, msg_time):
now_pt = time.time()
if now_pt > self.pt and now_pt > msg_time[0]:
self.l = 0
self.pt = now_pt
else:
self.l = max(self.l, msg_time[1]) + 1
self.pt = max(self.pt, msg_time[0])
Ключевая идея: если физическое время ушло вперёд больше чем на
max_drift — сбрасываешь счётчик. При получении сообщения берёшь максимум из локального времени и времени отправителя. Работает как в Cassandra, Spanner.Как измерять skew
Типичная ошибка — полагаться на
time.time() без мониторинга. Бери ntplib, дёргай общий пул, смотри разброс. Или используй tcpdump на RTT между узлами. На одном узле — time.monotonic() для интервалов, чтобы не зависеть от перевода часов.Практический совет: вычитай медиану skew из всех меток. Для критичных операций — голосование за "истинное время", как TrueTime в Spanner.
Когда без этого никак
Детерминированное воспроизведение событий в отладке, conflict resolution в CRDT на основе timestamp, синхронизация транзакций в многомастерной репликации. В production это разница между "работает" и "работает надежно".
Вывод: Clock skew — не баг, а инженерная задача, которую решают гибридные логические часы и мониторинг смещения, а не слепая вера в NTP.