یک محیط اجرای پایتون (Python playground) مبتنی بر مرورگر که یک runtime ۵.۵ مگابایتی را ارائه میداد، برای کاربرانی با اتصالات کند، شروع به بروز خطاهای بیصدا کرد. مقصر اصلی، استفاده نادرست از Network Information API و یک داشبورد گروهبندی خطا بود که مشکل را اشتباه برچسبگذاری کرده بود. این باگ هفتهها پنهان ماند، وقت توسعهدهندگان را تلف کرد و بخشی از کاربران را از اجرای کد ناتوان ساخت.
نحوه بروز مشکل
ردیاب خطای محیط اجرا، یک پیام تک و چشمگیر را نمایش داد: “undefined is not an object.” عنوان پیام نشاندهنده یک غلط تایپی ساده در جاوااسکریپت بود، بنابراین تیم به دنبال یک مسیر کد غیرموجود رفت. وقتی متادیتای خام را بررسی کردند، متوجه شدند که ۸۹٪ از آن موارد در واقع تایماوتهای شبکه (network timeouts) بودهاند. داشبورد اولین خطایی را که دریافت میکرد برمیداشت و از آن برای نامگذاری کل دسته استفاده میکرد، که باعث پنهان شدن نوع واقعی خطا میشد.
درس ۱ – عناوین داشبورد میتوانند فریبنده باشند
یک داشبورد که حوادث را تجمیع میکند، تنها زمانی کمککننده است که منطق تجمیع آن منعکسکننده علت واقعی هر رویداد باشد. در اینجا، گروهبندی بر اساس مکان به جای علت خطا، تصویری نادرست از یک باگ سمت کلاینت (client-side) ارائه داده بود. نکته اصلی: هرگز یک مشکل را صرفاً بر اساس عنوان یک داشبورد حل نکنید. پیش از تخصیص منابع، نمونهای از رویدادهای زیربنایی را استخراج کرده و آنچه واقعاً در حال رخ دادن است را تأیید کنید.
درس ۲ – مقادیر جایگزین (placeholder)، اندازهگیری نیستند
برای جلوگیری از بارگذاری runtime سنگین برای کاربرانی با لینکهای کند، کد از Network Information API استفاده کرد و ویژگی downlink را که سرعت را بر حسب مگابیت بر ثانیه گزارش میدهد، خواند. در اولین بازدید، کروم اغلب به جای یک اندازهگیری واقعی، یک مقدار جایگزین (placeholder) برمیگرداند. منطق برنامه با آن مقدار جایگزین مانند یک اتصال سریع برخورد کرد و از بهینهسازی صرفنظر کرد، که در عمل باعث مسدود شدن همان کاربرانی شد که قرار بود به آنها کمک کند.
با هر مقدار پیشفرض یا مقدار نگهبان (sentinel value) به عنوان «بدون داده» برخورد کنید. یک مقدار جایگزین باید باعث اجرای یک استراتژی جایگزین (fallback strategy) شود، نه اینکه به عنوان یک سرعت واقعی تفسیر گردد.
درس ۳ – شرایط شبکه تغییر میکند، بنابراین یک تصویر لحظهای (snapshot) غیرقابل اعتماد است
پس از رفع مشکل downlink، تیم به بررسی effectiveType روی آورد که اتصالات را به دستههایی مانند "4g"، "3g" و غیره تقسیم میکند. یک تست آزمایشگاهی سریع با موفقیت انجام شد، اما همان تست که لحظاتی بعد دوباره اجرا شد، با شکست مواجه گشت. اتصالات موبایل نوسان دارند؛ یک کاربر میتواند در یک ثانیه روی یک لینک سریع 4G باشد و ثانیه بعد به یک 3G کند سقوط کند. بررسی اتصال تنها در هنگام بارگذاری صفحه، یک قمار است.
رویکرد صحیح این است که در رویداد change در شیء Network Information اشتراک (subscribe) کنید و به جای اتخاذ یک تصمیم یکباره، به هرگونه تغییر در پهنای باند واکنش نشان دهید.
تغییراتی که تیم اعمال کرد
- دانلود دو مرحلهای – اکنون runtime با یک فایل بوتاسترپ (bootstrap) بسیار کوچک شروع میشود. اگر اتصال به عنوان کند شناسایی شود، فایل بوتاسترپ بقیه runtime را در تکههای کوچک دریافت میکند تا احتمال قطع کامل دانلود کاهش یابد.
- مانیتورینگ زنده – به جای یک بار خواندن
downlink، کد اکنون به رویدادهایchangeگوش میدهد و استراتژی دانلود را در لحظه تنظیم میکند. - انتخاب منبع پایدار – پیش از این، سیستم در میانه دانلود، زمانی که یک نقطه انتهایی (endpoint) سریعتر ظاهر میشد، CDN را تغییر میداد. در یک لینک کند، این کار باعث میشد دانلود از صفر شروع شود و مشکل را دوچندان کند. منطق جدید، منبع را برای مدت زمان دانلود ثابت نگه میدارد.
- نوشتن تأخیری در کش – عملیات سنگین کش که قبل از قابل استفاده شدن اپلیکیشن اجرا میشدند، اکنون تا پس از شروع runtime به تعویق میافتند تا پهنای باند برای دانلود حیاتی آزاد شود.
پیامدهای گستردهتر
برای توسعهدهندگانی که ابزارهای مبتنی بر وب میسازند، تغییرپذیری شبکه یک دغدغه اصلی است. یک شکست بیصدا در یک لینک کند، کاربران را ناامید کرده و تلهمتری (telemetry) را منحرف میکند که باعث میشود تیمها در مسیر اشتباه عیبیابی قرار بگیرند. در این مورد، تفسیر نادرست دادهها باعث هفتهها تحقیق بیثمر شد.
آنچه باید در آینده زیر نظر داشت
نکته کلیدی: وقتی دادهها بیش از حد تمیز به نظر میرسند، احتمالاً یک مقدار جایگزین هستند؛ وقتی عنوان یک داشبورد به یک باگ واحد اشاره میکند، عمیقتر کاوش کنید؛ و وقتی تصمیمی را بر اساس یک بار خواندن شبکه میگیرید، در واقع روی یک هدف متحرک شرطبندی کردهاید. تطبیق با این واقعیتها، شکستهای بیصدا را به رویدادهای قابل پیشبینی و قابل بازیابی تبدیل میکند.
