Оновлення OpenTelemetry Sampling

Вступ

OpenTelemetry опублікував версію 1.0 своєї специфікації трасування більше чотирьох років тому, і в той же рік W3C TraceContext Level 1 був опублікований з статусом рекомендації W3C. Ми як спільнота і ми, як індустрія спостережуваності, отримали два нових стандарти для розподіленого трасування. Звичайно, ми ще не закінчили.

Ступінь дискретизації вибірки (Sampling) є важливою темою специфікації Tracing SDK, і оригінальна специфікація включала набір вбудованих вибірок, AlwaysOn, AlwaysOff, ParentBased та TraceIdRatioBased, а також інтерфейс, що дозволяє реалізовувати нові вибірки, в основному Jaeger Remote.

Однак у специфікації Tracing 1.0 залишився помітний “TODO”, що стосується ймовірнісної вибірки, який впливав на вибірку TraceIdRatioBased. TODO попереджав користувачів специфікації про “неконсистентні” результати, що вибірки TraceIdRatioBased були безпечні лише для налаштування кореневих відрізків.

Це означало, що користувачі OpenTelemetry не могли безпечно налаштувати незалежні політики ймовірнісної вибірки в розподіленій системі, оскільки специфікація не охоплювала того, як досягти консистентності. Ця функція, можливість налаштування політик вибірки з нерівною ймовірністю в межах трасування і все ще очікувати повні трасування, є тим, що користувачі очікують; це дозволяє власникам сервісів налаштовувати незалежні обмеження на обсяг зібраних даних трасування в системі.

Консистентність на прикладі

Щоб зрозуміти, чому консистентність важлива, розглянемо систему з фронтендом і двома бекенд-сервісами, кешем і сховищем. Фронтенд обробляє запити високої вартості, тому запити фронтенда вибираються на 100%. Кореневий відрізок є значущим, оскільки помилки видимі кінцевому користувачу, тому він є основою вимірювання SLO в цьому прикладі, і оператор системи готовий збирати кожен відрізок.

Сервіс кешу отримує відносно великий обсяг запитів, тому для економії витрат на спостережуваність цей сервіс налаштований на вибірку 1 з 1000 трейсів. Через високий рівень запитів ця політика 0,1% забезпечує, що сервіс кешу виробляє достатньо трейсів для багатьох сценаріїв спостережуваності.

Сервіс сховища отримує відносно низький обсяг запитів у порівнянні з кеш-сервером, але все ще багато запитів у порівнянні з фронтенд-сервісом; Сховище налаштоване на вибірку 1 з 10 трейсів.

Коли ми говоримо про консистентність в розподіленому трасуванні, мета полягає в тому, щоб забезпечити, що коли семплер з найменшою ймовірністю (тут 0,1%) робить вибірку, семплери з вищою ймовірністю приймають таке саме рішення. Ось властивості, на які ми можемо покластися в цій конфігурації:

  • 100% запитів фронтенда буде зібрано
  • 1 з 10 трейсів буде складатися з запитів фронтенда та сховища
  • 1 з 1000 трейсів буде повною.

Проблеми з TraceIdRatioBased

Семплер на основі ймовірностей TraceIdRatioBased від OpenTelemetry з самого початку мав бути консистентним, проте робоча група не могла дійти згоди щодо конкретних деталей. Проблема консистентності вибірки була помʼякшена тим, що вибірка тільки кореневих елементів була нормою для сучасних систем відстеження з відкритим кодом і моделлю, яку прийняв Jaeger.

Частина назви, що походить від слова «співвідношення» (“ratio-based”), натякає на форму вирішення проблеми послідовного відбору зразків:

  1. Розгляньте значення TraceID як N-бітне випадкове значення
  2. Обчисліть N-у ступінь двійки
  3. Помножте ступінь двійки на співвідношення, отримуючи значення “порогу”
  4. Порівняйте TraceID з пороговим значенням, отримуючи послідовне рішення.

Нам було важко дійти згоди щодо такого рішення через більш глобальне питання. Які частки TraceID ми можемо вважати випадковими? Без основних вимог до випадковості OpenTelemetry не міг визначити консистентне рішення для вибірки.

За відсутності чітких вимог до випадковості загальноприйнятим підходом є використання хеш-функції. Використання Hash(TraceID) для отримання N-бітної випадковості працює досить добре, якщо хеш-функція якісна, але цей підхід не підходить для специфікації багатомовного SDK.

Деталі тут складні. Скільки бітів TraceID було б достатньо? Чи може кожен мовний SDK ефективно реалізувати необхідну логіку?

Представляємо W3C TraceContext Level 2

Проєкт OpenTelemetry звернувся до робочої групи W3C Trace Context з приводу цієї важливої проблеми. Чи можемо ми, включаючи системи трасування OpenTelemetry та інші системи, домовитися про те, скільки бітів TraceID є випадковими?

Специфікація W3C TraceContext Level 2, яка наразі є Candidate Recommendation Draft, відповідає на це питання новим значенням Random Trace Flag. З цим прапорцем нова специфікація W3C вимагає, щоб найменш значущі 56 бітів TraceID були “достатньо” випадковими. Це означає, наприклад, що коли ми представляємо TraceID як 32 шістнадцяткові цифри, останні 14 цифр справа є випадковими. Представленні як 16 байтів останні 7 байтів справа є випадковими.

OpenTelemetry приймає проєкт рекомендації W3C TraceContext Level 2 як основу для узгодженого відбору зразків. Усі SDK встановлюватимуть прапорець Random і забезпечуватимуть, щоб TraceID, які вони генерують, стандартно мали необхідні 56 бітів випадковості.

Узгоджений поріг відбору проб для відхилення

Повертаючись до узгодженої логіки “на основі співвідношення”, тепер ми можемо отримати 56 бітів випадковості з TraceID, і процес прийняття рішень, описаний вище, вимагає порогу для порівняння.

Було ще одне, що ми як група хотіли для специфікації відбору проб на основі ймовірності: спосіб для SDK повідомляти про свої рішення щодо відбору проб, як один одному у TraceContext, так і на шляху збору після завершення відрізків.

Нова специфікація дозволяє компонентам OpenTelemetry спілкуватися про “наскільки багато проб” потрапило у відрізок. Це підтримує багато складних архітектур відбору проб:

  • Надійні оцінки кількості відрізків
  • Узгоджений відбір проб з обмеженням швидкості
  • Адаптивний відбір проб
  • Узгоджений багатоступеневий відбір проб.

Ключові моменти нашого підходу наведені нижче, але зацікавлені читачі можуть ознайомитися з повною специфікацією.

Виходячи з кількості бітів, залишилося не так багато для специфікації. Однак ми хотіли підхід, який:

  • Підтримує як лексикографічне, так і числове порівняння
  • Мінімізує накладні витрати TraceContext
  • Залишається зрозумілим для досвідчених користувачів OpenTelemetry.

Наш підхід базується на тому, що ми називаємо порогом відбору для відхилення. Виходячи з випадкового значення R і порогу відбору T, ми приймаємо позитивне рішення про відбір проб, коли T <= R. Еквівалентно, ми приймаємо негативне рішення про відбір проб, коли T > R.

Стандартно, значення порогу 0 відповідає 100% відбору проб, тому користувачі можуть легко розпізнати цю конфігурацію. Абстрактно, як R, так і T мають діапазон 56 бітів, який можна представити як беззнакові цілі числа, зрізи по 7 байтів або рядки з 14 шістнадцяткових цифр.

OpenTelemetry TraceState

Специфікація W3C TraceContext визначає два заголовки HTTP для використання в розподілених системах трасування: заголовок tracecontext, який містить версію, TraceID, SpanID та прапорці, та tracestate, який підтримує «специфічні для постачальника» доповнення до контексту. SDK OpenTelemetry Tracing незабаром почнуть додавати запис з ключем «ot» у заголовок tracestate. Ось приклад:

tracestate: ot=th:0

У конфігурації 100% вибірки SDK OpenTelemetry Tracing вставляють ot=th:0 у TraceState. Значення TraceState, після введення в контекст, поширюються та записуються в даних відрізка OpenTelemetry. За задумом, нове значення OpenTelemetry TraceState кодується та передається лише для позитивних рішень щодо вибірки; у результаті негативних рішень щодо вибірки заголовок tracestate не зʼявляється.

У цьому випадку пороги вибірки логічно представляють 14 шістнадцяткових цифр або 56 бітів інформації.

Однак, щоб ефективно передати поріг вибірки, ми відкидаємо нульові біти (за винятком самого 0). Це дозволяє нам обмежити точність порогу до менш ніж 56 бітів, що знижує кількість байтів на контекст. Ось приклад tracestate, що вказує на 1% вибірки, обмеженої 12 бітами точності:

tracestate: ot=th:fd7

Ми приділили багато уваги зворотній сумісності, але також хотіли бути впевнені, що завжди зможемо використовувати заявлений поріг вибірки для екстраполяції в надійному статистичному контексті. З огляду на це, у нашій специфікації є ще одне значення OpenTelemetry TraceState: спосіб забезпечити явну випадковість у заголовку tracestate.

Щоб забезпечити послідовну вибірку та продовжити використання невипадкових ідентифікаторів TraceID, наприклад, користувачі можуть обрати явну випадковість:

tracestate: ot=rv:abcdef01234567

Явні значення випадковості мають кілька інших застосувань, наприклад:

  • Досягнення послідовного вибіркового спостереження по декількох трейсах шляхом застосування одного і того ж явного значення випадковості до незалежних коренів трейсів
  • Перетворення зовнішніх рішень щодо послідовної вибіркової вибірки (наприклад, на основі хеш-функції) у рішення OpenTelemetry щодо послідовної вибіркової вибірки.

Як приклад, ми оновили процесор probabilisticsampler OpenTelemetry Collector-Contrib, щоб зберегти своє оригінальне рішення щодо послідовної вибірки та все ще кодувати ймовірність вибірки в OpenTelemetry TraceState. Це робиться шляхом синтезу явного значення випадковості з хеш-функції, яку він використовує.

Попереду

Ця публікація охоплює важливе оновлення специфікації OpenTelemetry Tracing, що дозволяє нове покоління вибіркових механізмів для SDK OpenTelemetry та компонентів Collector.

Ось кілька корисних посилань, включаючи чотири пропозиції щодо вдосконалення OpenTelemetry, які проклали наш шлях:

Нижче наведено наші основні технічні документи:

Востаннє змінено October 17, 2025: [uk] Blog Sampling milestones (b8c7a0cf)