Время в Raspberrypi - 3

“Да сколько уже можно про время в Raspberrypi?!” - скажут некоторые. Однако, несмотря на две предыдущие статьи, мне есть о чём написать. Опять.

Как я уже неоднократно писал, время можно получить двумя способами:

  • подключившись к интернету
  • используя RTC модуль

На некоторых станциях у меня стоит RTC модуль, на некоторых нет. Но я не особо переживал по этому поводу, так как станции имели постоянное подключение к интернету.

Запах приключений

Всё работало очень хорошо и стабильно, до тех пор пока я не обратил на странное поведения в логах.

Dec 24 03:20:07 raspberrypi java[447]: [1577147653365] observation time passed. skip 43466

Поначалу я не придал этому должного внимания, так как эта запись могла означать, что очередь на обработку сигналов переполнилась и последующее наблюдение пропустило своё окно. Не имеет смысл запускать задачу по наблюдению за спутником, если тот уже улетел.

Тем не менее несколько недель назад я заметил шаблон: сообщения появлялись только при старте приложения. Иногда одно, иногда несколько наблюдений отменялись. Однако, в этом не было ничего критичного. Все последующие наблюдения отрабатывали на ура.

Я решил выделить время и попытаться понять в чём же причина. И причина, конечно же, оказалось в системном времени. А именно в том, в какой момент системное время синхронизируется. Для простоты можно нарисовать простейшую диаграмму последовательности:

  • стартует система
  • запускается systemd-timesyncd и асинхронно запрашивает время
  • запускается r2cloud и стартует наблюдения
  • systemd-timesyncd получает асинхронный ответ и модифицирует системное время

Из такого описания явно следует проблема: все наблюдения рассчитаны на основе неправильного системного времени. А значит, когда оно изменится, должны быть отброшены.

Решение близко

К счастью, решение есть! Оно называется systemd-time-wait-sync.service. Этот сервис был добавлен сравнительно недавно в Debian Buster дистрибутив. Он, как следует из названия, дожидается, когда будет получен асинхронный ответ по протоколу NTP, и отправляет специальное событие time-sync.target.

Для того чтобы всё заработало, нужно сделать несколько шагов. Во-первых, нужно включить сервис:

sudo systemctl enable systemd-time-wait-sync

Можно проверить статус сервиса:

sudo systemctl status systemd-time-wait-sync
● systemd-time-wait-sync.service - Wait Until Kernel Time Synchronized
   Loaded: loaded (/lib/systemd/system/systemd-time-wait-sync.service; enabled; vendor preset: enabled)
   Active: active (exited) since Tue 2020-08-18 12:01:07 BST; 1 day 8h ago
     Docs: man:systemd-time-wait-sync.service(8)
  Process: 111 ExecStart=/lib/systemd/systemd-time-wait-sync (code=exited, status=0/SUCCESS)
 Main PID: 111 (code=exited, status=0/SUCCESS)

Aug 18 11:17:01 raspberrypi systemd-time-wait-sync[111]: adjtime state 5 status 40 time Tue 2020-08-18 10:17:01.037765 UTC

Во-вторых, необходимо, чтобы наш сервис, в данном случае r2cloud, стартовал только тогда, когда время синхронизировано. Для этого нужно в описание юнита добавить зависимость:

[Unit]
Description=R2Cloud Service
After=time-sync.target

Результат

В результате r2cloud теперь стартует после того, как время синхронизировано:

Aug 18 12:01:07 raspberrypi systemd[1]: Started Wait Until Kernel Time Synchronized.
Aug 18 12:01:07 raspberrypi systemd[1]: Reached target System Time Synchronized.
Aug 18 12:01:07 raspberrypi systemd[1]: Started R2Cloud Service.

Все наблюдения запускаются в своё время и r2cloud станции по всему миру стали чуть эффективнее работать. Ура!