Введение

Месяц назад я устроился на работу в новую компанию, где моей обязаностью стала разработка сервисов для мониторинга. С некоторыми технологиями я не работал прежде, - с интересом приступил к изучению. За прошедше время в моём приложении для хранения заметок накопилась информация, которую я планирую оформить в цикл статей о мониторинге.

Проблематика

В процессе работы с 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.

Issue на гитхаб.

Примечание

Grafana умеет отфильтровывать NaN, но, если у вас другой сценарий использования Prometheus, то можно воспользоваться сравнением > 0.

Допустим, есть метрика some_metric, для которой возвращается множество значений, среди которых есть NaN. Тогда достаточно составить запрос так, что бы он отображал метрики значения, которых больше 0.

То есть some_metric > 0.

Из-за свойства NaN, которое предполагает, что NaN != NaN, использовать выражение some_metric != NaN не имеет смысла.

Комментарии (7)


  1. SLASH_CyberPunk
    16.11.2021 12:59

    Спасибо за статью, все хорошо пояснено, кроме "примечания", some_metric может быть равна нулю или быть меньше нуля...


    1. Evreke Автор
      16.11.2021 13:33

      Действительно, примечание оказалось не проработано. Спасибо, учту Ваше замечание и в будущем внесу необходимые уточнения.


  1. amarao
    16.11.2021 15:42

    Я скажу ещё интереснее, NaN!=NaN, более того, это самый простой способ проверить на NaN. Если метрика не равна сама себе - это NaN.


    1. lllamnyp
      16.11.2021 18:57

      Тогда можно делать тест some_metric == some_metric чтобы фильтровать NaN


      1. amarao
        16.11.2021 18:58

        Да, вполне будет рабочим.


  1. czz
    16.11.2021 19:30
    +1

    Еще можно добавить, что VictoriaMetrics, которая в основном совместима с Prometheus, но не полностью, игнорирует метрики со значением NaN.


    1. valyala
      17.11.2021 21:58
      +1

      Вот тут есть подробности по поводу обработки NaN в VictoriaMetrics - https://medium.com/@romanhavronenko/victoriametrics-promql-compliance-d4318203f51e