اگر امروز برای بهینهسازی سرعت برنامههایتان فقط به سراغ تنظیمات پایگاهداده میروید، احتمالاً نیمی از گلوگاههای واقعی را نادیده میگیرید. در یک مورد واقعی، تأخیر p99 (میزان تأخیر در بدترین حالت) یک API پرداخت با هدف قرار دادن کد اپلیکیشن، از ۲.۱ ثانیه به ۸۶۰ میلیثانیه کاهش یافت. این موفقیت حاصل همکاری یک توسعهدهنده با Claude Code برای شکار یک گلوگاه پنهان بود؛ باگی با پیچیدگی مرتبه دوم (Quadratic Complexity) که تنها در سفارشهای حجیم فعال میشد.
تأخیرهای متناوب در لبههای توزیع (Tail Latency) یکی از سختترین چالشهای مهندسی نرمافزار هستند، چون بهندرت در محیطهای محلی یا تستهای مصنوعی ظاهر میشوند. بسیاری از تیمها بهطور پیشفرض فرض میکنند مشکل از کندی پایگاهداده است، اما این مورد نشان میدهد که چنین تشخیصی اغلب اشتباه است. همانطور که در تحلیل قبلی ما دربارهی اولویت اکوسیستم بر بنچمارک در ابزارهای توسعه اشاره کردیم، این کاربرد واقعی نشاندهنده تغییری بنیادین است: تبدیل هوش مصنوعی از یک «نویسنده کد» به یک «شریک تحلیل عملکرد» (Profiling Partner).
زمینه و ریشه مشکل
به گزارش توسعهدهنده، چند هفته پیش از رفع مشکل، API پرداخت در داشبورد «نقاط انتهایی کند» ظاهر شد. در حالی که تأخیر p50 (میانه تأخیر) روی ۱۸۰ میلیثانیه و در وضعیت سالمی بود، تأخیر p99 طی یک ماه به ۲.۱ ثانیه رسیده بود. این مشکل تا زمانی که یک تیکت پشتیبانی صراحتاً ذکر کرد «پرداخت گاهی کند است»، نادیده گرفته شده بود.
این «گاهی» دشوارترین بخش دیباگ است. تأخیرهای لبهای گریزان هستند چون تستهای فشار (Load Tests) معمولاً روی حالتهای متوسط تمرکز میکنند و لبههای عجیبی که باعث کندی شدید میشوند را از دست میدهند. در واقع، تستهای مصنوعی اغلب سناریوهای مرزی را که باعث ایجاد Tail Latency میشوند، شبیهسازی نمیکنند و به همین دلیل این مشکلات تنها در محیط تولید و با دادههای واقعی نمایان میشوند.
تیم پیش از روی آوردن به هوش مصنوعی، یک راهکار سنتی را امتحان کرد: افزودن یک ایندکس به یک ستون مشکوک در پایگاهداده. طبق مستندات این مورد، این کار تنها ۴۰ میلیثانیه از p50 کم کرد و هیچ تأثیری روی p99 نداشت. این نتیجه سیگنالی حیاتی بود؛ اگر مشکل اصلی در لبههای توزیع است، راهکار باید روی تفاوت درخواستهای کند با درخواستهای سریع تمرکز کند، نه روی طرح کلی پرسوجوهای متوسط (Average-case query plan). این یعنی مشکل در لایهای دیگر از استک قرار داشت و بهینهسازی دیتابیس تنها یک تلاش در مسیر اشتباه بود. این در حالی است که در برخی سناریوها، بهینهسازیهای دقیق در سطح دیتابیس میتواند توان عملیاتی را به شدت افزایش دهد، اما در اینجا ریشه مشکل در منطق کد بود.
فرآیند تشخیص با هوش مصنوعی
توسعهدهنده بهجای صرف یک روز کامل برای افزودن لاگها، استقرار مجدد و خیره شدن به ردپاهای سیستم (Traces)، از Claude Code به عنوان یک شریک پروفایلینگ استفاده کرد. او بهجای توصیف مشکل با متن، دادههای خام JSON را از سیستم ردیابی (Tracing Backend) مستقیماً به عامل (Agent) داد.
او ۴۰ ردپای مربوط به درخواستهایی که بیش از ۱.۵ ثانیه زمان برده بودند را به همراه کد منبع هندلر ارسال کرد و از هوش مصنوعی خواست الگو را پیدا کند و بفهمد درخواستهای کند چه ویژگی مشترکی دارند که درخواستهای سریع ندارند. او میخواست AI تفاوتهای ساختاری در دادههای ورودی این درخواستها را شناسایی کند.
در عرض چند دقیقه، هوش مصنوعی یک همبستگی دقیق یافت: هر درخواست کند، تعداد اقلام (line_items) بیش از ۱۲ عدد داشت. سفارشهای بالای ۱۲ قلم بهطور مداوم از مرز ۱.۵ ثانیه میگذشتند، در حالی که سفارشهای کوچک سریع بودند. این یافته، مسیر تحقیق را از یک مشکل کلی پایگاهداده به یک مشکل مقیاسپذیری خاص در رابطه با اندازه سفارش تغییر داد و فرضیه را از «کندی دیتابیس» به «پیچیدگی الگوریتم» منتقل کرد.
بازتولید باگ مرتبه دوم
برای اثبات این فرضیه، Claude Code یک اسکریپت بذر (Seed Script) نوشت تا سفارشهایی با تعداد اقلام مختلف (۱، ۵، ۱۲، ۲۵ و ۵۰) تولید کند و یک بنچمارک کوچک با استفاده از time.perf_counter() دور هندلر پرداخت ساخت تا زمان دقیق اجرای هر سناریو را اندازهگیری کند.
نتایج رشد غیرخطی را فاش کرد:
- ۱ قلم: ۴۲.۱ میلیثانیه
- ۵ قلم: ۵۸.۳ میلیثانیه
- ۱۲ قلم: ۲۱۰.۴ میلیثانیه
- ۲۵ قلم: ۹۸۰.۷ میلیثانیه
- ۵۰ قلم: ۲۳۴۰.۱ میلیثانیه
این جهش نمایی، وجود یک باگ با پیچیدگی زمانی $O(n^2)$ را تأیید کرد. هوش مصنوعی منطق را تا تابع apply_bundle_discounts ردیابی کرد. مشکل یک حلقه تودرتو بود: برای هر قلم کالا، کد دوباره کل لیست اقلام را اسکن میکرد تا جفتهای تخفیفی (Bundle Pairs) را پیدا کند. یعنی اگر ۵۰ قلم کالا وجود داشت، کد در واقع ۲۵۰۰ بار بررسی را انجام میداد.
این تابع برای دو سال «خوب» کار کرده بود چون سفارشهای متوسط فقط ۳ تا ۴ قلم داشتند و تأخیر ناشی از $O(n^2)$ در این مقیاس نامحسوس بود. اما قابلیت «سفارش عمده» (Bulk Order) که دو ماه پیش عرضه شده بود، اندازه سبدهای خرید را افزایش داد و باگی را که هیچکس فکر نمیکرد دوباره بررسی کند، فعال کرد. در واقع، تغییر در شکل دادهها باعث شد باگی که سالها خفته بود، بیدار شود و به یک گلوگاه جدی تبدیل گردد.
رفع مشکل و پاکسازی کد
راهکار شامل ایندکس کردن پیشفرض اقلام بر اساس ویژگی مورد نیاز برای بررسی تخفیف بود که عملیات را به یک جستوجوی خطی $O(n)$ تبدیل کرد. حالا بهجای اسکن کامل لیست در هر تکرار، هر قلم یک جستوجوی هدفمند در یک ایندکس گروهبندیشده انجام میدهد.
این تغییر، زمان پردازش سفارشهای ۵۰ قلمی را از ۲.۳ ثانیه به تنها ۱۳۹ میلیثانیه کاهش داد.
اما ارزش اصلی در توانایی هوش مصنوعی برای «جستوجوی همخانواده» (Sibling Search) بود. توسعهدهنده از عامل خواست کل کد را برای هر مورد دیگر از حلقههای تودرتو روی مجموعههای مشابه (مثلاً for x in items: for y in items:) جستوجو کند تا مطمئن شود هیچ «مین» دیگری در کدبیس وجود ندارد. این سطح از کنترل روی کدبیس، یادآور رویکردهای جدید در مدیریت عاملهای کدنویسی است که حتی با مدلهای محلی کوانتیده برای مدیریت دقیق تغییرات در مخازن بزرگ به کار میروند.
این جستوجو چهار مورد را شناسایی کرد:
- دو مورد مثبت کاذب: اینها عمدی و محدود بودند، مانند یک لیست کوچک ۳ تایی از گزینههای ارسال که تأثیری در عملکرد نداشت و پیچیدگی آن در مقیاس کوچک قابل چشمپوشی بود.
- یک باگ اصلی: همان تابع تخفیفات که قبلاً شناسایی شده بود.
- یک باگ نهفته: باگ دومی در مسیر رزرو موجودی (Inventory Reservation). این مورد هنوز در ردپاهای تولید ظاهر نشده بود چون سفارش به اندازه کافی بزرگی برای فعال کردن آن ثبت نشده بود؛ در واقع یک «مین» بود که منتظر انفجار بود و اگر شناسایی نمیشد، در آینده با افزایش حجم سفارشات باعث کندی شدید میشد.
هوش مصنوعی برای هر مورد دلایل خود را به زبان ساده توضیح داد و فرآیند خستهکننده بررسی دستی برای تشخیص اینکه آیا هر تطابق واقعاً محدود است یا خیر را حذف کرد و تریاژ را تسریع نمود.
تأیید نهایی
پیش از استقرار، توسعهدهنده بنچمارک را دوباره اجرا کرد و نمونهای از سفارشهای کند واقعی را روی کد اصلاحشده تست کرد. نتایج برای سفارشهای ۵۰ قلمی از ۲.۳ ثانیه به ۱۳۹ میلیثانیه رسید و ثبات عملکرد در مقیاسهای مختلف تأیید شد.
پس از استقرار، تأخیر p99 برای نقطه انتهایی پرداخت در عرض یک روز روی ۸۶۰ میلیثانیه تثبیت شد. تأخیرهای باقیمانده به تأخیرهای شبکه در درگاههای پرداخت (Payment Provider) مربوط بود، نه ناکارآمدی کد داخلی، که نشان میدهد اکنون کد در بهینهترین حالت خود قرار دارد.
درسهای آموخته شده
این تجربه چندین نکته کلیدی برای مهندسی عملکرد به همراه داشت:
۱. داده بر فرضیه اولویت دارد: حدس «احتمالاً مشکل از پایگاهداده است» رایج اما اغلب غلط است. استخراج دادههای واقعی Trace اجازه میدهد تا خودِ دادهها فرضیه را انتخاب کنند و از اتلاف وقت روی بهینهسازیهای بیاثر جلوگیری شود.
۲. دادههای خام بهتر از توصیف متنی هستند: دادن Traceهای JSON خام به عامل هوش مصنوعی بسیار مؤثرتر از خلاصه کردن مشکل است، زیرا به AI اجازه میدهد همبستگیها (مانند آستانه ۱۲ قلم کالا) را سریعتر و دقیقتر از انسان پیدا کند.
۳. باگهای خفته: باگهای عملکردی اغلب تا زمانی که تغییری در شکل دادهها (مانند معرفی سفارشات عمده) رخ ندهد، خفته میمانند و در تستهای اولیه دیده نمیشوند.
۴. شکار همخانوادهها: وقتی یک الگوی باگ پیدا شد، جستوجوی آن در کل کدبیس از حوادث آینده جلوگیری میکند و از تکرار اشتباهات مشابه در ماژولهای دیگر میکاهد.
۵. دقت در بنچمارک: استفاده از یک ابزار سنجش یکسان برای اعداد قبل و بعد، اطمینان میدهد که p99 واقعاً جابجا شده است، نه اینکه فقط امیدوار باشیم اصلاحیه کار کرده باشد.
این گردش کار، استاندارد بهینهسازی عملکرد را تغییر میدهد. بهجای چرخه تکراری «افزودن لاگ، استقرار و انتظار برای ترافیک»، توسعهدهندگان اکنون میتوانند از عاملها برای شکل دادن به فرضیات بر اساس دادههای خام و نوشتن فوری ابزارهای تست استفاده کنند.
برای یک توسعهدهنده معمولی، این به معنای خودکار شدن مرحله «تریاژ خستهکننده» است. توانایی تطبیق الگوی یک باگ خاص در کل مخزن کد، اثر «مینهای خفته» را از بین میبرد، جایی که رفع یک مشکل در یک بخش، آسیبپذیریهای مشابه را در جاهای دیگر باقی میگذارد.
در آینده شاهد ظهور «پروفایلینگ عاملمحور» (Agentic Profiling) به عنوان بخشی استاندارد از خط لوله CI/CD خواهیم بود، جایی که عاملهای هوش مصنوعی بهطور فعال به دنبال پسرفتهای پیچیدگی (Complexity Regressions) میگردند، پیش از آنکه این مشکلات به محیط تولید برسند و تجربه کاربر را تخریب کنند.
گام بعدی شما
- بهجای توصیف متنی مشکلات عملکردی، دادههای خام JSON یا Traceها را به مدلهای استدلالی بدهید.
- پس از رفع یک باگ پیچیدگی زمانی، از هوش مصنوعی بخواهید الگوهای مشابه (Sibling Search) را در کل مخزن کد جستوجو کند.
- برای هر تغییر در عملکرد، یک بنچمارک کوچک با دادههای متغیر (مثلاً تعداد ۱، ۱۰، ۱۰۰) بسازید تا رشد خطی یا نمایی بودن زمان اجرا را بسنجید.
اما داستان سختافزاری این تحول حتی شگفتانگیزتر است — به تحلیل ما دربارهی تراشههای Blackwell مراجعه کنید.




گفتگو