پروفایلینگ وب اپلیکیشن؛ از RUM و Trace تا رفع گلوگاه

کندی وب‌اپلیکیشن معمولاً در جایی نیست که اولین نمودار نشان می‌دهد. تصور کنید پس از یک Release، صدک ۹۵ زمان تکمیل پرداخت برای بخشی از کاربران موبایل ایران از ۴ به ۹ ثانیه می‌رسد. CPU سرور بالا نیست و میانگین پاسخ API نیز عادی به نظر می‌رسد. تیم یک تابع JavaScript را ۶۰٪ سریع‌تر می‌کند، اما کاربر تغییری حس نمی‌کند؛ چون زمان واقعی میان صف Main Thread، درخواست قیمت، Redirect درگاه و اتصال شبکه تقسیم شده بود. بهینه‌سازی درست بود، ولی گلوگاه نبود.

پروفایلینگ وب اپلیکیشن یعنی نسبت‌دادن مصرف زمان، CPU، حافظه، I/O یا رندر به مسیرها و قطعه‌کدهای مشخص. دیباگینگ عملکرد فرایند گسترده‌تری است که از تعریف علامت و بازتولید شروع می‌شود، شواهد چندلایه را به فرضیه وصل می‌کند و با تغییر کنترل‌شده و اندازه‌گیری دوباره پایان می‌یابد. این راهنما یک Workflow عملی برای Front-end، Back-end، پایگاه داده و سرویس‌های توزیع‌شده می‌سازد؛ بدون نسخه‌پیچی از روی یک Score یا Flame Graph.

Profiling، Monitoring و Benchmarking را یکی نگیرید

فعالیتپرسش اصلیخروجیمحدودیت
Monitoringاکنون چه چیزی خارج از حالت مطلوب است؟Metric، Log، Alert و SLOاغلب محل دقیق مصرف کد را نشان نمی‌دهد
Tracingیک Request از کدام اجزا عبور کرد؟Span و Waterfall سرویس‌هاتابع پرمصرف داخل Span را الزاماً نشان نمی‌دهد
Profilingکدام Stack یا Allocation منبع مصرف کرد؟CPU/Wall/Heap/Allocation Profileنمونه و سربار دارد و Context کسب‌وکار لازم است
Benchmarkingاین کار در شرایط تعریف‌شده چقدر زمان یا ظرفیت دارد؟Latency، Throughput و Resource curveمحیط غیرواقعی می‌تواند نتیجه را منحرف کند
Performance Debuggingعلت علامت چیست و آیا Fix واقعاً اثر کرد؟فرضیه، آزمایش، Diff و تصمیمبه شواهد همه فعالیت‌های بالا نیاز دارد

Lighthouse یک ابزار ممیزی و Lab است؛ APM یک دسته محصول است؛ Chrome DevTools مجموعه ابزارهای محلی است؛ و OpenTelemetry استاندارد/اکوسیستم Telemetry است. هیچ‌کدام مترادف Profiling یا «حقیقت کامل» نیستند. معماری پایش بلندمدت را در راهنمای Observability با Metric، Log و Trace جداگانه طراحی کنید.

از علامت کاربر شروع کنید، نه از ابزار

جمله «اپلیکیشن کند است» قابل عیب‌یابی نیست. یک قرارداد مسئله بنویسید که Journey، جمعیت، زمان، نسخه، علامت و معیار موفقیت را مشخص کند:

Journey: جست‌وجو → صفحه محصول → افزودن → پرداخت
Population: Android / مرورگر درون‌برنامه‌ای / اپراتورهای منتخب ایران
Symptom: زمان کلیک «پرداخت» تا نمایش نتیجه
Window: از Release 2026.08.10 به بعد
Baseline: p50=2.1s, p75=3.4s, p95=4.0s
Current:  p50=2.2s, p75=4.1s, p95=9.0s
Guardrails: Error, duplicate order, abandonment, CPU, memory
Exit: p95≤4.5s بدون افزایش خطا یا سفارش تکراری

میانگین می‌تواند Tail دردناک را پنهان کند. صدک‌ها را همراه حجم نمونه، بازه، Route، Device، Network، Geography، Version و Cache state گزارش کنید. اگر هدف اقتصادی و بودجه Performance هنوز روشن نیست، ابتدا سرعت سایت را به Journey و نتیجه کسب‌وکار وصل کنید.

چرخه عیب‌یابی عملکرد را قابل تکرار کنید

  1. تعریف: علامت، دامنه اثر، Severity، Baseline و Guardrail را ثبت کنید.
  2. اعتبارسنجی داده: Tracking، Clock، Sampling، Aggregation و تغییر ابزار را بررسی کنید.
  3. Segment: Route، نسخه، دستگاه، شبکه، شهر، Tenant، Feature flag و وابستگی را تفکیک کنید.
  4. بازتولید: سناریوی حداقلی و بار/داده نزدیک به واقعیت بسازید.
  5. مکان‌یابی: بودجه زمان را میان Client، Network، Edge، Queue، App، DB و Dependency بشکنید.
  6. Profile: کم‌خطرترین ابزار مناسب را روی لایه مشکوک اجرا کنید.
  7. فرضیه: مکانیسم علّی و نتیجه قابل رد بنویسید.
  8. تغییر: یک تغییر محدود با Feature flag، Canary و Rollback اعمال کنید.
  9. تأیید: همان سناریو، Segment و Window را دوباره بسنجید و Regressionها را چک کنید.
  10. نهادینه‌سازی: Budget، Test، Dashboard، Runbook و مالک را به‌روز کنید.

ترتیب مهم است. اگر قبل از اعتبارسنجی Telemetry کد را تغییر دهید، شاید Instrumentation خراب را «بهبود» دهید. اگر چند Fix را یکجا Deploy کنید، علت اثر معلوم نمی‌شود. اگر پس از Fix فقط CPU را ببینید، ممکن است Task کاربر هنوز کند یا خطاپذیر باشد.

اول داده Field و Lab را مرزبندی کنید

Lab برای تکرارپذیری، بررسی Trace و مقایسه قبل/بعد عالی است؛ Field یا RUM تنوع واقعی Device، Network، Cache، Session و رفتار را نشان می‌دهد. Lab کندی واقعی را اثبات نمی‌کند و RUM خام نیز محل کد را دقیق نشان نمی‌دهد. Workflow خوب از Field برای یافتن Cohort مسئله، از Lab برای بازتولید و از Field برای تأیید اثر استفاده می‌کند.

دادهمناسب برایخطر تفسیر
CrUX/Search Consoleتجربه Field تجمیعی و روندGranularity و تأخیر؛ نبودن در Dataset
RUM داخلیRoute/Version/Device/Network و JourneySampling، Consent، Bot و Cardinality
LighthouseLab audit و فرصت‌های تشخیصیScore نوسانی و محیط غیرنماینده
DevTools Traceآبشار، Main Thread، Rendering و Stackیک Session و اثر محیط توسعه
Synthetic JourneyRegression و Availability تکرارشوندهDevice/Route/Account مصنوعی

Core Web Vitals جاری LCP، INP و CLS هستند؛ FID در مارس ۲۰۲۴ با INP جایگزین شد. منبع رسمی Google آستانه‌های «خوب» را در صدک ۷۵ برابر LCP حداکثر ۲٫۵ ثانیه، INP حداکثر ۲۰۰ میلی‌ثانیه و CLS حداکثر ۰٫۱ بیان می‌کند. این‌ها بودجه کامل اپلیکیشن نیستند و پاسخ API، تکمیل پرداخت یا کندی پس از Login را پوشش نمی‌دهند. برای تشخیص تخصصی آن‌ها به راهنمای LCP، INP، CLS و RUM و برای Snapshot جاری به تعریف رسمی آستانه‌های Core Web Vitals مراجعه کنید.

ماتریس بازتولید بسازید

یک Trace لپ‌تاپ قوی با Cache گرم نماینده کاربران نیست. کمترین ماتریس مفید شامل Build/Commit، Browser/WebView، Device class، OS، Viewport، Network/RTT/loss، Cache cold/warm، Account/Tenant، Dataset size، Feature flag و مرحله Journey است. برای ایران، چند اپراتور و اتصال ثابت، ساعت شلوغ و مرورگر درون‌برنامه‌ای را نمونه‌برداری کنید.

شرایط را عوض کنید، اما هر بار یک محور را. اگر مشکل فقط با حساب دارای ۵۰هزار رکورد رخ می‌دهد، Device را متهم نکنید. اگر فقط پس از رفت‌وبرگشت مکرر SPA ظاهر می‌شود، Lifetime و Cleanup مشکوک‌اند. اگر فقط پس از Deploy و با Cache گرم Edge دیده می‌شود، Version skew یا Cache key را بررسی کنید.

بودجه زمان End-to-End را بشکنید

زمان تجربه‌شده کاربر را به اجزای قابل مالکیت تقسیم کنید. برای یک تعامل:

User-perceived time = input delay
                    + client processing
                    + request queue/DNS/connect/TLS/upload
                    + edge/gateway/queue
                    + application + database + dependency
                    + download/parse
                    + render/presentation delay

جمع Spanها همیشه دقیقاً با زمان کاربر برابر نمی‌شود: Clockها، Parallel work، Sampling، Queue بدون Instrumentation و زمان Render ممکن است فاصله بسازند. اختلاف را به‌عنوان «زمان بدون توضیح» اندازه بگیرید؛ آن را خودکار به Network یا Front-end نسبت ندهید.

Correlation ID و Trace context را از مرورگر تا Worker ببرید

در سامانه چندخدمتی، Request ID محلی کافی نیست. Trace context را از Edge به API، Queue، Worker، Database client و Dependency مجاز منتقل کنید و Version/Region/Tenant class را با Cardinality کنترل‌شده اضافه کنید. مستند رسمی OpenTelemetry Traces توضیح می‌دهد Span یک واحد کار است و Context propagation اجازه می‌دهد Spanهای سرویس‌های مختلف در یک Trace جمع شوند.

نام Span را بر Route template مانند POST /orders/:id/pay بنا کنید، نه URL دارای ID واقعی. User ID، شماره موبایل، Token، Query خام یا Payload حساس را Attribute نکنید. Sampling باید خطاها و Tailهای مهم را حفظ کند؛ در غیر این صورت سریع‌ترین درخواست‌ها تصویر غالب می‌سازند. درباره Contract، Timeout، Retry و Idempotency خود API به راهنمای طراحی و قابلیت اطمینان API رجوع کنید.

Workflow پروفایلینگ Front-end در Chrome DevTools

ابتدا نسخه Chrome و تنظیمات ضبط را ثبت کنید؛ UI و Insightهای DevTools تغییر می‌کنند. در حالت Incognito یا Profile تمیز، Extensionهای مزاحم را حذف کنید، Throttling را مستند سازید، Screenshot و Web Vitals را در صورت نیاز فعال کنید و فقط Window حاوی تعامل مسئله را ضبط کنید. مرجع رسمی Performance panel کروم بین Record load و Record runtime تفاوت می‌گذارد و بخش‌های Summary، Bottom-up، Call tree و Event log را برای تحلیل Trace ارائه می‌کند.

از Overview به Stack بروید

  1. در Network زمان شروع، Queue، TTFB، Download، Initiator، Priority، Protocol و Cache را ببینید.
  2. در Main track بازه Interaction یا Load را انتخاب کنید؛ Long task، Script، Style، Layout، Paint و Composite را تفکیک کنید.
  3. در Bottom-up، Self time را برای کار مستقیم و Total time را برای تابع همراه فرزندان بخوانید.
  4. Call tree و Heaviest stack را برای مسیر فراخوانی بررسی کنید؛ نام Minified بدون Source map قابل اتکا نیست.
  5. Screenshot و Experience track را با لحظه‌ای که کاربر نتیجه را می‌بیند تطبیق دهید.

بزرگ‌ترین بلوک زرد الزاماً بهترین Fix نیست. ممکن است کد هنگام Idle اجرا شود و Journey را مسدود نکند، یا یک تابع کوچک هزاران بار فراخوانی شود و اثر تجمعی بسازد. انتخاب را با Critical path و علامت کاربر گره بزنید.

Long Task و INP را به سه زیرمرحله بشکنید

هر Task بیش از ۵۰ میلی‌ثانیه در اصطلاح Long Tasks یک Long task است؛ اما طول Task به‌تنهایی INP نیست. Interaction می‌تواند از Input delay، Processing duration و Presentation delay آسیب ببیند. پیش از خردکردن کد، مشخص کنید کدام بخش غالب است. راهنمای رسمی web.dev درباره Long Tasks Run-to-completion و امکان Yield دادن برای کار با اولویت بالاتر را شرح می‌دهد.

راه‌حل ممکن است حذف کار، Lazy initiation، شکستن Task، زمان‌بندی پس از Paint، Web Worker یا کاهش DOM باشد. setTimeout پراکنده، Yield بیش‌ازحد یا انتقال کور همه چیز به Worker می‌تواند Overhead، Race و پیچیدگی بسازد. نتیجه را روی Device ضعیف و Interaction واقعی اندازه بگیرید.

Layout، Paint و Composite را با Trace تشخیص دهید

خواندن Geometry پس از تغییر Style/DOM ممکن است مرورگر را وادار به محاسبه هم‌زمان Layout کند. اگر این Read/Write در حلقه تکرار شود، Layout thrashing شکل می‌گیرد. Performance Insights کروم Forced reflow را به‌عنوان وضعیتی شناسایی می‌کند که JavaScript محاسبه Layout را تحمیل می‌کند. راه‌حل را با دسته‌بندی Readها و Writeها، کوچک‌کردن دامنه DOM و حذف اندازه‌گیری غیرضروری بیازمایید.

transform و opacity غالباً گزینه‌های مناسب‌تری برای Animation هستند، اما تضمین نمی‌کنند همه کار فقط روی GPU انجام شود. ساخت Layer، Memory و Upload texture هزینه دارد. will-change را محدود و موقت استفاده کنید. Paint flashing، Layers و Trace واقعی را ببینید؛ نسخه عمومی «همیشه Composite و رایگان» نپیچید.

JavaScript را از Bundle تا Execution پروفایل کنید

حجم دانلود، Parse/Compile، Evaluation، Runtime، Hydration و Third-party هزینه‌های متفاوت‌اند. Coverage فایل بلااستفاده در همان Session را نشان می‌دهد، نه اینکه کد برای همه کاربران بی‌فایده است. Source map باید به Commit و Artifact دقیق مربوط باشد و در محیط امن نگهداری شود.

علامتشاهدفرضیه‌های محتملآزمایش
Main thread پر پیش از نمایشScript/Evaluate و Long taskBundle، Hydration، Third partyDisable/Delay/Route split با Canary
Interaction کندEvent timing و StackHandler، State update، RenderMark زیرمرحله‌ها و حذف کار
GC مکررAllocation و Minor/Major GCAllocation churn یا Memory pressureAllocation profile و reuse محدود
کندی پس از Navigationهای زیادHeap growth و RetainerListener، Timer، Cache یا Detached DOMسناریوی تکراری و Heap comparison

User Timing را روی مرزهای کسب‌وکار بگذارید

نام تابع Framework برای Product owner معنا ندارد. با User Timing مراحل مهمی مانند search_results_visible، cart_repriced یا payment_confirmed را علامت بزنید. نام‌ها نسخه‌دار، کم‌تعداد و فاقد داده شخصی باشند.

performance.mark('checkout:start');

await validateCart();
performance.mark('checkout:validated');

await requestPayment();
performance.mark('checkout:payment-ready');

performance.measure(
  'checkout:validation',
  'checkout:start',
  'checkout:validated'
);

برای جمع‌آوری RUM، PerformanceObserver را با تشخیص Capability و Sampling به کار ببرید. Buffer محدود است و APIها در همه Browser/WebViewها یکسان نیستند. Raw URL یا Detail حساس را ارسال نکنید و هزینه Instrumentation را با A/B telemetry یا آزمایش روشن/خاموش بسنجید.

Memory leak را از Memory bloat و Allocation churn جدا کنید

Leak یعنی اشیای دیگرلازم به‌علت Reference همچنان قابل دسترس می‌مانند و مصرف در تکرار سناریو روند افزایشی دارد. Bloat یعنی Working set برای دستگاه هدف بیش از حد بزرگ است. Allocation churn ممکن است Heap نهایی را بالا نبرد، اما GC مکرر و Pause ایجاد کند. یک Snapshot منفرد هیچ‌کدام را قطعی ثابت نمی‌کند.

سناریوی Browser برای شکار Leak

  1. یک مسیر تکرارشونده مانند باز/بسته‌کردن Modal یا Navigation A→B→A تعریف کنید.
  2. پس از Warm-up و در حالت Idle، Baseline را ثبت کنید.
  3. سناریو را چند بار با ورودی ثابت اجرا کنید.
  4. پس از فرصت GC، Heap/Allocation را مقایسه کنید.
  5. Constructor، Shallow size، Retained size و Retainer path را بخوانید.
  6. Listener، Subscription، Timer، Closure، Cache و Detached DOM را بررسی کنید.
  7. Fix را با تکرار بیشتر و Device کم‌حافظه تأیید کنید.

مستند رسمی Memory panel کروم Heap snapshot، Allocation instrumentation و Allocation sampling را با کاربردهای متفاوت معرفی می‌کند. Detached node یک سرنخ است؛ تا Retainer path و Lifetime مورد انتظار را نفهمیده‌اید آن را علت قطعی نخوانید.

پروفایلینگ Back-end را با نوع انتظار هماهنگ کنید

Latency زیاد الزاماً CPU زیاد نیست. Request ممکن است در Queue، Lock، Disk، Network، Connection pool، Garbage collection یا Dependency منتظر باشد. CPU profile فقط Stackهای درحال اجرا را خوب می‌بیند؛ برای زمان انتظار به Wall-clock/Off-CPU، Trace، Queue metrics و Runtime events نیاز دارید.

علامتپروفایل/شاهد مناسبخطای رایج
CPU بالاSampling CPU profile و Flame graphبهینه‌سازی تابع خارج Critical path
Latency بالا، CPU پایینTrace، Off-CPU، I/O و Queue waitافزایش Replica بدون یافتن Dependency
Memory رو به رشدHeap/Allocation/RSS/Native memoryیکی‌گرفتن Heap runtime با کل RSS
Pause دوره‌ایGC events، Heap pressure و Event-loop lagنسبت‌دادن همه Pauseها به Leak
Throughput سقف داردSaturation، Queue depth و Utilization curveنگاه‌کردن فقط به Average latency

Sampling و Instrumentation را آگاهانه انتخاب کنید

Sampling profiler در فواصل زمانی Stack را نمونه می‌گیرد و معمولاً برای CPU با سربار کمتر مناسب است؛ تابع کوتاه یا رخداد نادر ممکن است از نمونه جا بماند. Instrumentation رخداد یا Allocation را دقیق‌تر ثبت می‌کند، اما می‌تواند حجم و سربار بیشتری بسازد. Frequency بالاتر نیز همیشه بهتر نیست؛ هزینه و Bias اندازه‌گیری را افزایش می‌دهد.

OpenTelemetry در Snapshot فعلی Profiles را سیگنالی با وضعیت Alpha معرفی می‌کند و On-CPU، Off-CPU، Heap و Allocation را از هم جدا می‌سازد. بنابراین قابلیت Export، Runtime، Agent و Backend را نسخه‌به‌نسخه PoC کنید و Alpha بودن را در Procurement ثبت کنید. جزئیات در مرجع رسمی OpenTelemetry Profiles آمده است.

Node.js را بدون به‌خطرانداختن Production پروفایل کنید

در Node.js می‌توان از Inspector و گزینه‌های Runtime مانند --cpu-prof و --heap-prof استفاده کرد؛ نام و Stability گزینه‌ها را با نسخه نصب‌شده و مستند رسمی CLI همان نسخه Node.js تطبیق دهید. فایل Profile ممکن است کد، نام‌ها یا داده حساس را آشکار کند؛ مسیر ذخیره، دسترسی، Retention و حذف امن لازم است.

Heap snapshot روی فرایند Production اقدام کم‌خطر نیست. مستند رسمی Node.js هشدار می‌دهد ساخت Snapshot کار Main thread را متوقف می‌کند، ممکن است بیش از یک دقیقه طول بکشد و به‌علت ساخت در حافظه، Heap را دوبرابر و حتی Process را Crash کند. آن را ابتدا روی Replica قابل حذف یا محیط مشابه بار اجرا کنید و ظرفیت/Failover را تأیید کنید؛ راهنما در Using Heap Snapshot در Node.js موجود است.

Event loop، Queue و Backpressure را اندازه بگیرید

در Runtimeهای Event-driven، یک Handler CPU-heavy یا کار هم‌زمان زیاد می‌تواند Event loop را عقب بیندازد. Event-loop delay/utilization، Active request، Queue depth/age، Worker saturation، Pool wait و Rejection را کنار Latency ببینید. افزایش Concurrency بدون Backpressure ممکن است Throughput کوتاه‌مدت را بالا ببرد اما Tail، Timeout و Memory را بدتر کند.

برای هر Queue، نرخ ورود/خروج، سن قدیمی‌ترین پیام، Retry، Dead-letter و ظرفیت Consumer را ثبت کنید. Trace را از Producer به Consumer با Semantics درست ادامه یا Link کنید. اگر درخواست کاربر قبل از پایان Job پاسخ می‌گیرد، زمان Task کسب‌وکار را جدا از HTTP response گزارش کنید.

پایگاه داده را از روی Query واقعی عیب‌یابی کنید

Slow query log یا Trace، Query fingerprint، فراوانی، Total time، Rows و Wait را جمع کنید. Query با Latency متوسط اما میلیون‌ها اجرا ممکن است مهم‌تر از Query بسیار کند و نادر باشد. N+۱ را از روی تعداد فراخوانی در یک Journey اثبات کنید، نه صرف وجود ORM.

EXPLAIN Plan تخمینی می‌دهد؛ EXPLAIN ANALYZE در PostgreSQL واقعاً Query را اجرا می‌کند و Side effectهای Statement رخ می‌دهند. مستند جاری PostgreSQL درباره EXPLAIN همچنین می‌گوید هزینه تخمینی واحد زمان واقعی نیست و نتیجه محیط کوچک را نباید به داده بزرگ تعمیم داد. اجرای Query تغییر‌دهنده داده، Lock و بار Profiling را با DBA و محیط امن کنترل کنید.

Estimate در برابر Actual rows، تعداد Loop، Buffer/I/O، Sort spill، Lock wait و Plan change را بررسی کنید. Index جدید هزینه Write، Memory، Storage و نگهداری دارد و Correctness را جایگزین نمی‌کند. چرخه عمیق Query/Index/Lock/Connection pool در راهنمای بهینه‌سازی پایگاه داده آمده است.

Dependency خارجی را از کد خود جدا کنید

DNS، Connect، TLS، Pool wait، Upload، Server wait، Download، Retry و Circuit breaker را در Span جدا ببینید. Timeout کل باید از Budget Journey مشتق شود؛ اگر هر لایه Timeout مستقل و بزرگ داشته باشد، Retryهای تو در تو Latency و بار را چندبرابر می‌کنند.

برای درگاه، پیامک، Map یا سرویس خارجی در ایران، موفقیت از یک شبکه یا Region را تعمیم ندهید. Error code، زمان، Provider، Route و Result تجاری را بدون داده حساس ثبت کنید. Stub در Test برای تکرارپذیری مفید است، اما Chaos کنترل‌شده یا Synthetic کم‌خطر برای رفتار واقعی Timeout/Retry لازم است.

Load test را با مدل Workload بسازید

تعداد کاربر مجازی به‌تنهایی Workload نیست. Arrival rate، Think time، Journey mix، Payload/data size، Cache state، Auth، Write/read ratio، Region، Duration و Ramp را تعریف کنید. آزمون باید Warm-up، Steady state، Spike و Recovery را از هم جدا کند و Guardrailهای خطا و Correctness داشته باشد.

Generator نباید گلوگاه باشد. Client timeout، Connection reuse و Clock را پایش کنید. سیستم Load test ممکن است در Overload نرخ درخواست را ناخواسته کم کند و بدترین Latency را پنهان سازد؛ نرخ برنامه‌ریزی‌شده را در برابر نرخ واقعی و Dropped/late starts ثبت کنید. تست مخرب یا پرهزینه را بدون سقف، محیط و Kill switch اجرا نکنید.

یک Hypothesis قابل رد بنویسید

«Database کند است» فرضیه نیست. نمونه خوب: «در نسخه X، Query fingerprint Q برای Tenantهای بالای ۲۰هزار سفارش به‌دلیل تخمین کم Rows وارد Nested Loop می‌شود؛ اگر Statistics/Query اصلاح شود، DB span صدک ۹۵ حداقل ۴۰٪ کم می‌شود، بدون تغییر Result count یا Write latency.»

جزءسؤال
Mechanismچه مکانیسمی مصرف منبع یا انتظار را ایجاد می‌کند؟
Populationکدام Route/Version/Data/Device تحت تأثیر است؟
Predictionاگر درست باشد کدام Metric چگونه تغییر می‌کند؟
Falsifierچه نتیجه‌ای فرضیه را رد می‌کند؟
Guardrailچه Correctness، Error یا Cost نباید بدتر شود؟

یک Optimization ممکن است Benchmark خرد را بهتر و سیستم را بدتر کند: Cache بزرگ‌تر Hit rate را بالا می‌برد اما Memory/GC را آسیب می‌زند؛ Batch بزرگ‌تر Throughput را زیاد می‌کند اما Tail را بالا می‌برد؛ Parallelism بیشتر زمان یک Task را کم می‌کند اما Dependency را اشباع می‌سازد.

قبل/بعد را به‌صورت علّی و هم‌سطح مقایسه کنید

Commit، Artifact، Config، Dataset، Load، Device، Network و Window را هم‌سطح نگه دارید. چند Run انجام دهید و توزیع را گزارش کنید. Warm-up/JIT/Cache و Noise همسایه را ثبت کنید. اگر Production متغیر است، Canary هم‌زمان یا A/B زیرساختی از مقایسه «سه‌شنبه با جمعه» معتبرتر است.

سه لایه نتیجه را جدا کنید: Resource مانند CPU/Memory، System مانند Latency/Error/Throughput، و User/business مانند Task completion/abandonment/order correctness. کاهش CPU فقط وقتی موفقیت است که Guardrailها حفظ و مسئله اصلی بهتر شود.

Fix عملکرد را مانند Release پرریسک منتشر کنید

تغییر Cache، Query، Concurrency، Serialization یا Scheduling می‌تواند Correctness را بشکند. Test عملکردی، Contract، Load و Failure را قبل از Production اجرا کنید؛ Feature flag، Canary، Progressive rollout و Rollback آماده باشد. الگوی Build once، Canary و Verify/Rollback در راهنمای CI/CD وب‌اپلیکیشن توضیح داده شده است.

Performance budget را به Pipeline اضافه کنید، اما Gate را از Benchmark ناپایدار نسازید. Budget را برای Page/Route/Device class تعریف کنید، Noise band و Re-run policy داشته باشید و Regression بزرگ را Fail کنید. Fix مربوط به WordPress یا Plugin باید با Staging و Restore واقعی هماهنگ شود؛ فرایند آپدیت امن، تست و Rollback وردپرس مرزهای آن را پوشش می‌دهد.

سربار ابزار را اندازه بگیرید، حدس نزنید

عدد ثابت «APM فقط ۲ تا ۵ درصد سربار دارد» قابل تعمیم نیست. سربار به Runtime، Agent/version، Hook، Sampling، Stack depth، Export، Cardinality، Traffic و Query capture بستگی دارد. Latency، CPU، Memory، Allocation، Network egress و حجم داده را با Telemetry خاموش/روشن در Workload نماینده مقایسه کنید.

برای Production از Minimum viable telemetry شروع کنید، Rate/Head/Tail sampling را کنترل و Attribute پرکاردینال را محدود کنید. User ID و URL خام در Metric label می‌توانند هم هزینه را منفجر و هم حریم خصوصی را تهدید کنند. Drop/Queue/Exporter failure خود Telemetry نیز باید دیده شود.

امنیت و حریم خصوصی Profileها را بخشی از طراحی بدانید

Heap snapshot ممکن است Token، محتوا، PII و Secret در حافظه را در بر داشته باشد. CPU profile و Trace نیز نام Function، Endpoint، Query، File path و ساختار داخلی را آشکار می‌کنند. Profile را Artifact حساس طبقه‌بندی کنید: مجوز محدود، Encryption، Retention کوتاه، Audit access و حذف تأییدشده.

Inspector یا Endpoint تشخیصی را عمومی نکنید. Symbol/Source map و Debug port باید فقط از مسیر امن و موقت قابل دسترسی باشند. در Incident، گرفتن شواهد را با Availability و Privacy متعادل کنید؛ Crash کردن تنها Replica برای Snapshot معمولاً تصمیم درستی نیست.

ملاحظات ایران را در ماتریس Performance وارد کنید

  • Device: گوشی اقتصادی، حافظه محدود و WebView قدیمی‌تر را کنار دستگاه قوی آزمایش کنید.
  • Network: چند اپراتور، اینترنت ثابت، RTT/loss و ساعات مختلف را جدا ثبت کنید.
  • Route: CDN، DNS و Provider داخلی/خارجی ممکن است Segmentهای متفاوت بسازند.
  • Dependency: درگاه، پیامک، Map و سرویس SaaS را با Eligibility و شرایط جاری بررسی کنید؛ دورزدن Policy راه‌حل Performance نیست.
  • فارسی: فونت، زیرمجموعه حروف، RTL/BiDi، عدد، تومان/ریال و متن بلند را در Trace واقعی بگنجانید.
  • داده: Payload و Dataset نمونه باید اندازه و توزیع فارسی واقعی داشته باشد، اما داده شخصی Production را کپی نکند.
  • ابزار: Agent، Registry، Source map store و Backend پروفایل را از نظر دسترسی، Renewal، Export و بازیابی بسنجید.

فونت فارسی می‌تواند Discovery، Transfer، Paint و CLS را تغییر دهد. برای تشخیص و اصلاح تخصصی آن از راهنمای WOFF2، Preload، CLS و فونت فارسی استفاده کنید؛ آن را با حذف کور فونت یا Preload همه فایل‌ها جایگزین نکنید.

Runbookهای دیباگینگ عملکرد

افزایش ناگهانی Latency پس از Release

داده را بر Version/Route/Segment بشکنید، تغییر Telemetry و Traffic را رد کنید، Trace نمونه Tail را با نسخه قبل مقایسه و Feature flag یا Rollback آماده را فعال کنید. اگر Correctness یا Availability در خطر است، سرعت مهار از کامل‌بودن Root cause مهم‌تر است؛ شواهد را پیش از تغییر حفظ کنید.

CPU بالا بدون افزایش Throughput

Saturation و Queue را تأیید، Sampling CPU profile کوتاه و کنترل‌شده بگیرید و Stackها را با Request/Version مرتبط کنید. Loop، Serialization، Compression، Regex، GC و Retry storm را بررسی کنید. Scale موقت می‌تواند زمان بخرد، اما علت را پاک نمی‌کند.

رشد حافظه و OOM

Heap، RSS، Native، Container limit، GC و Restart pattern را تفکیک کنید. ابتدا Profile کم‌خطر یا Replica مشابه، سپس Snapshot با ظرفیت و Failover بگیرید. Retainer path یا Allocation stack را پیدا کنید؛ Restart دوره‌ای فقط Mitigation موقت است.

INP بد فقط برای بخشی از کاربران

Route، Interaction، Device، Browser/WebView، Network و نسخه را Segment کنید. RUM attribution و DevTools trace همان Interaction را به Input/Processing/Presentation بشکنید. Third party، Hydration، Long task و Render را با آزمایش Disable/Delay محدود بررسی کنید.

Database یا Dependency Tail spike

Pool wait، Queue، Timeout، Retry، Lock، Plan و Provider را جدا کنید. Retry را کور افزایش ندهید. Circuit breaker/Load shed یا Degrade امن را فعال، Query/Dependency را با شواهد نسخه‌ای تحلیل و پس از Recovery Backlog را کنترل کنید.

برنامه ۳۰/۶۰/۹۰روزه

روز ۱ تا ۳۰: Baseline و قابلیت تشخیص

  • سه Journey مهم و Performance contract هرکدام را تعریف کنید.
  • RUM حداقلی، Server latency/error/saturation و Correlation ID را اعتبارسنجی کنید.
  • Release annotation، Device/Network/Version segmentation و Data-quality checks بسازید.
  • دسترسی امن به ابزار و Runbook مهار Incident را آماده کنید.

روز ۳۱ تا ۶۰: Profiling و آزمایش

  • یک مشکل Front-end، یک مشکل Back-end و یک Query پرتکرار را Evidence-led عیب‌یابی کنید.
  • CPU/Memory/Trace overhead را با Workload نماینده اندازه بگیرید.
  • Benchmark قابل تکرار، Dataset مصنوعی واقع‌نما و Device/network matrix ایران بسازید.
  • Fixها را با Hypothesis، Guardrail، Canary و Rollback منتشر کنید.

روز ۶۱ تا ۹۰: Regression و Governance

  • Budgetهای پایدار را در CI و RUM Release comparison وارد کنید.
  • Trace-to-profile correlation را برای سرویس‌های پرریسک PoC کنید.
  • هزینه/Retention/Cardinality و دسترسی Artifactهای تشخیصی را ممیزی کنید.
  • Postmortem، Backlog عملکرد، Owner و Expiry هر استثنا را عملیاتی کنید.

چک‌لیست جلسه Performance

  • علامت کاربر، Population، Baseline و Severity دقیق است.
  • Tracking، Sampling، Clock و تغییر Dashboard بررسی شده‌اند.
  • Field و Lab و Average و Tail از هم تفکیک شده‌اند.
  • نسخه، Device، Network، Route، Data size و Cache state ثبت شده‌اند.
  • زمان End-to-End میان Client، Network، Queue، App، DB و Dependency شکسته شده است.
  • ابزار بر اساس CPU/Wall/Heap/Allocation/Trace انتخاب شده است.
  • سربار، Privacy، دسترسی و ریسک Production ارزیابی شده‌اند.
  • Hypothesis نتیجه قابل رد و Guardrail دارد.
  • Fix با Canary، Rollback و سنجش هم‌سطح منتشر می‌شود.
  • نتیجه در Resource، System و User/business تأیید شده است.

جمع‌بندی: Profile پاسخ نیست، شاهد است

Flame Graph، Heap snapshot، Lighthouse Score یا Slow query هرکدام یک نمای محدود از سیستم‌اند. عیب‌یابی قابل دفاع از تجربه کاربر و Contract شروع می‌شود، داده را اعتبارسنجی می‌کند، دامنه را Segment می‌کند و سپس کم‌خطرترین ابزار را روی لایه مشکوک به کار می‌گیرد. Profile وقتی ارزش دارد که به Trace، Release، Journey و فرضیه‌ای قابل رد وصل شود.

پس از یافتن گلوگاه نیز کار تمام نیست. Correctness، هزینه و Tail را کنار بهبود منبع بسنجید، Fix را مرحله‌ای منتشر کنید و همان روش بازتولید را تکرار کنید. خروجی واقعی یک جلسه Profiling فقط کد سریع‌تر نیست؛ یک سیستم یادگیری است که Regression بعدی را زودتر پیدا و امن‌تر مهار می‌کند.

سؤالات متداول

تفاوت پروفایلینگ و دیباگینگ عملکرد چیست؟

Profiling مصرف CPU، زمان، حافظه یا Allocation را به Stackها و کد نسبت می‌دهد. Performance debugging فرایند کامل تعریف علامت، اعتبارسنجی داده، بازتولید، مکان‌یابی، Profiling، آزمون فرضیه و تأیید Fix است. بنابراین Profile یکی از شواهد دیباگینگ است، نه کل آن.

برای پروفایلینگ وب اپلیکیشن از کجا شروع کنیم؟

از یک Journey کند و Baseline Field شروع کنید، نه از نصب ابزار. Cohort مشکل را با Route، Version، Device و Network مشخص کنید؛ سناریو را در Lab بازتولید و زمان را میان Client، Network، App، Database و Dependency تقسیم کنید. سپس Profiler مناسب همان لایه را اجرا کنید.

آیا Lighthouse برای پیدا کردن همه مشکلات عملکرد کافی است؟

خیر. Lighthouse یک ممیزی Lab مفید برای Load و فرصت‌های مشخص است، اما رفتار کاربران واقعی، مسیرهای Loginشده، Tail سرور، Memory leak طولانی، Queue و Dependency را کامل نمی‌بیند. آن را با RUM، DevTools Trace، Server telemetry و Profile تکمیل کنید.

چگونه Memory leak در SPA را ثابت کنیم؟

یک سناریوی تکراری تعریف کنید، پس از Warm-up Baseline بگیرید، سناریو را چند بار اجرا و روند Heap/Allocation را پس از فرصت GC مقایسه کنید. اشیای رو‌به‌رشد را تا Retainer path دنبال کنید و Listener، Timer، Subscription، Cache و Detached DOM را بررسی کنید. یک Heap بزرگ یا Detached node منفرد اثبات Leak نیست.

آیا گرفتن CPU Profile یا Heap Snapshot در Production امن است؟

به ابزار، Runtime، مدت، بار و نوع Profile بستگی دارد و تضمین عمومی ندارد. Sampling کوتاه روی Canary یا Replica معمولاً از Snapshot کامل کم‌خطرتر است. Heap snapshot می‌تواند فرایند را متوقف، حافظه را زیاد و داده حساس را ثبت کند؛ ظرفیت، Failover، دسترسی، Retention و Rollback را پیش از اجرا تأیید کنید.

دیدگاهتان را بنویسید

نشانی ایمیل شما منتشر نخواهد شد. بخش‌های موردنیاز علامت‌گذاری شده‌اند *