Кардинальность: почему часть имён сгруппирована
Если на странице производительности или метрик вы увидели предупреждение о лимите различных значений, а в списке появилось <cardinality-limit> — эта страница объясняет, что произошло и что с этим делать.
Что случилось
Проект прислал больше различных значений какого-то поля, чем допускает потолок (по умолчанию 10 000 за час). Значения сверх потолка не отбрасываются, а группируются под общим именем <cardinality-limit>.
Ограничиваются поля, значения которых приходят от вашего кода и по природе открыты:
| Поле | Откуда берётся |
|---|---|
| Имя транзакции | transaction у Sentry SDK, имя корневого спана у OTLP |
| Окружение | environment в конфигурации SDK |
| Имя метрики | имя инструмента OTLP |
| Сервис | service.name в ресурсных атрибутах |
| Операция спана | op у Sentry SDK, span.kind/имя у OTLP |
| Хост | имя хоста (host.name в ресурсных атрибутах OTLP) |
Почему потолок вообще есть
Эти значения попадают в ключи сортировки ClickHouse и в группировку предагрегатов. Каждое новое значение создаёт отдельную строку агрегата — со своими состояниями перцентилей, — которая не схлопнётся ни с чем и проживёт всю ретенцию.
Десяток эндпойнтов — это десяток строк на пятиминутку. Сто тысяч «эндпойнтов» — сто тысяч строк на пятиминутку, и хранилище общее на все проекты инстанса. Потолок защищает не столько нас от вас, сколько соседние проекты друг от друга.
Настоящая причина — почти всегда переменная в имени
В подавляющем большинстве случаев это не злой умысел и не реальный рост, а идентификатор, попавший в имя:
GET /users/8812/profile ← должно быть GET /users/:id/profile
GET /orders/a7f3e9.../items ← должно быть GET /orders/:id/items
queue.process.job-88213 ← должно быть queue.process
environment = "web-07" ← должно быть environment = "production"
Узнать это можно по примерам в предупреждении: если значения отличаются только числом или хэшем — причина найдена.
Как исправить
Sentry SDK (любой язык). Имя транзакции задаётся фреймворковой интеграцией из шаблона маршрута. Если вы задаёте его вручную — подставляйте шаблон, а не готовый путь:
# плохо
sentry_sdk.set_transaction_name(f"GET /users/{user_id}/profile")
# хорошо
sentry_sdk.set_transaction_name("GET /users/:id/profile")
Если интеграция сама даёт «сырой» путь, обычно это значит, что маршрут не зарегистрирован во фреймворке (например, обработчик повешен на префикс). Зарегистрируйте маршрут — и имя станет шаблонным само.
OpenTelemetry. Имя спана должно быть низкокардинальным по спецификации: путь с идентификатором кладут в атрибут http.route или url.path, а не в имя. Проверьте, что инструментация не переопределяет имя вручную.
Окружение. Это production, staging, dev — короткий фиксированный список. Если туда попадает имя хоста, номер пода или версия — уберите их: для этого есть release и server_name.
Метрики. Имя инструмента — константа. Всё переменное идёт в атрибуты, а не в имя.
Пока чините
Данные не потеряны: суммарная нагрузка, длительности и число ошибок по проекту остаются верными — сгруппированные значения продолжают учитываться, просто под общим именем. Разбивка по хвосту вернётся, как только имена станут шаблонными: набор различённых значений начинается заново каждое окно.
Если у вас честно много значений
Бывает и так: большой монолит с тысячами настоящих маршрутов. Потолок поднимается переменной окружения:
GOTCHA_CARDINALITY_LIMIT=50000 # 0 — снять ограничение совсем
GOTCHA_CARDINALITY_WINDOW_SECONDS=3600
Поднимая, учитывайте цену: каждое различное значение — это отдельные строки предагрегатов на каждую пятиминутку, то есть место на диске и время слияний в ClickHouse.
У самой защиты тоже есть граница: она помнит значения в памяти процесса, и суммарно по всем проектам их не больше миллиона. При исчерпании бюджета сначала выбрасываются наборы проектов с истёкшим окном, а затем новые значения перестают запоминаться — защита продолжает работать, но не растёт. Сколько значений она помнит прямо сейчас, показывает gotcha_cardinality_tracked_values в /metrics. Число различных ИМЁН полей на проект тоже ограничено (200): имя поля приходит от отправителя ровно так же, как значение.
Что дальше
- Производительность — как читать страницу эндпойнтов.
- Метрики — как устроен приём метрик.
- Конфигурация — все переменные окружения.