Введение
Месяц назад я устроился на работу в новую компанию, где моей обязаностью стала разработка сервисов для мониторинга. С некоторыми технологиями я не работал прежде, - с интересом приступил к изучению. За прошедше время в моём приложении для хранения заметок накопилась информация, которую я планирую оформить в цикл статей о мониторинге.
Проблематика
В процессе работы с Prometheus мы с коллегами обратили внимание на то, что некоторые метрики содержат значение NaN. Мной не было найдено единого источника информации по следующим вопросам: откуда значение берётся, почему NaN, как его интерпретировать.
Согласно модели данных Prometheus, значения метрик являются вещественными числами типа float64.
Описание арифметики для чисел с плавающей точкой задано в IEEE 754. Статья на wikipedia.
В общем в результате арифметических операций с такими числами вполне возможно получить следующие значения: -Inf, +Inf, NaN.
NaN - Not a number это численный тип данных, интерпретация которого не определена или не репрезентативна.
Опытным путём было обнаружено, что NaN содержится в статистических метриках, в которых вычисляются процентили, квантили, медианы, средние значения.
При вычислении процентиля используется операция деления. При отсутствии значений для вычисления метрики может происходить деление вещественных чисел на 0, что приводит к появлению значения NaN.
Экспертное мнение
Brian Brazil, один из создателей Prometheus, утверждает, что это абсолютно нормальное поведение. NaN не является отсутствием значения, если значение отсутствует, то метрика должна отсутствовать. Подробнее тут.
Ответ Brian Brazil по поводу NaN.
Более детальная информация о том, как появляется NaN.
Персональные наблюдения
Было замечено, что значения NaN преобладают для метрик, по которым давно не было активности. Например, метрика http_request_duration_milliseconds для Grafana содержит метку quantile="0.5", в ночное время, когда пользовательская активность отсутствует, значение этой метрики NaN.
То же самое можно обнаружить для метрики http_request_duration_milliseconds{quantile="0.5", status="401", handler="/login"}, показывающей медианное значение неавторизванных подключений на эндпоинт login. Т.к. таких попыток не было зарегистрировано, то при вычислении медианы мы получаем деление на 0, следовательно, увидим значение NaN.
С точки зрения логики и получаемых метрик это вполне адекватное решение, т.к. 0 количественный показатель, а NaN это не число.
NaN иногда может возникать в результате дефектов в экспортерах. Например, memcached в старых версиях не поддерживал команду touch, но экспортер всё равно отображал метрики с этим лейблом, что не является правильным, т.к. NaN это не отсутствие значения. Иными словами, если вы пишете свой экспортер, то учтите, что NaN != null; NaN != 0.
Подробнее о проблеме в memcached exporter.
Примечание
Grafana умеет отфильтровывать NaN, но, если у вас другой сценарий использования Prometheus, то можно воспользоваться сравнением > 0.
Допустим, есть метрика some_metric, для которой возвращается множество значений, среди которых есть NaN. Тогда достаточно составить запрос так, что бы он отображал метрики значения, которых больше 0.
То есть some_metric > 0.
Из-за свойства NaN, которое предполагает, что NaN != NaN, использовать выражение some_metric != NaN не имеет смысла.
Комментарии (7)
czz
16.11.2021 19:30+1Еще можно добавить, что VictoriaMetrics, которая в основном совместима с Prometheus, но не полностью, игнорирует метрики со значением NaN.
valyala
17.11.2021 21:58+1Вот тут есть подробности по поводу обработки NaN в VictoriaMetrics - https://medium.com/@romanhavronenko/victoriametrics-promql-compliance-d4318203f51e
SLASH_CyberPunk
Спасибо за статью, все хорошо пояснено, кроме "примечания", some_metric может быть равна нулю или быть меньше нуля...
Evreke Автор
Действительно, примечание оказалось не проработано. Спасибо, учту Ваше замечание и в будущем внесу необходимые уточнения.