Tail Sampling بالعربي: قلل تكلفة الـ Tracing من غير ما تعمي نفسك
مستوى القارئ: متوسط
هتخرج من المقال بإعداد عملي يخلي OpenTelemetry Collector يحتفظ بالـ traces المهمة، بدل ما يبعث كل الضوضاء لنظام التخزين.
المشكلة باختصار
الطريقة الشائعة إنك تشغل tracing وتبعت كل span إلى Tempo أو Jaeger أو أي backend. الطريقة دي بتفشل لما حجم الطلبات يكبر. هتشوف كل حاجة، لكن هتدفع في storage وnetwork وquery latency، وبعد شهر هتبدأ تقلل retention بدل ما تصلح التصميم.
الافتراض إن عندك خدمة API بتستقبل حوالي 5 ملايين request يوميًا، وكل request ينتج trace متوسطه 12 span. لو احتفظت بكل شيء، أنت بتتعامل مع 60 مليون span يوميًا. حتى لو كل span بعد الضغط حجمه 500 بايت فقط، ده حوالي 30GB بيانات خام يوميًا قبل metadata والفهارس.
الفكرة: قرار العينة بعد ما الـ trace يكتمل
ركز في الفرق. Head sampling بياخد القرار في بداية الطلب. يعني ممكن يرمي request فشل في آخر خطوة لأنه كان اختار من البداية إنه لا يحتفظ به. Tail sampling يستنى شوية، يجمع spans الخاصة بنفس trace_id، ثم يقرر.
مثال بسيط: عندك طلب checkout بدأ طبيعي، وبعد 1.8 ثانية فشل في payment provider. لو استخدمت sampling عشوائي 5% من البداية، غالبًا الطلب ده هيختفي. مع tail sampling تقدر تقول: احتفظ بكل ERROR، واحتفظ بكل trace أبطأ من 2000ms، وخد 5% فقط من النجاح العادي.
علميًا، الـ OpenTelemetry Collector مبني من receivers وprocessors وexporters داخل pipelines. الـ processor يقدر يغير أو يفلتر البيانات قبل التصدير، والـ tail_sampling processor في توزيعة contrib يقيّم traces بناءً على policies مثل status_code وlatency وprobabilistic.
إعداد عملي قابل للنسخ
استخدم الإعداد التالي كبداية. هو يستقبل traces عبر OTLP، يحمي الذاكرة، يطبق tail sampling، ثم يرسل النتائج إلى Tempo. عدّل endpoint حسب بيئتك.
receivers:
otlp:
protocols:
grpc:
endpoint: 0.0.0.0:4317
http:
endpoint: 0.0.0.0:4318
processors:
memory_limiter:
check_interval: 5s
limit_mib: 1024
spike_limit_mib: 256
tail_sampling:
decision_wait: 20s
num_traces: 50000
expected_new_traces_per_sec: 2500
decision_cache:
sampled_cache_size: 100000
non_sampled_cache_size: 100000
policies:
- name: keep-errors
type: status_code
status_code:
status_codes: [ERROR]
- name: keep-slow-traces
type: latency
latency:
threshold_ms: 2000
- name: sample-success-noise
type: probabilistic
probabilistic:
sampling_percentage: 5
batch:
timeout: 5s
send_batch_size: 1024
exporters:
otlp/tempo:
endpoint: tempo:4317
tls:
insecure: true
service:
pipelines:
traces:
receivers: [otlp]
processors: [memory_limiter, tail_sampling, batch]
exporters: [otlp/tempo]