تصور کنید یک دستور برای راهاندازی سرور ارسال میکنید، اما اتصال اینترنت قطع میشود؛ شما پاسخی دریافت نمیکنید، اما در پسزمینه، یک ماشین لینوکس گرانقیمت با موفقیت بوت شده است. این «پاسخ نامرئی» — که زمانی رخ میدهد که اتصال شبکه پیش از رسیدن تاییدیه به کلاینت قطع شود — یکی از خطرناکترین و موذیترین حالتهای شکست در زیرساختهای ابری است. واکنش طبیعی انسان و برنامهها در این شرایط، تکرار درخواست (Retry) است، اما در سیستمی که منابع محاسباتی گرانقیمت را مدیریت میکند، یک تکرار ساده و بدون فکر میتواند منجر به ناکارآمدی فاجعهباری شود؛ چرا که باعث راهاندازی ماشین دوم و دوبرابر شدن هزینه برای یک اقدام واحد میشود. این موضوع یادآور چالشهای منطق تکرار در APIهای هوش مصنوعی است که میتواند منجر به اتلاف بودجه و زمان شود.
این چالش، دریچهای است به منطق پیچیده بکاند Mainbrella؛ جایی که مشکل بنیادی، جدایی میان «انجام عمل» و «مشاهده نتیجه» در مرزهای توزیعشده است. همانطور که در تحلیلهای پیشین ما دربارهی پایداری سیستمهای ابری اشاره کردیم، مدیریت وضعیت (State) در مقیاس بالا نیازمند حذف هرگونه ابهام در تاییدیه است.
برای درک راهکار Mainbrella، باید چرخه حیات یک پردازش معمولی را بررسی کنیم؛ برای مثال، فرآیندی که یک اسکریپت تحلیل را اجرا میکند، دادهها را از یک کانتینر مجزا میخواند و در نهایت یک فایل متریک (Metrics File) مینویسد. بیایید این پردازش را در یک جایگاه ماشین خاص، که آن را slot c17 مینامیم، دنبال کنیم. طبق مستندات فنی این پلتفرم، مشکل حتی پیش از بوت شدن هسته لینوکس آغاز میشود؛ این بحران با تصمیمگیری درباره مالکیت و بودجه شروع میشود.
در محیطهایی با تراکم بالای درخواست (High-Concurrency)، ممکن است دو درخواست ایجاد (Create) مجزا بهطور همزمان برسند، در حالی که حساب کاربر تنها یک جایگاه (Slot) باقیمانده دارد. اگر سیستم صرفاً یک شمارنده را بخواند، ماشین را بوت کند و سپس شمارنده را بهروزرسانی کند، یک «شرایط مسابقه» (Race Condition) رخ میدهد. در این حالت، هر دو درخواست مقدار «۱» را در شمارنده میبینند، هر دو دستور بوت را صادر میکنند و سیستم ناگهان خود را در وضعیت تخصیص بیش از حد منابع (Over-provisioned) میبیند. تا زمانی که این خطا شناسایی شود، گرانترین بخش فرآیند — یعنی تخصیص منابع محاسباتی — پیش از آن اتفاق افتاده است.
Mainbrella برای حل این بحران از Cloudflare Durable Objects استفاده میکند. یک شیء بادوام (Durable Object) به عنوان یک هماهنگکننده پایدار با فضای ذخیرهسازی اختصاصی خود عمل میکند و تضمین میکند که تمام درخواستها برای یک موجودیت خاص، بهصورت متوالی (Serialized) پردازش شوند.
فرآیند به این صورت است که API عمومی ابتدا کاربر احراز هویت شده و استحقاقهای پرداخت او را شناسایی کرده و سپس به یک هماهنگکننده اختصاصی برای آن کاربر (account:
اما صفبندی در ورودی، تنها لایه اول دفاع است. مشکل «پاسخ نامرئی» در لایههای عمیقتر استک نیز وجود دارد. هنگامی که یک ماشین در حال بوت شدن است، دستور شروع میتواند بدون اینکه فراخواننده نتیجه را ببیند، اجرا شود. اگر کلاینت دستور «ایجاد» را تکرار کند، سیستم باید بتواند بین درخواستی برای یک ماشین «جدید» و درخواستی برای «تایید» ماشینی که احتمالاً در حال بوت شدن است، تمایز قائل شود.
این امر نیازمند پیادهسازی دقیق «کلیدهای Idempotency» (Idempotency Keys) است. هر درخواست با یک شناسه منحصربهفرد برچسبگذاری میشود. وقتی بکاند درخواستی را دریافت میکند، بررسی میکند که آیا آن کلید خاص قبلاً پردازش شده است یا خیر. اگر کلید وجود داشته باشد و ماشین در حال بوت یا فعال باشد، سیستم نمونه جدیدی را شروع نمیکند؛ در عوض، وضعیت نمونه موجود را بازمیگرداند. این مکانیسم تضمین میکند که فرآیند بازیابی، گرانتر از شکست اولیه نباشد.
این الگوی «عمل بدون مشاهده» فراتر از بوت شدن ماشین گسترش مییابد. سناریویی را در نظر بگیرید که در آن یک درخواست خصوصی برای تغییر پیکربندی ماشین ارسال میشود، اما طول عمر درخواست از عضویت شبکهای کاربری که آن را آغاز کرده، بیشتر است. یا فرآیند کپچر دیسک (Disk Capture) را در نظر بگیرید: کپچر ممکن است در بکاند ذخیرهسازی بهطور کامل موفق شود، اما «دستگیره» (Handle) مورد نیاز برای بازیابی آن کپچر، به دلیل تایماوت در پاسخ نهایی API گم شود. در هر یک از این موارد، سیستم وارد وضعیت «موفقیت شبحوار» شده است؛ یعنی کار انجام شده، اما سندی از موفقیت در دفتر کل کلاینت وجود ندارد.
به نقل از تیم مهندسی Mainbrella، راهکار نهایی آنها پیادهسازی یک «حلقه تطبیق وضعیت» (State-Reconciliation Loop) است. در این مدل، سیستم بهجای تکیه صرف بر پاسخ لحظهای یک فراخوانی API، «وضعیت مطلوب» (مثلاً: ماشین c17 باید در حال اجرا باشد) را به عنوان منبع حقیقت (Source of Truth) در نظر میگیرد. بکاند بهطور مداوم وضعیت واقعی زیرساخت را با این وضعیت مطلوب مقایسه میکند. اگر ماشینی بوت شده باشد اما پاسخ آن ناپدید شده باشد، چرخه تطبیق بعدی تشخیص میدهد که ماشین وجود دارد و سالم است، و سپس داشبورد کاربر را بر اساس آن بهروز میکند. این رویکرد، مدل قابلیت اطمینان را از «تایید همزمان» به «سازگاری نهایی با تضمین وضعیت» (Eventual Consistency with Guaranteed State) تغییر میدهد.
علاوه بر این، تعامل بین کانتینرها لایه دیگری از پیچیدگی را اضافه میکند. وقتی اسکریپت analysis.py دادهها را از کانتینر دیگری میخواند، از یک مرز شبکهای عبور میکند. اگر کانتینر تامینکننده داده در میانه جریان (Mid-stream) کرش کند، اسکریپت تحلیل ممکن است متوقف شود یا با شکست مواجه شود. Mainbrella این موضوع را با پیادهسازی تایماوتهای سختگیرانه و «قطعکنندههای مدار» (Circuit Breakers) در مرز کانتینرها مدیریت میکند. اگر یک وابستگی پاسخگو نباشد، سیستم بهجای مصرف چرخههای محاسباتی در یک انتظار بیهوده، بهسرعت شکست را اعلام میکند (Fail Fast). این کار از تبدیل شدن یک جزء معیوب به یک تخلیه منابع در سطح کل سیستم جلوگیری میکند.
مدیریت کپچر دیسک نیز از منطق مشابهی در مورد دستگیرههای مجزا پیروی میکند. برای اطمینان از اینکه یک کپچر موفق هرگز «گم» نشود، سیستم دستگیره کپچر را پیش از آغاز جابهجایی واقعی دادهها، در یک ذخیرهساز متادیتای پایدار مینویسد. اگر فرآیند در نیمه راه شکست بخورد، سیستم میداند که یک کپچر تلاش شده است و میتواند یا آن را از سر بگیرد و یا دادههای ناقص را پاک کند. با ثبت «قصد» پیش از «عمل»، Mainbrella ریسک ایجاد منابع یتیم (Orphaned Resources) را که فضای ذخیرهسازی را بدون ارائه ارزش اشغال میکنند، حذف میکند.
در نهایت، معماری Mainbrella مطالعهای در «بیاعتمادی» است. این سیستم فرض میکند که شبکه شکست خواهد خورد، پاسخها ناپدید میشوند و کلاینتها بهشدت درخواستها را تکرار میکنند. با بهرهگیری از Durable Objects برای متوالیسازی، کلیدهای Idempotency برای ردیابی درخواستها و حلقههای تطبیق برای مدیریت وضعیت، سیستم تضمین میکند که فارغ از تعداد دفعاتی که یک درخواست «ایجاد» ارسال میشود، تنها یک ماشین بوت شود و کاربر در نهایت وضعیت صحیح زیرساخت خود را مشاهده کند.
هدف این است که رفتار سیستم در مواجهه با شرایط غیرقطعی شبکه، قطعی (Deterministic) شود. این رویکرد، «پاسخ نامرئی» را از یک شکست بحرانی به یک رویداد تأخیر جزئی تبدیل میکند و تضمین میکند که هزینه بازیابی هرگز از هزینه عملیات اصلی بیشتر نشود. از طریق این مکانیسمها، Mainbrella محیطی پایدار فراهم میکند که در آن ماشین بوت میشود و حتی اگر پاسخ ناپدید شود، سیستم آن را به خاطر میسپارد.
گام بعدی شما
- اگر در حال طراحی سیستمهای توزیعشده هستید، الگوی «ثبت قصد پیش از عمل» را برای جلوگیری از منابع یتیم پیاده کنید.
- برای حذف Race Condition در تخصیص منابع، از مکانیزمهای Serialization (مانند Durable Objects) بهجای شمارندههای ساده استفاده کنید.
- مدل تاییدیه سیستم خود را از Synchronous (همزمان) به Eventual Consistency (سازگاری نهایی) تغییر دهید تا اثر قطعیهای شبکه خنثی شود.
اما مدیریت این وضعیتها در لایه سختافزار و GPUها چالشهای متفاوتی دارد — به تحلیل ما دربارهی بهینهسازی استنتاج در خوشههای توزیعشده مراجعه کنید.




گفتگو