تصور کنید یک خطای کوچک در کدنویسی، بهجای اینکه فقط یک درخواست را رد کند، تبدیل به بمبی شود که کل زیرساخت تولید شما را به زانو درآورد. این دقیقاً همان اتفاقی است که در ۲۴ اوت ۲۰۲۶ رخ داد و یک نقص منطقی در مدیریت خطاها را به یک قطعی کامل تبدیل کرد. در حالی که شکست این روز در ابتدا شبیه به یک اضافهبار معمولی سرور یا فروپاشی مدل (Model Collapse) به نظر میرسید، اما در واقع توسط یک حلقهٔ تکرار (Retry Loop) ایجاد شده بود که یک تخلف دائمی از قرارداد API را به عنوان یک اختلال موقت تلقی میکرد. این نقص منطقی باعث شد یک Worker پسزمینه که برای تلخیص رشتهگفتگوهای پشتیبانی و تبدیل آنها به گزارشهای روزانه طراحی شده بود، به کاتالیزوری برای یک قطعی کامل در محیط Production تبدیل شود.
بسیاری از توسعهدهندگان کد خطای HTTP 429 را صرفاً به معنای محدودیت نرخ درخواست (Rate Limit) میبینند. اما در محیطهای عملیاتی، این فرض میتواند مرگبار باشد. وقتی سیستمی بهطور خودکار درخواستی را تکرار میکند که سرور پیشتر بهدلیل حجم زیاد رد کرده است، فقط شکست نمیخورد، بلکه یک حلقهٔ بازخورد ایجاد میکند که به درگاه API ضربه میزند.
همانطور که در تحلیلهای قبلی ما دربارهی پایداری سیستمهای مبتنی بر مدلهای زبانی اشاره کردیم، مدیریت استثناها در لایهی شبکه، حیاتیترین بخش معماری است. در این حادثه، یک عامل (Agent) که وظیفهی تلخیص رشتهگفتگوهای پشتیبانی را داشت، به دلیل اشتباه در تخمین حجم داده، تبدیل به عامل تخریب شد. این وضعیت یادآور شکستهای خاموشی است که در آن عاملهای هوش مصنوعی علیرغم نمایش وضعیت موفقیت در مانیتورینگ، در پسزمینه دچار فروپاشی میشوند.
کالبدشکافی شکست
قطعی در ساعت ۱:۴۷ بامداد آغاز شد و با الگویی شروع شد که در ابتدا تصادفی به نظر میرسید. طبق گزارش dev.to، حدود یکششم درخواستها خطای ۴۲۹ میدادند، در حالی که سایر درخواستها پس از ۹۰ ثانیه با خطای Timeout مواجه میشدند. از آنجایی که خط لوله (Pipeline) تمام ارزیابیهای قبلی را با موفقیت پشت سر گذاشته بود، تیم فنی در ابتدا به «تیکهای سبز» اعتماد کردند و علت ریشهای را نادیده گرفتند.
بررسیهای دقیقتر نشان داد که شمارندهٔ تکرار سیستم، یک بستهٔ دادهٔ شکستخورده را ۱۱ بار از صف عبور داده است. این یک اتفاق تصادفی نبود، بلکه یک حلقهٔ تکرار بود. نکته تکاندهنده این بود که این Job پیش از این فروپاشی، به مدت دو هفته بدون ثبت حتی یک خطای واحد اجرا شده بود. این نوع رفتار مشابه مکانیزمهای خودترمیمی است که گاهی با پنهان کردن خطاهای حیاتی، مسیر را برای یک شکست بزرگتر هموار میکنند.
فرضیات عیبیابی
تیم فنی برای یافتن علت بیداری اجباری در ساعت ۲ صبح، توالی خاصی از فرضیات را برای تشخیص عیب دنبال کرد:
فرضیه اول: محدودیتهای لایه رایگان. آنها گمان کردند سهمیهٔ دقیقهای (Per-minute quota) تمام شده است. برای حل این مشکل، یک فاصله زمانی (Sleep interval) بین درخواستها ایجاد کردند، اما شکستها با همان فرکانس قبلی ادامه یافتند.
فرضیه دوم: ناپایداری شبکه. با احتمال وجود مشکل در سمت ارائهدهنده، تیم تصمیم گرفت ارائهدهندهٔ شبکه را بهطور کامل تغییر دهد. این اقدام منجر به رفتاری دقیقاً مشابه شد و بدین ترتیب شبکه از دایرهٔ متهمان خارج شد.
شکاف در شمارش توکنها
نقطهٔ عطف زمانی رخ داد که تیم بدنهٔ دقیق درخواست را در کنار کد وضعیت ثبت کرد. آنها متوجه تفاوت فاحشی در نحوهٔ شمارش توکن (Token) — تکههای کوچکی از متن، شبیه برشهای یک کیک طولانی که مدل تکهتکه میخورد — شدند. آنها دریافتند که سرور درخواست را بر اساس شمارش توکنهای خودش رد میکند، نه بر اساس تخمین کلاینت.
- تخمین سمت کاربر: توکنساز (Tokenizer) حجم پرامپت تلخیص را ۸,۴۰۰ توکن تخمین زده بود.
- شمارش سمت سرور: سرور عدد ۱۰,۳۰۰ توکن را ثبت کرد، زیرا تاریخچهٔ کامل گفتگو را که در مرحلهای مجزا اضافه شده بود، در شمارش نهایی لحاظ کرده بود.
به دلیل عبور از حد مجاز (Allowance threshold)، سرور خطای ۴۲۹ را با پیامی ارسال کرد که شبیه به محدودیت نرخ درخواست بود. منطق تکرار (Retry Logic) که فقط به کد وضعیت نگاه میکرد و بدنهٔ پاسخ را نمیخواند، با وفاداری تمام همان بستهٔ حجیم را ۱۱ بار ارسال کرد. یک تصمیم ساده، یک تخلف از قرارداد API را به ۱۱ تخلف یکسان تبدیل کرد.
خطر پسروی همزمان
فاجعه با نبود «لرزش» (Jitter) در منطق تکرار تشدید شد. کد اصلی از یک حلقهٔ ساده استفاده میکرد: for attempt in range(10): response = call_model(payload). در صورت بروز خطای ۴۲۹، دستور time.sleep(2 ** attempt) اجرا میشد.
چون تمام کارکنان (Workers) از این برنامهٔ زمانی پسروی نمایی (Exponential Backoff) یکسان پیروی میکردند، همگی برای مدت زمانی برابر به خواب میرفتند و در یک لحظه بیدار میشدند تا دوباره به درگاه API حمله کنند. این وضعیت منجر به یک شکست متامدیک (Metastable Failure) شد.
به نقل از مخزن Arc Ops، تکرارهای بدون بودجهبندی میتوانند منجر به جهشهای عظیم شکست شوند. در یک نمونه ذکر شده در این منبع، افت کوتاهی که در حالت عادی باید تنها ۶۰۰ درخواست را شکست میداد، در نهایت باعث شکست ۴,۵۴۳ درخواست شد، زیرا کلاینتها دقیقاً همزمان و پس از رفع علت اصلی، تلاش مجدد کردند. این چالش دقیقاً در راهکارهای Arc Ops برای بودجهبندی مجدد تلاشهای تکراری مورد بررسی قرار گرفته است تا از فروپاشیهای سیستمیک جلوگیری شود.
راهکار سه مرحلهای
برای رفع قطعی، تیم سه تغییر معماری خاص اعمال کرد که هیچکدام به تغییر خود مدل مربوط نبود:
- تجزیهٔ پاسخ (Response Parsing): سیستم اکنون بدنهٔ پاسخ را برای هر وضعیت غیر از ۲۰۰ بررسی میکند. با ثبت تعداد توکنهای اعلام شده توسط سرور، یک رد قرارداد به عددی تبدیل میشود که میتوان آن را با تخمین کلاینت مقایسه کرد.
- سقف سخت سمت کاربر: یک بررسی بودجهٔ جدید اضافه شد که تاریخچهٔ کامل گفتگو را با همان قوانینی که سرور استفاده میکند میشمارد. سیستم اکنون پیش از آنکه درخواست به شبکه برسد، خطای
BudgetExceededصادر کرده و Job را رد میکند. - تکرارهای محدود و لرزشی: پسروی همزمان با تکرارهای محدود به حداکثر ۳ بار جایگزین شد. اکنون از دستور
time.sleep((2 ** attempt) + random.uniform(0, 1.5))استفاده میشود تا از ایجاد ضربات همزمان جلوگیری شود.
اعتبارسنجی و محیط ایزوله
تیم برای تست این اصلاحات بدون مصرف اعتبار پولی، Job را روی یک سرور رایگان بازسازی کرد و آن را به MonkeyCode متصل نمود. MonkeyCode یک پروژه متنباز است که دسترسی رایگان به مدلها شامل سهمیه ۱۰ میلیون توکنی و گزینه سرور رایگان را فراهم میکند. این امکان باعث شد تیم بتواند دهها بار سناریوی بازتولید شکست را در یک بعدازظهر اجرا کند.
این تمرین بخشی از تلاشهای تبلیغاتی محصول MonkeyCode برای بازتولید دقیق حالت شکست در یک محیط کنترلشده بود. با این حال، به توسعهدهندگان هشدار داده میشود که سهمیههای رایگان تغییر میکنند و باید به عنوان محیط Sandbox در نظر گرفته شوند، نه به عنوان تضمینهای عملیاتی.
این حادثه این فرض را تغییر میدهد که کدهای خطا، نتیجهٔ نهایی هستند. خطای ۴۲۹ تنها یک فرضیه است؛ میتواند محدودیت نرخ، مشکل سهمیه، تخلف در حجم داده یا رد شدن توسط سرور باشد. ثبت بدنهٔ پاسخ، ارزانترین و مؤثرترین ابزار نظارتی است که یک توسعهدهنده میتواند به خط لولهٔ هوش مصنوعی خود اضافه کند.
برای کسانی که حجم کاری بالایی دارند، درس روشن است: منطق تکرار، بخشی از رفتار سیستم است. شکستی که ۱۱ بار تکرار میشود، یک نقص طراحی است، نه بدشانسی. Jitter برای دلیلی وجود دارد و تکرارهای محدود (Bounded Retries) برای دلیلی بهتر.
توسعهدهندگان باید اکنون حلقههای تکرار خود را برای وجود Jitter بازرسی کنند و بودجهبندی سختگیرانه توکن در سمت کلاینت را پیادهسازی نمایند. اگرچه سقف سمت کلاینت شما را از مدلی که ناگهان طول خروجیاش تغییر میکند نجات نمیدهد — به این معنی که اعتبارسنجی خروجی برای ویژگیهای Real-time همچنان ضروری است — اما از قطعیهای سیستمیک جلوگیری میکند. اگر در حال ارزیابی یک مدل برای محیط Production هستید، به یاد داشته باشید که لایه رایگان یک Sandbox مفید است، اما جایگزینی برای تست قرارداد (Contract Test) در برابر ارائهدهندهای که در نهایت به او پول پرداخت خواهید کرد، نیست.
گام بعدی شما
- تمام حلقههای Retry را بررسی کنید و برای جلوگیری از ضربات همزمان، مقدار Jitter (تصادفی) به زمان استراحت اضافه کنید.
- منطق شمارش توکن در سمت کلاینت را با مستندات آخرین نسخهٔ API ارائهدهنده تطبیق دهید.
- سیستم لاگینگ خود را بهگونهای تغییر دهید که در صورت دریافت خطاهای ۴xx و ۵xx، بدنهٔ پاسخ (Response Body) را بهطور کامل ذخیره کند.
اما داستان سختافزاری این تحول حتی شگفتانگیزتر است — به تحلیل ما دربارهی تراشههای Blackwell مراجعه کنید.




گفتگو