Проблемная секунда
Сегодня каждый может легко проверить текущее время множеством способов. У нас есть смартфоны, часы, компьютеры, ТВ, холодильники и так далее. Все знают, какой на дворе год, и что он означает. Мы разбираемся во временны́х зонах и знаем, что такое всемирное координированное время (UTC). Всё кажется таким тривиальным. Однако с точки зрения IT-систем здесь существует множество сюрпризов, вынуждающих нас сталкиваться с проблемами, которые возникают очень редко.
В различных IT-системах можно встретить множество нюансов, касающихся времени. Чаще всего они связаны с точностью его измерения и синхронизации между машинами в распределённых системах. В этой же статье я разберу одну странную ситуацию, которая за всю историю случалась лишь несколько раз.
▍ Високосная секунда
Изначально секунда была определена как 1/86400 доля суток. Затем в 1960 году её определение было изменено на 1/31 556 925,9747 долю тропического года. Однако оказалось, что Земля движется вокруг Солнца не всегда в точности одинаково, и иногда возникают отклонения от этого значения, вызываемые приливами, движением масс внутри планеты и землетрясениями. В связи с этим в 1967 году значение секунды было определено совершенно иным образом, а именно:
Как равное 9 192 631 770 периодам излучения, соответствующего переходу между двумя уровнями F = 3 и F = 4 основного состояния сверхтонкой структуры S1/2 атома цезия 133Cs.
Наиболее точными являются атомные часы, которые измеряют время на основе скорости прохождения электронов между двух оболочек атома. Однако остаётся вопрос компенсации разницы между значениями, измеряемыми атомными часами, и фактическим временем вращения Земли вокруг Солнца.
Для этого и была придумана «високосная секунда». День, когда нужно добавить эту секунду, определяет IERS (International Earth Rotation and Reference Systems Service, международная служба вращения Земли). Обычно она добавляется в последний день июня или декабря. Вычитание секунды тоже возможно, но на сегодня ещё ни разу не производилось.
История внесения високосных секунд:
- (+11 с) 30 июня, 1972
- (+12 с) 31 декабря, 1972
- (+13 с) 31 декабря, 1973
- (+14 с) 31 декабря, 1974
- (+15 с) 31 декабря, 1975
- (+16 с) 31 декабря, 1976
- (+17 с) 31 декабря, 1977
- (+18 с) 31 декабря, 1978
- (+19 с) 31 декабря, 1979
- (+20 с) 30 июня, 1981
- (+21 с) 30 июня, 1982
- (+22 с) 30 июня, 1983
- (+23 с) 30 июня, 1985
- (+24 с) 31 декабря, 1987
- (+25 с) 31 декабря, 1989
- (+26 с) 31 декабря, 1990
- (+27с) 30 июня, 1992
- (+28 с) 30 июня, 1993
- (+29 с) 30 июня, 1994
- (+30 с) 31 декабря, 1995
- (+31 с) 30 июня, 1997
- (+32 с) 31 декабря, 1998
- (+33 с) 31 декабря, 2005
- (+34 с) 31 декабря, 2008
- (+35 с) 30 июня, 2012
- (+36 с) 30 июня, 2015
- (+37 с) 31 декабря, 2016
▍ Время и IT-системы
Отслеживание времени используется в большинстве проектов. Нам нужна возможность отмечать время, когда произошло определённое действие пользователя, или когда было отправлено электронное письмо. Также бывает важно определять конкретный порядок событий. В случае одной машины это не особо сложно. А вот в распределённых системах, где мы имеем дело со множеством узлов, ситуация сильно усложняется.
Аппаратное время, устанавливаемое обычным тактовым генератором, не является абсолютно точным, и между узлами могут возникать различия. Когда для нас важна последовательность событий, расхождение в показаниях часов узлов становится неприемлемым. Для их синхронизации используется NTP (Network Time Protocol, протокол сетевого времени), который сопоставляет время конкретной машины со временем, указываемым группой серверов.
В компьютерах существует как минимум два вида часов: часы истинного времени и монотонные часы.
▍ Часы реального времени
Эти часы просто отображают фактическую дату и время. В системах Unix они опираются на дату 1 января 1970 года, 00:00:00, отсчитывая с этого момента количество прошедших секунд. Такие часы являются проблематичными, поскольку синхронизируются через NTP, который может заставить их перескакивать вперёд или назад. В связи с этим они недостаточно подходят для измерения пройденного времени, например, таймаутов.
▍ Монотонные часы
Монотонные часы более точны и предоставляют значения в микросекундах или даже более мелких единицах. Реализуются они в виде счётчиков, которые, например, могут начинать отсчёт при запуске системы. NTP в этом случае может корректировать только скорость отсчёта, замедляя или ускоряя конкретные часы. Поскольку перескакиваний в этом случае не происходит, такие часы лучше подходят для измерения пройденного времени.
▍ Последствия плохой синхронизации
Результатом недостаточной синхронизации расходящихся по времени часов может стать потеря данных. В этом случае лучше, когда система полностью падает, чем работает некорректно, что может привести к повреждению данных. Поэтому в ситуациях, когда мы опираемся на часы, они должны быть точно синхронизированы и отслеживаться. Если же часы работают некорректно, их нужно рассматривать как полностью неисправные.
▍ Порядок событий
К примеру, при репликации с несколькими ведущими узлами используется стратегия, при которой приоритет отдаётся последней операции записи. Поэтому если часы двух узлов будут рассинхронизированы, пусть даже на 2-3 мс, может измениться порядок записей, что приведёт к получению некорректных данных. Тем не менее по факту даже синхронизации с помощью NTP бывает недостаточно, поскольку в процессе работы также присутствуют сетевые задержки. В связи с этим для упорядочивания событий следует использовать логические часы на основе инкрементирования счётчика. Они не измеряют время, а лишь определяют относительный порядок событий.
В некоторых базах данных вроде Spanner от Google используется TrueTime API, возвращающий диапазон времени, которое может быть актуальным на данный момент (то есть с учётом возможной ошибки) — [самое раннее, самое позднее]. Такая база данных подобающим образом обрабатывает транзакции, затронутые этой ошибкой, обеспечивая тем самым правильный порядок событий. Естественно, чтобы добиться хорошей производительности, диапазон времени не должен быть слишком большим. Для этого Google в каждом из своих дата-центров для определения текущего времени использует GPS или атомные часы, что позволяет добиваться синхронизации с точностью до 7 мс.
Как видите, время в IT-системах является довольно непростой темой. Но мы с вами сосредоточимся на високосной секунде и проблемах, связанных именно с ней.
▍ Проблемы и сбои
Когда некое явление происходит редко, программисты понимают, что оно наверняка будет отличаться от их ожиданий, поскольку с ним ещё никто не сталкивался. В отношении високосных секунд подобная ситуация происходила 27 раз за 52 года, то есть в среднем она случается каждые два года. Это крайне редко, что ведёт к неизбежным проблемам и программным ошибкам. Самым сложным периодом оказался промежуток между 1999 и 2005 годами, когда о високосных секундах, по сути, просто забыли.
30 июня 2012 года на Reddit была зафиксирована значительная просадка быстродействия. Поначалу администраторы решили, что причина в замедлении сети, которое уже происходило несколькими днями ранее. Однако после более детального расследования выяснилось, что на машинах под управлением Linux очень сильно нагружаются процессоры. Проблема заключалась в ядре Linux — модуль hrtimer
обезумел после того, как добавили лишнюю секунду. И коснулось это не только платформы Reddit, поскольку сбои заметили и другие компании. Линус Торвальдс прокомментировал ситуацию так:
Практически каждый раз, когда мы добавляем високосную секунду, мы что-то обнаруживаем. Это реально раздражает, поскольку здесь у нас классический случай, когда мы имеем дело с кодом, который никогда не выполнялся, а значит не тестировался пользователями в типичных условиях.
▍ Объяснение проблемы hrtimer
Модуль hrtimer
отвечает за вывод приложений из состояния ожидания. Имеются в виду приложения, которые ожидают, например, завершения выполнения операционной системой некоего задания, чтобы приступить к выполнению своего. Когда была добавлена високосная секунда, модуль сошёл с ума и пробудил все приложения, вызвав перегрузку процессора и сильное падение производительности, а иногда даже полный отказ системы.
В случае Reddit проблема в основном повлияла на базу данных Cassandra и Java, в то время как аналогичные проблемы наблюдались в базах данных MySQL, на серверах Tomcat и платформе Hadoop.
Интересно, что за день до этого, когда операторы NTP объявили о добавлении високосной секунды, упали серверы Opera.
▍ Потребление энергии
Дата-центр Hetzner Online сообщил, что в ночь на 1 июля 2012 года значительно подскочило потребление энергии, превысив 1 мегаватт — вызвано это было описанным выше багом. Процессоры внезапно начали работать под 100% нагрузкой, что привело к повышению энергопотребления. На графике показано, что инцидент произошёл в 2:00 ночи, вероятно, из-за учёта временно́й зоны. Аналогичный скачок энергопотребления произошёл и в OVH. Скорее всего, подобная аномалия произошла и в других центрах, хотя сообщили о ней только эти два.
▍ Проблема 2000 года
Ещё несколько десятилетий назад мы и подумать не могли о такой роскоши, как практически безграничное использование памяти.
По этой причине год записывали, используя только две последние цифры. Первые проблемы начали возникать в 1990-х при определении, например, действительности кредитной карты, срок действия которой уходил в 2000-е годы. Многие люди говорили о катастрофе, которую это может вызвать — прогнозировали реальный апокалипсис. Конец света, конечно, не случился, но это всё же одна из глупейших ошибок программистов в истории. Очень опрометчиво брать в расчёт только недалёкое будущее. В конечном итоге приведение программного обеспечения в порядок потребовало много работы и, естественно, затрат.
▍ Варианты добавления високосной секунды
Простейшим способом является объявление серверами NTP о предстоящем добавлении високосной секунды. В итоге при её фактическом добавлении в конце минуты значение 59 отображается дважды.
Есть ещё один подход, который в основном используется в Windows и подразумевает просто игнорирование системой дополнительной секунды. В этом случае время переводится на корректное значение только при последующей синхронизации с NTP.
У обоих этих решений есть свои недостатки, поскольку в первом мы дублируем одно и то же время, а во втором сталкиваемся со странным переводом часов. Поэтому в Google решили подойти к этому вопросу иначе. Инженеры компании придумали альтернативное решение с «размазыванием секунды» на все 24 часа. Вместо единовременного добавления целой секунды оно подразумевает постепенное добавление миллисекунд в течение суток. Говоря точнее, к каждой секунде дня, в который должна быть добавлена високосная секунда, прибавляется значение 1/86400, получающееся в результате разделения одной секунды на все секунды суток, которых в них 24*60*60 = 86400.
▍ Возможные проблемы со временем в будущем
Следующая проблема может возникнуть 19 января 2038 года. В системах на базе Unix время хранится в виде количества секунд, прошедших с момента 00:00:00, 1 января 1970 года. Хранятся они в одной 32-битной целочисленной переменной, максимальное значение которой может достигать 2 147 483 647, что произойдёт 19 января 2038 года в 03:14:07. Решением этой проблемы будет переход на запись времени с помощью 64 бит, что значительно увеличит диапазон возможных значений (2^63-1). В этом случае следующая подобная проблема возникнет лишь в 292 277 026 596 году, то есть через 292 миллиарда лет. Операционные системы наверняка должным образом адаптируют, но, как известно, всегда найдётся тот, кто продолжит использовать старое недоработанное ПО и столкнётся с серьёзной проблемой.
▍ Отказ от високосной секунды
В результате многих лет дискуссий между различными органами по стандартизации в ноябре 2022 года на 27-й Генеральной конференции по мерам и весам было принято решение отказаться от високосной секунды к 2035 году или ранее. Начиная с 2035 года, дополнительные секунды перестанут быть проблемой. Международное бюро по мерам и весам разрешит отклонение UTC и UT1 до как минимум 2135 года в надежде, что учёные смогут выработать более эффективный метод учёта потерянного времени, или что компьютеры станут более совершенны в управлении его изменением.
▍ Итог
Как видите, оперировать временем не так просто, особенно в случае високосных секунд, когда фактическая работа того или иного решения в продакшене проверяется очень редко. Подобные проблемы неизбежно продолжат происходить, что не удивительно, так как это относительно низкоуровневые решения, которые используются во многих других продуктах. Тем не менее стоит признать, что наиболее предсказуемым и безопасным, похоже, является предложенный Google вариант с «размазыванием секунды». Он позволяет избежать неестественного скачка каждые X лет, применяя дополнительную секунду плавно без влияния на нормальную работу программ. Для тех, кому интересна тематика странностей, связанных со временем, рекомендую этот сайт.
P.S. В день добавления високосной секунды по возможности лучше отказаться от перемещений на самолёте и вообще оставаться дома. Что-то обязательно опять не будет работать — посмотрим, что на этот раз.