تیمی که یک Python runtime با حجم ۵.۵ مگابایت را به مرورگرها ارسال میکرد، متوجه شد که ۶۹٪ از خطاهای ثبتشده در طول یک اسپرینت اخیر، تحت یک عنوان واحد و گمراهکننده دستهبندی شدهاند و ۸۹٪ از آنها در واقع تایماوتهای شبکه (network timeouts) بودند. این گزارشدهی نادرست، توسعهدهندگان را به مسیر اشتباهی در عیبیابی هدایت کرد و بخش قابل توجهی از کاربران را با شکستهای بیصدای دانلود (silent download failures) مواجه کرد؛ مشکلی که هر اپلیکیشن تحت وب با داراییهای (assets) حجیم میتواند بهزودی با آن مواجه شود.
داشبورد گمراهکننده بود
سیستم ردیابی خطا، حوادث را بهطور خودکار بر اساس محل کد که برای اولین بار ظاهر میشوند، گروهبندی میکند. عنوان حاصل شبیه به یک باگ ساده در runtime loader به نظر میرسید، بنابراین کل اسپرینت صرف جستوجو در مسیرهای کدی شد که هرگز دچار تایماوت نشده بودند. وقتی تیم دادههای متا (metadata) زیربنایی را بررسی کرد، تصویر واقعی آشکار شد: اکثر شکستها اصلاً باگ نبودند، بلکه اتصالات شبکه متوقفشدهای بودند که باعث ایجاد تایماوت شده بودند.
نکته کلیدی: عنوان خطا صرفاً برای راحتی است، نه برای تشخیص دقیق. بهصورت دورهای دادههای خام را بررسی کنید تا تأیید کنید که عنوان اصلی واقعاً نشاندهنده چیست.
API اتصال مرورگر یک مقدار جایگزین (placeholder) ارائه داد
برای جلوگیری از طولانی شدن فرآیند دانلود ۵.۵ مگابایتی برای کاربران با سرعت پایین، توسعهدهندگان از Network Information API مرورگر (navigator.connection) استفاده کردند. این API برای هر بازدیدکننده جدید، پهنای باند ثابت ۱.۷ مگابیت بر ثانیه را گزارش میکرد.
مرورگرها زمانی که دادههای تاریخی برای یک کاربر جدید ندارند، یک مقدار پیشفرض ارائه میدهند. آن مقدار پیشفرض فقط یک نشانه است، نه یک سرعت قطعی. وقتی یک placeholder مشابه برای هر نشست (session) جدید ظاهر میشود، نشاندهنده این است که API هنوز برای آن مخاطب کالیبره نشده است.
نکته کلیدی: هر سیگنال شبکهای را که هرگز تغییر نمیکند، به عنوان یک مقدار جایگزین (fallback) در نظر بگیرید، نه یک معیار قطعی.
اسنپشاتهای تکباره غیرقابل اعتماد هستند
پس از کنار گذاشتن نشانه پهنای باند غیرقابل اعتماد، تیم به سراغ سیگنال متفاوتی رفت که به نظر میرسید در مجموعه تستهایشان کار میکند. یک اجرای تست با موفقیت انجام شد، اما تکرار تست به مدت سه بار، هر بار با شکست مواجه شد. سرعت شبکه بهطور مداوم نوسان دارد. کد فقط یک اسنپشات (snapshot) گرفته بود، تصمیمی دائمی اتخاذ کرده بود و سپس حتی اگر اتصال لحظاتی بعد تغییر میکرد، به کار خود ادامه میداد.
نکته کلیدی: یک اقدام دائمی را بر اساس یک بار خواندنِ یک هدف متغیر بنا نکنید. بهجای یک بار پرسوجو (polling) کردن، مشترک رویدادهای تغییر (change events) شوید.
اصلاحات عملی که تیم اجرا کرد
- اشتراک در تغییرات اتصال. بهجای اینکه
navigator.connectionرا تنها یک بار بخواند، کد اکنون به رویدادchangeگوش میدهد و اگر پهنای باند در حین دانلود کاهش یا افزایش یابد، واکنش نشان میدهد. - افزودن یک نگهبان (watchdog) برای «عدم پیشرفت». یک تایمر هر درخواستی را که پس از یک بازه زمانی کوتاه هیچ پیشرفتی نداشته باشد، لغو میکند تا مرورگر بتواند دوباره تلاش کند یا به حالت جایگزین برود.
- توقف تغییر CDN در میانهی دانلود. تغییر منبع یک فایل حجیم در یک لینک کند، انتقال را از صفر شروع میکند و باعث هدر رفتن بایتهای دریافتشده قبلی میشود. اکنون دانلود در تمام مدت خود به همان CDN که در ابتدا انتخاب شده بود، پایبند میماند.
- به تعویق انداختن کارهای سنگینِ کش کردن. وظایفی که حجم زیادی از دادهها را در کش مینویسند، تا زمانی که بارگذاری runtime تمام نشود به تعویق میافتند تا مسیر بحرانی (critical path) کوتاه باقی بماند.
اگر داشبوردهای شما تصویری بهطور غیرعادی مرتب و بینقص نشان میدهند، عمیقتر بررسی کنید. اگر یک اندازهگیری شبکه هرگز تغییر نمیکند، با آن مانند یک placeholder برخورد کنید. و اگر یک اسنپشات واحد، سرنوشت یک دانلود چند مگابایتی را تعیین میکند، در واقع روی یک سراب شرط بستهاید. این شرطبندیها خود را بهصورت شکستهای بیصدا نشان میدهند که اعتماد کاربر را از بین میبرند؛ چیزی که هیچ مقدار از کدهای هوشمندانه نمیتواند پس از وقوع، آن را بهطور کامل ترمیم کند.
