به گزارش از وبسایت cncf، در مقیاس Atlassian صدها microservice به هم‌پیوسته که در چندین منطقه جغرافیایی پخش شده‌اند، باعث می‌شوند هر incident تولیدی حجم بسیار زیادی از telemetry ایجاد کند. مشکل اصلی اینجاست که پیدا کردن علت ریشه‌ای در میان این حجم از داده‌ها هنوز تا حد زیادی به تجربه انسانی، حدس و گمان و تطبیق دستی اطلاعات وابسته است. 😓

یک workflow معمول برای root cause analysis امروز چیزی شبیه این است: مهندس on-call هشدار دریافت می‌کند، dashboard مربوط به metrics را باز می‌کند، یک anomaly در error rate یا latency می‌بیند، بعد به ابزار log می‌رود تا exceptionها را در همان بازه زمانی جست‌وجو کند، سپس UI مربوط به tracing را باز می‌کند تا مسیر هر request را بررسی کند. در نهایت، الگوها را در این سه نمای جداگانه کنار هم می‌گذارد، در ذهنش یک فرضیه درباره محل مشکل می‌سازد و بعد در dependency graph سرویس‌ها عقب‌گرد می‌کند تا آن را تأیید کند.

این فرایند هم زمان‌بر است و هم از نظر ذهنی هزینه زیادی دارد. موفقیت در آن وابسته به این است که فرد از قبل بداند کدام dashboard را باید ببیند، چه log queryهایی را اجرا کند و کدام سرویس‌ها upstream سرویس خراب‌شده هستند. مهندسان ارشد که سال‌ها تجربه دامنه‌ای دارند، ممکن است این کار را در چند دقیقه انجام دهند. اما برای دیگران، این مسیر بسیار طولانی‌تر است و در یک incident که روی کاربر اثر می‌گذارد، هر دقیقه اهمیت دارد. ⏱️

ما یک سؤال ساده مطرح کردیم: اگر مرحله تولید فرضیه را کاملاً خودکار کنیم چه می‌شود تا پاسخ‌گوها بتوانند مستقیم سراغ validation و resolution بروند؟

رویکرد ما: RCA را به‌عنوان یک مسئله همبستگی چند-سیگناله در نظر بگیرید. ایده پشت سیستم خودکار RCA ما این است که root cause analysis اساساً یک مسئله correlation در سه بُعد است: نوع signal شامل metrics، logs و traces؛ زمان، یعنی anomalyهایی که هم‌زمان رخ می‌دهند احتمالاً به هم مرتبط‌اند؛ و topology، یعنی خطاها در امتداد یال‌های dependency سرویس‌ها منتشر می‌شوند. اگر بتوانیم anomalyها را به‌صورت مستقل در هر signal تشخیص دهیم، آن‌ها را روی یک timeline مشترک هم‌راستا کنیم و سپس از طریق dependency graph شناخته‌شده سرویس‌ها ردگیری کنیم، می‌توانیم فرضیه‌های رتبه‌بندی‌شده‌ای درباره منشأ خطا و نحوه انتشار آن برای ایجاد symptomهای قابل مشاهده توسط کاربر تولید کنیم. 🧩

این سیستم عمداً به‌صورت modular طراحی شده است. هر روش anomaly detection یک component قابل‌وصل است و correlation engine روی eventهای anomaly نرمال‌سازی‌شده کار می‌کند، بدون توجه به این‌که کدام detector آن‌ها را تولید کرده است. این یعنی می‌توانیم به‌تدریج اجزای جداگانه را بهبود دهیم، یک مدل آماری را با یک ML model جایگزین کنیم، یا یک نوع signal جدید اضافه کنیم، بدون این‌که کل pipeline را از نو بسازیم.

معماری: از telemetry خام تا فرضیه‌های رتبه‌بندی‌شده

گام 1: محدود کردن جست‌وجو با استفاده از service topology

وقتی یک incident شناسایی می‌شود، اولین کار ما کوچک‌کردن blast radius است. به‌جای تحلیل همه سرویس‌های platform، dependency graph سرویس‌ها را query می‌کنیم تا مجموعه سرویس‌هایی را که در مسیر call تجربه کاربری degraded قرار دارند شناسایی کنیم. این کار یک subgraph متمرکز به ما می‌دهد؛ معمولاً شامل ده‌ها سرویس به‌جای صدها سرویس، که احتمالاً خطا در آن‌ها رخ داده است. برای این کار از service mapهای مشتق‌شده از OpenTelemetry استفاده می‌کنیم. dependency graph از رابطه‌های parent-child در سطح span که در traffic تولیدی دیده می‌شود ساخته می‌شود و تصویری real-time از این‌که سرویس‌ها واقعاً چگونه با هم ارتباط دارند به ما می‌دهد، نه صرفاً آن‌طور که در documentation نوشته شده است.

گام 2: تشخیص مستقل anomaly در هر signal

بعد از شناسایی سرویس‌های مرتبط، برای هر telemetry signal ماژول‌های تخصصی anomaly detection را اجرا می‌کنیم:

Metrics (RED signals): برای هر endpoint سرویس، rate، error rate و duration را با ترکیبی از روش‌های آماری پایش می‌کنیم. Median absolute deviation برای تشخیص جهش‌ها استفاده می‌شود، در حالی که percentile bandها انحراف‌های پایدار را شناسایی می‌کنند. وقتی یک metric از آستانه dynamic خود عبور کند، یک event نرمال‌سازی‌شده anomaly همراه با severity score، مقدار مشاهده‌شده و baselineای که از آن فاصله گرفته است تولید می‌شود.

Distributed traces: traceهایی را که از سرویس‌های درگیر عبور می‌کنند تحلیل می‌کنیم تا anomalyهای ساختاری مانند exceptionهای غیرمنتظره، الگوهای جدید انتشار خطا و جهش latency در spanهای مشخص را پیدا کنیم. detector مبتنی بر trace هم از روش‌های آماری مثل نقض percentileهای latency استفاده می‌کند و هم از pattern analysis برای شناسایی نوع exceptionهایی که با بازه incident هم‌بستگی دارند. هر trace مشکوک یک event تولید می‌کند که به سرویس و timestamp مشاهده anomaly متصل است.

Logs: روی جریان log سرویس‌های درگیر clustering techniques اعمال می‌کنیم تا clusterهای خطای جدید یا نادر که در بازه incident ظاهر شده‌اند را برجسته کنیم. چالش اصلی اینجا حجم log است. در مقیاس بالا نمی‌توان هر خط را به‌صورت naïve بررسی کرد. ما از embedding-based clustering استفاده می‌کنیم تا ورودی‌های log از نظر معنایی مشابه را گروه‌بندی کنیم و clusterهایی را که نسبت به توزیع معمول خطای سرویس به‌طور آماری جدید هستند علامت بزنیم.

هر detector eventهایی با یک schema مشترک تولید می‌کند:

{ "timestamp": "2025-07-24T15:24:00Z", "service": "payment-service", "signal_type": "metric", "severity_score": 0.85, "details": { ... } }

این نرمال‌سازی بسیار مهم است. چون به correlation engine پایین‌دستی اجازه می‌دهد بدون توجه به این‌که event را کدام detector تولید کرده، درباره signalهای مختلف استدلال کند.

گام 3: همبستگی زمانی؛ پیدا کردن anomalyهایی که هم‌زمان رخ داده‌اند

اولین وظیفه correlation engine این است که خوشه‌هایی از anomalyها را شناسایی کند که در زمان به هم نزدیک رخ داده‌اند. منطق ما این است: اگر یک database در ساعت 15:24 خطا بدهد، سرویسی که از آن استفاده می‌کند در ساعت 15:24:30 timeout شود، و frontendای که آن سرویس را صدا می‌زند در ساعت 15:25 خطای 500 برگرداند، این‌ها تقریباً قطعاً به هم مرتبط‌اند.

ما از یک sliding window قابل‌تنظیم استفاده می‌کنیم، معمولاً مثبت و منفی 5 دقیقه، تا anomalyهای هم‌زمان را در correlation bundleها گروه‌بندی کنیم. هر bundle یک temporal cohesion score دریافت می‌کند:

S_temporal = (1 / N(N-1)) × Σ exp(-|ti - tj| / τ)

در این فرمول، N تعداد eventهاست، ti و tj timestamp رویدادها هستند و τ یک decay constant قابل تنظیم است. هرچه anomalyها فشرده‌تر و نزدیک‌تر باشند، امتیاز بالاتری می‌گیرند.

یک بهبود مهم که در عمل به آن رسیدیم این بود که همان زنجیره علّی معمولاً در طول یک incident چندین بار تکرار می‌شود؛ مثلاً همان timeout upstream هر چند ثانیه همان failure downstream را دوباره ایجاد می‌کند. بدون deduplication، این موضوع bundleهای تکراری می‌سازد و سیگنال اصلی را مبهم می‌کند. ما این مشکل را با sequence fingerprinting حل می‌کنیم؛ fingerprint را از فهرست مرتب‌شده سرویس‌ها در هر مسیر anomaly محاسبه می‌کنیم و sequenceهای تکراری را در یک bundle واحد با replay count ادغام می‌کنیم. این کار اجازه می‌دهد به‌جای ساخت 47 hypothesis مشابه، بگوییم «این الگوی خرابی در 5 دقیقه، 47 بار تکرار شده است.» 🔁

گام 4: تحلیل اثر مبتنی بر graph؛ پیدا کردن جهت علّی

همبستگی زمانی فقط نشان می‌دهد کدام anomalyها با هم رخ داده‌اند. تحلیل مبتنی بر graph مشخص می‌کند کدام سرویس علت است و کدام‌ها اثر آن هستند. برای هر correlation bundle، ما sink node را پیدا می‌کنیم؛ یعنی سرویسی که بالاترین severity anomaly را دارد و معمولاً همان نقطه‌ای است که اثر آن برای کاربر قابل مشاهده است. سپس با BFS و تا عمق قابل تنظیم، به‌صورت upstream در dependency graph حرکت می‌کنیم و همسایه‌های دارای anomaly را بررسی می‌کنیم.

نکته کلیدی این است: اگر Service A، Service B را صدا بزند و هر دو در همان بازه زمانی anomalous باشند، اما anomaly در Service B زودتر از Service A رخ داده باشد، احتمالاً Service B منشأ خطاست و Service A فقط اثر downstream را تجربه می‌کند.

ما برای هر مسیر علّی احتمالی امتیاز محاسبه می‌کنیم:

S_path = (1/m) × Σ S_anomaly(Ui) × w_edge(Ui → Ui+1) × exp(-α × Δt)

در این فرمول، m طول مسیر است، S_anomaly شدت anomaly در هر node را نشان می‌دهد، w_edge قدرت رابطه dependency را مشخص می‌کند و decay نمایی برای anomalyهایی که از sink فاصله زمانی زیادی دارند جریمه در نظر می‌گیرد. مسیری که بالاترین امتیاز را بگیرد، بهترین حدس ما از زنجیره انتشار خطاست.

گام 5: تولید فرضیه و روایت نهایی

در مرحله آخر، امتیاز cohesion زمانی و امتیاز مسیر با هم ترکیب می‌شوند تا برای هر correlation bundle یک confidence score کلی ساخته شود:

S_overall = w1 × S_temporal + w2 × S_path

سپس bundleها بر اساس این امتیاز رتبه‌بندی می‌شوند و top N به‌عنوان root cause hypothesis خروجی داده می‌شوند. هر hypothesis شامل موارد زیر است:

– سرویس مشکوک به root cause، یعنی منشأ upstream خطا
– مسیر انتشار که نشان می‌دهد خطا چگونه پخش شده و به symptom قابل مشاهده برای کاربر رسیده است
– شواهد در هر node، مانند این‌که کدام metric از threshold عبور کرده، چه exceptionهایی ظاهر شده و کدام trace IDها خرابی را نشان می‌دهند
– confidence score و توضیحی از این‌که چگونه محاسبه شده است
– یک روایت قابل‌فهم برای انسان که فرضیه را به زبان ساده توضیح می‌دهد

این بخش آخر از آنچه به نظر می‌رسد مهم‌تر است. یک فهرست رتبه‌بندی‌شده از سرویس‌ها با score برای ماشین‌ها مفید است، اما پاسخ‌گوها باید خیلی سریع تشخیص دهند که آیا یک فرضیه ارزش پیگیری دارد یا نه. قالب‌های روایی ما توضیحاتی شبیه این تولید می‌کنند:

«بین ساعت 15:24 تا 15:28، endpoint /charge در payment-service با افزایش 4 برابری error rate مواجه شد (baseline: 0.2%، observed: 0.8%). این اتفاق پیش از جهش latency در checkout-service /complete رخ داد (p99: از 340ms به 2100ms) و سپس به frontend به شکل HTTP 500 errors منتقل شد. با توجه به تقدم زمانی و موقعیت graph، به‌احتمال زیاد خطا از payment-service شروع شده است. Confidence: 0.87.»

جای گرفتن در یک platform گسترده‌تر برای reliability

Automated RCA به‌تنهایی وجود ندارد. در Atlassian، ما در حال ساخت یک platform یکپارچه برای incident response هستیم که تشخیص خودکار اثر روی کاربر، شناسایی سرویس معیوب، diagnosis علّی و یک AI-powered incident copilot را در یک تجربه واحد برای پاسخ‌گوها ترکیب می‌کند. موتور RCA ما نقش مغز تشخیصی این platform را بازی می‌کند. وقتی یک incident اثرگذار بر کاربر شناسایی می‌شود، چه به‌صورت خودکار از طریق real-time user experience signals و چه به‌صورت دستی توسط تیم‌های پشتیبانی هنگام مشاهده surge در ticketها، موتور RCA فعال می‌شود. سپس فرضیه‌های خود را در یک shared incident context منتشر می‌کند تا سایر componentها از آن استفاده کنند:

Faulty service identification از نتایج اولیه RCA استفاده می‌کند تا تیم درست را سریع‌تر در جریان قرار دهد و زمان رسیدگی را کاهش دهد.

یک incident copilot از hypothesisها و شواهد آن‌ها برای توضیح وضعیت برای پاسخ‌گوها و کمک به تصمیم‌گیری استفاده می‌کند. 🤝