В восьмой главе рассматриваются проблемы, которые могут возникнуть в распределенной системе. Второй раздел посвящен вопросу синхронизации времени.
Ненадежные часы
В распределенной системе у каждого сервера в сети свои аппаратные часы, поэтому время на разных серверах всегда будет хоть немного, но отличаться. Часы можно синхронизировать до некоторой степени. Самый распространенный
механизм для этого — NTP (network time protocol, сетевой протокол времени), позволяющий компьютерам подстраивать свои часы в соответствии с временем, передаваемым группой серверов точного времени.
Виды часов
📌 Часы истинного времени возвращают текущие дату и время в соответствии с каким-то календарем. Именно они обычно синхронизируются с помощью NTP.
Например, в Java есть метод
System.currentTimeMillis(). Он возвращает количество миллисекунд, прошедших с начала эры UNIX — полуночи 1 января 1970 года по UTC. Этот метод представляет собой часы истинного времени. 📌 Монотонные часы подходят для измерения продолжительности интервалов времени, например, времени отклика сервиса. Они называются монотонными, потому что гарантированно движутся вперед, в отличие от часов истинного времени, которые могут перепрыгивать назад во времени по итогам синхронизации.
Можно посмотреть показания монотонных часов в какой-то момент времени, выполнить какие-либо действия, а затем посмотреть их показания снова. Разница между двумя полученными значениями соответствует времени, прошедшему между двумя проверками. Однако абсолютные показания монотонных часов смысла не имеют: это может быть количество наносекунд с момента загрузки компьютера или что-то такое же случайное. Не имеет смысла сравнивать показания монотонных часов с двух разных компьютеров, поскольку их значения различаются.
Например, в Java метод
System.nanoTime() является примером монотонных часов.Потеря данных при репликации
При репликации с несколькими лидерами или без лидера часто используется стратегия разрешения конфликтов «выигрывает последний» (last write wins).
Допустим у нас два лидера и реплика. На каждом лидере операции записи получают метку даты/времени в соответствии с часами этого лидера и затем реплицируются на остальные узлы.
На реплике при получении конфликтующих операций записи от разных лидеров победителем выбирается запись с максимальным значением даты/времени. Соответственно если у лидеров немного расходятся часы, то победителем может быть выбрана запись от лидера со спешащими часами, а запись от лидера с отстающими часами будет отброшена, хотя на самом деле могла произойти позже и быть актуальной. Более подробно — на картинке к посту.
Паузы на лидере при репликации
Допустим, есть БД с репликацией с одним лидером. Принимать операции записи способен только лидер. Откуда узел знает, что он все еще является лидером (а не был объявлен остальными узлами неработающим) и может спокойно принимать операции записи?
Одна из возможностей — получить договор аренды (lease). Это некое подобие блокировки с заданным временем ожидания. Быть владельцем аренды одновременно может только один узел, поэтому при получении договора узел знает, что стал лидером на некоторое время, до истечения аренды.
Чтобы продолжать оставаться лидером, узел должен периодически возобновлять договор перед истечением срока его действия. В случае отказа узел прекращает возобновлять договор аренды и функции лидера передаются другому узлу.
Однако если в процессе выполнения программы на лидере после проверки договора, но до обработки запроса возникла пауза, то возможна ситуация, когда за время этой паузы договор аренды истечет, лидером станет другой узел, но первый не будет знать об этом и выполнит обработку запроса.
Примерам такой паузы может быть:
- пауза на сборку мусора
- переключение контекста операционной системы на другой поток выполнения или переключении гипервизора на другую виртуальную машину
Современные сборщики фокусируются на том, чтобы делать очень короткие паузы даже на кучах большого обьема.
#кабанчик #сисдиз
