OpenTelemetry Tail Sampling: قلل تكلفة الـ traces من غير ما تعمي نفسك
مستوى القارئ: متوسط
هتخرج من المقال بإعداد عملي يقلل حجم الـ traces اللي بتتبعت للـ backend، مع الاحتفاظ بالأخطاء والطلبات البطيئة اللي تهمك فعلاً.
المشكلة باختصار
لو عندك 8 خدمات بتستقبل 2 مليون request يوميًا، وإنت بتصدر 100% من الـ traces، تكلفة التخزين والبحث هتزيد بسرعة. الطريقة الشائعة هي head sampling بنسبة ثابتة، مثلاً 10%. الطريقة دي سهلة، لكنها بتفشل في نقطة مهمة: ممكن ترمي trace فيه error لأن القرار اتاخد في أول الطلب قبل ما تعرف نهايته.
الافتراض هنا إن عندك microservices بتصدر traces عبر OTLP، وعندك OpenTelemetry Collector قبل Jaeger أو Tempo أو vendor مدفوع. لو الـ traffic عندك أقل من 20 ألف request يوميًا، غالبًا مش محتاج التعقيد ده دلوقتي.
الفكرة: القرار يتاخد بعد ما التريس يخلص
Tail Sampling معناه إن قرار الاحتفاظ بالـ trace بيتاخد بعد ما Collector يشوف كل أو معظم الـ spans. ركز في الفرق: بدل ما تقول من البداية “خد 10% وخلاص”، تقول “احتفظ بكل الأخطاء، واحتفظ بكل request أبطأ من 5 ثواني، وخد 5% من الطلبات السليمة”.
مثال بسيط: عندك checkout service. من 100 ألف trace في الساعة، 97 ألف ناجحين وسريعين، 2500 بطيئين، و500 فيهم أخطاء. بدل ما تخزن 100 ألف، ممكن تخزن 8 آلاف تقريبًا: كل الأخطاء، كل البطيء، وعينة صغيرة من السليم. المكسب تقديريًا 92% تقليل في حجم التريس. الخسارة إنك مش هتشوف كل request ناجح سريع بالتفصيل.
إعداد عملي لـ OpenTelemetry Collector
أفضل طريقة تبدأ بيها هي gateway collector واحد أو أكثر قدام الـ backend. استخدم memory_limiter قبل tail_sampling، واستخدم batch بعده. المثال التالي يحتفظ بالأخطاء، والطلبات فوق 5 ثواني، و5% من الباقي.
receivers:
otlp:
protocols:
grpc:
http:
processors:
memory_limiter:
check_interval: 1s
limit_mib: 1024
tail_sampling:
decision_wait: 10s
num_traces: 50000
policies:
- name: keep-errors
type: status_code
status_code:
status_codes: [ERROR]
- name: keep-slow-traces
type: latency
latency:
threshold_ms: 5000
- name: keep-small-baseline
type: probabilistic
probabilistic:
sampling_percentage: 5
batch:
exporters:
otlp:
endpoint: tempo:4317
tls:
insecure: true
service:
pipelines:
traces:
receivers: [otlp]
processors: [memory_limiter, tail_sampling, batch]
exporters: [otlp]
اللي بيحصل فعلاً إن Collector يستنى decision_wait عشان يجمع spans لنفس trace. بعد كده يطبق السياسات. لو أي policy match، التريس يتصدر. لو مفيش match، التريس يترمي.
مثال واقعي بالأرقام
شركة SaaS صغيرة عندها API بيعمل 600 request/second وقت الذروة. متوسط trace فيه 12 span، ومتوسط span بعد الضغط حوالي 700 byte. ده معناه تقريبًا 6 MB/second من بيانات التريس الخام قبل أي overhead. على مدار يوم شغل تقيل، الرقم ممكن يوصل لمئات الجيجابايت.
بعد تطبيق السياسات السابقة، افترض إن 1% أخطاء، و3% بطيء، و5% baseline من الناجح. هتحتفظ تقريبًا بـ 9% من التريس. لو كنت بتدفع على ingestion، الفرق ممكن يبقى من 300GB يوميًا إلى 27GB يوميًا. الأرقام تقديرية، لكنها كفاية توضح قرار هندسي: انت مش بتقلل الرؤية عشوائيًا، انت بتختار إشارات أهم.
الـ trade-off هنا
Tail Sampling مش مجاني. بتكسب تكلفة أقل ورؤية أفضل للأخطاء. بتخسر بساطة التشغيل، وبتحتاج ذاكرة أعلى لأن Collector لازم يحتفظ بتريس مؤقتًا قبل القرار. لو decision_wait عالي جدًا، الذاكرة تزيد. لو قليل جدًا، ممكن القرار يتاخد قبل وصول كل spans.
ابدأ بـ 10 ثواني لو معظم requests عندك أقل من 2 ثانية. لو عندك jobs طويلة أو request بتعدي 30 ثانية، افصلها بسياسة مختلفة أو pipeline منفصل. لا ترفع num_traces عشوائيًا. راقب dropped spans وذاكرة Collector بعد أول deploy.
متى لا تستخدم هذه الطريقة
متستخدمش Tail Sampling لو عندك monolith بسيط وtraffic قليل. head sampling بنسبة 10% غالبًا كفاية. ومتستخدموش لو Collector عندك resource محدود جدًا، لأن tail sampling stateful وبيحتاج ذاكرة. ومتستخدموش كبديل للـ logs أو metrics. الـ traces بتجاوب “الطلب مشي فين”، لكنها مش بديل لإنذار CPU أو log فيه stack trace كامل.
مصادر راجعتها
- OpenTelemetry Sampling Concepts: https://opentelemetry.io/docs/concepts/sampling/
- OpenTelemetry Collector Processors: https://opentelemetry.io/docs/collector/components/processor/
- OpenTelemetry Tail Sampling sample configuration: https://opentelemetry.io/docs/demo/sample-configurations/tail-sampling-service-criticality/
الخطوة التالية
افتح إعداد الـ Collector عندك، وطبّق سياسة واحدة فقط أولاً: احتفظ بكل traces اللي فيها ERROR. بعد 24 ساعة، قارن ingestion volume وعدد الأخطاء المرئية قبل ما تضيف latency أو probabilistic sampling.