گرفتن اپهایی که درست بعد از انتشار کرش میکنند
در ۲۵ سپتامبر، سه انتشار موفق اعلام شدند با اینکه API اپ چند ثانیه بعد از هر بوت کرش میکرد. چون «موفق» شده بودند، هیچ rollbackای اجرا نشد؛ یک بررسی پسزمینه چند دقیقه بعد آنها را ناموفق اعلام کرد و کل سایت به ۴۰۴ رفت. بررسی سلامت کرشها را در فهرست رویدادهای پلتفرم میزبانی میشمرد — فهرستی که فقط پنج رویداد آخر هر ماشین را نگه میدارد، پس یک حلقهی کرش آن را پر نگه میدارد و شمارش هرگز بالا نمیرود. حالا زمان جدیدترین کرش را مقایسه میکند، و یک انتشار با کرش مداوم ناموفق میشود و نسخهی قبلی بازمیگردد.
انتشار منتظر چه چیزی است
وقتی یک اپ با بکاِند را در Mythex منتشر میکنید، نسخهی جدید روی ماشینهای پلتفرم میزبانی ما مستقر میشود و یک health check تصمیم میگیرد آیا انتشار زنده شد یا شکست خورد. اگر شکست خورده باشد، یک rollback نسخهی قبلی را برمیگرداند.
سختی کار این است که «شروع شده» همان «سرویسدهی» نیست. پلتفرم بهمحض بالا آمدن یک ماشین، آن را شروعشده گزارش میکند. اپی که یک ثانیه بعد از بوت کرش میکند فوراً دوباره راهاندازی میشود و لحظهای بعد دوباره شروعشده خوانده میشود. پس health check در طول زمان چند لایه پیدا کرده بود:
- دو بار نگاه کن. بعد از اینکه یک ماشین شروعشده خوانده شد، کمی صبر کن و دوباره نگاه کن. کرش هنگام بوت اغلب در نگاه دوم خودش را نشان میدهد.
- دنبال حلقههای کرش بگرد. اگر پلتفرم گزارش دهد که یک ماشین مدام کرش کرده و دیگر راهاندازی مجددش را رها کرده، شکست بخور.
- کرشها را از وقتی انتشار شروع شد بشمار. دو نگاه «شروعشده» ممکن است یک راهاندازی مجدد را در میان داشته باشند. پس اگر از وقتی این انتشار شروع شد هر کرشی رخ داده، یک بار سوم نگاه کن؛ اگر کرشها بیشتر از نگاه قبلی باشند، اپ پایدار نیست.
بررسی سوم همان بود که اینجا شکست خورد.
چرا شمارش ثابت ماند
اطلاعات کرش از فهرست رویدادهای هر ماشین میآید: شروعها، خروجها، راهاندازیهای مجدد. پلتفرم فقط پنج رویداد آخر هر ماشین را نگه میدارد.
اپی را تصور کنید که ۲.۵ ثانیه بعد از هر بوت کرش میکند، مثل یکی از آنها. هر چرخه یک کرش و یک شروع اضافه میکند. بعد از چند چرخه، فهرست پر است. از آن به بعد، هر رویداد جدید یکی از قدیمیها را بیرون میراند. فهرست به جلو میلغزد، اما همیشه تقریباً همان ترکیب را دارد: چند کرش و چند شروع.
پس «چند کرش از وقتی انتشار شروع شده؟» در هر نگاه همان عدد را برمیگرداند. نگاه دوم، مثلاً دو کرش دید. نگاه سوم هم دو کرش دید — کرشهای متفاوت، اما همان تعداد. بررسی نتیجه گرفت که کرشها متوقف شدهاند و انتشار را قبول کرد.
این سه بار در ۲۵ سپتامبر، روی دو اپ، اتفاق افتاد. هر بار:
- انتشار زنده اعلام شد،
- هیچ rollbackای اجرا نشد، چون چیزی شکست نخورده بود،
- reconciler ما، یک job پسزمینه که دیپلویها را بعد از وقوع بررسی میکند، چند دقیقه بعد دیپلوی را ناموفق اعلام کرد،
- و کل سایت ۴۰۴ برگرداند.
انتشاری که بهموقع شکست بخورد قابل بازیابی است: rollback نسخهی قبلی را برمیگرداند. انتشاری که چند دقیقه دیرتر، بعد از اعلام موفقیت، شکست بخورد، از قبل rollback خود را رد کرده است.
چطور پیدایش کردیم
محلی بازتولیدش کردیم: یک API سالم را گرفتیم، اجازه دادیم بخوابد، و سپس با تغییری که هنگام بوت کرش میکرد دوباره منتشرش کردیم. بررسی سلامت آن را زنده اعلام کرد.
این با تولید همخوانی داشت: در هر سه انتشار، اپ ثانیهها بعد از هر بوت میمرد، اما عدد کرشهایی که بررسی مقایسه میکرد بین دو نگاه هرگز بالا نرفت.
راهحل: جدیدترین کرش را مقایسه کن، نه شمارش را
بهجای شمردن کرشها، بررسی حالا زمان آخرین کرش از وقتی انتشار شروع شد را نگاه میکند.
اگر نگاه دوم هر کرشی را از وقتی انتشار شروع شده نشان دهد، بررسی صبر میکند و یک بار سوم نگاه میکند. اگر جدیدترین کرش در نگاه سوم دیرتر از جدیدترین کرش در نگاه دوم باشد، ماشین دوباره در فاصلهی آن دو کرش کرده است. پایدار نیست، و انتشار با پیامی شکست میخورد که میگوید ماشین از وقتی منتشر شده بارها کرش کرده و هنوز در حال راهاندازی مجدد است.
این بدون توجه به کوتاه بودن فهرست رویدادها کار میکند. یک پنجرهی لغزان پنجتایی میتواند تعداد کرشها را پنهان کند، اما نمیتواند پنهان کند که یک کرش جدید همین الان اتفاق افتاده — جدیدترین رویداد همیشه همان چیزی است که نگه داشته میشود.
با این تغییر، بازتولید محلی درست رفتار میکند: انتشار شکست میخورد و تصویر قبلی بازمیگردد. همچنین تستی داریم که رفتار پلتفرم را شبیهسازی میکند — هر نگاه یک کرش و یک شروع اضافه میکند و فهرست را به پنج مورد کوتاه میکند — و بررسی میکند که حکم شکست باشد، نه موفقیت.
آنچه از این ماجرا یاد گرفتیم
- شکل دادهای را که میشمارید بدانید. شمارش روی یک فهرست محدود، وقتی فهرست پر شود دیگر معنایی ندارد. این محدودیت پنهان نبود؛ فقط بررسی را طوری طراحی نکرده بودیم که با آن کار کند.
- «آیا چیز تازهای اتفاق افتاده؟» را به «چند بار اتفاق افتاده؟» ترجیح دهید. جدیدترین زماننما از کوتاه شدن فهرست جان سالم به در میبرد. مجموع این کار را نمیکند.
- شکست دیرهنگام بدتر از شکست زودهنگام است. همان بیلد خراب اگر انتشار شکست بخورد و rollback شود بیضرر است، و اگر اول موفقیتش اعلام شود، سایت را پایین میآورد.
- با شرایط واقعی بازتولید کنید. این باگ به ماشینی نیاز داشت که از قبل آنقدر کرش کرده بود که فهرست رویدادهایش پر شده باشد. یک کرش سریع در تست آن را نشان نمیداد.
این اصلاح با اصلاحی دیگر از همان روز همراه است، که کاری کرد rollback نسخهی قدیمی را برگرداند بهجای حذف آن: یک انتشار مجدد ناموفق هرگز بکاِند سالم را حذف نمیکند. اگر اپ خودتان بهخاطر کرش در هنگام شروع منتشر نمیشود، عیبیابی انتشار در مستندات و راهنمای ما دربارهی عیبیابی اپی که با هوش مصنوعی ساخته شده نقاط شروع خوبی هستند.