Три часовых пояса в одной таблице
Расхождение было ровно в одну плавку из сорока семи - то есть два процента, то есть именно столько, сколько принято списывать на «особенности выгрузки». Списывал полгода и я.
Задача
Суточная сводка по расходу графитированных электродов считается на плавку: расход делится на число плавок. В сводке стояло 46 плавок, в журнале мастера - 47. Удельный расход при этом получался на два процента выше правды, и ровно на эти два процента месяц к месяцу не сходился с нормой.
Ко мне пришли со словами «сводка теряет плавки». Я проверил логи выгрузки, увидел нулевое число ошибок и ответил, что ничего не теряется. Это был правильный ответ на неправильно заданный вопрос.
Как мерил
Поначалу я сравнивал суммы: сумма по сводке против суммы по журналу, день за днём. Так расхождение видно, а причина - нет, и три попытки подряд не дали ничего. После третьей я остановился и сменил приём: взял один идентификатор плавки и выписал про него все метки времени, какие нашёл в трёх системах.
| Источник | Зона метки | Чем ставится метка | Сдвиг |
|---|---|---|---|
| Архив историка | UTC, зона указана | момент приёма значения сервером | 0 |
| Журнал плавок | локальная, зона не указана | подтверждение оператором вручную | +3 ч 0…6 мин |
| Лабораторная выгрузка | локальная, зона не указана | момент печати протокола | +2 ч 53 мин |
Третья строка выглядела абсурдно: лаборатория отставала от журнала плавок на семь минут при одной и той же локальной зоне. Причина скучная - на сервере лаборатории не был настроен синхронизатор времени, и часы ушли на семь минут за год с лишним. Никто не замечал, потому что лабораторию никто не сверял поминутно.
Что оказалось неправдой: «ничего не теряется»
Неправдой было моё собственное утверждение «данные не теряются, значит всё в порядке». Данные действительно не терялись ни на одном шаге. Плавка № 47 начиналась в 23:58 локального времени, а сводка за сутки резалась по границе UTC - и эта плавка штатно попадала в предыдущие сутки, где её уже посчитали. В месячной сумме всё сходилось; разъезжались только суточные числа, а по ним и считали удельный расход.
Вторая неправда была архитектурной: я считал, что метка времени - это свойство события. На самом деле у каждой из трёх систем метка означает разное. Историк пишет, когда значение дошло до сервера, и на загруженной очереди это до 90 секунд после самого события. Журнал плавок пишет, когда человек нажал кнопку. Лаборатория пишет, когда напечатался протокол.
Чем разделил
Чтобы не спорить о зонах по справочникам, я сделал синтетический тик: канал, в который раз в минуту пишется значение, равное номеру минуты. Этот канал я прописал во все три системы на двое суток.
Дальше сверка стала арифметикой. Тик с номером 1 440 должен лежать в полуночной точке каждого источника; где он лежал на самом деле, там и был сдвиг. Любая версия про «летнее время» или «ошибку выгрузки» этим опытом отсекается сразу: тик не зависит ни от технологии, ни от оператора, ни от нагрузки на сеть.
Минутный тик с тех пор живёт в архиве постоянно и стоит мне один тег из одиннадцати тысяч. Два раза за год он ловил расползание часов на других серверах раньше, чем это замечали люди.
Вывод
В любой выгрузке, где больше одного источника, рядом с временем обязана стоять колонка «откуда метка и в какой зоне». Без неё таблица выглядит согласованной и не является таковой.
И второе, более общее: суточная граница - это не факт, а чьё-то решение. Пока не сказано, по какой зоне режутся сутки и какое событие считается началом плавки, любые «на два процента больше нормы» обсуждать бессмысленно. После правки граница в сводке стала локальной, расхождение с журналом мастера ушло в ноль, а подбор ближайшей точки по времени, которым я грешил в ноябре, я переписал на явное окно.