باگی که روی مانیتور دیده میشد ولی تو تستها نه
خلاصهٔ کاملتر
نویسنده تو بخش ادیت ویدیوی محصولش به اسم Voomero یه مشکل عجیب دید. وقتی نوار seek رو عقب میکشید، یه تیکه از صفحه، مخصوصاً تایملاین پایین، برای یه لحظه غیب میشد. این چشمکزدن روی مانیتور خودش کاملاً معلوم بود، ولی تو اسکرینشاتهای مرورگر headless (یعنی مرورگری که بدون پنجرهٔ قابلدیدن اجرا میشه) هیچ اثری ازش نبود. این بررسی رو با کمک Claude Code انجام داده که بیشتر اسکریپتهای اندازهگیری رو نوشته و اجرا کرده.
اول رفته سراغ مظنونهای همیشگی. تغییرات DOM رو موقع درگ لاگ کرده و دیده فقط همون چیزایی عوض میشن که باید: playhead، نوار پیشرفت و برچسبهای زمان. پس memo و useMemo اینجا کمکی نمیکرد. نشت حافظه هم نبود، چون بعد از شش بار درگ، تعداد نودهای DOM و event listenerها و حجم heap برمیگشت سر جاش. حافظهٔ GPU هم حداکثر ۲۱۲ مگابایت بود و از سقف رد نمیشد. از همه بدتر، ۶۶۱ فریمی که تو تست گرفته شد همه سالم بودن.
سرنخ اصلی از یه ضبط صفحهٔ شش و نیم ثانیهای اومد. از ۱۵۶ فریم، تو سهتاش یه بخش صفحه خالی بود. شکل این بخش خالی دنبال لبهٔ هیچ کامپوننتی نبود و یه برش افقی صاف بود. محل برش تو دو فریم ۴۵۱ پیکسل و تو سومی ۶۷۵ پیکسل بود. اختلافش ۲۲۴ پیکسل میشد، یعنی تقریباً یکچهارم ارتفاع پنجره.
دلیلش اینه که Chrome کل صفحه رو یهجا نقاشی نمیکنه. محتوا رو به لایهها تقسیم میکنه و هر لایه رو تیکهتیکه تو بخشهایی به اسم tile نقاشی میکنه. اینجا هر tile به پهنای پنجره و حدود ۲۲۴ پیکسل CSS ارتفاع داشت. playhead و نوارهای پیشرفت روی لایهٔ اصلی صفحه بودن، پس جابهجا شدن یه خط دوپیکسلی باعث میشد یه نوار پهن، با همهٔ thumbnailهاش، دوباره نقاشی بشه. تو یه درگ با ۱۲۰ حرکت موس، ۲۶۷ بار tile لایهٔ اصلی دوباره نقاشی شده بود.
حدس نویسنده اینه که زیر بار سنگین صحنهٔ گالری، Chrome گاهی فریم رو قبل از آماده شدن tileها نشون میده و اون بخش یه فریم خالی میمونه. خودش میگه این مرحله رو مستقیم ثبت نکرده و اینکه چرا درگ به عقب بدتر بود هم هنوز اندازهگیری نشده. راهحل اضافه کردن will-change: transform فقط به همون چند المان کوچیکی بود که موقع seek عوض میشن. با این کار Chrome اونا رو به یه composited layer جدا (یعنی لایهای که GPU جدا از بقیه سرهم میکنه) میبره و تعداد ریپینت لایهٔ اصلی از ۲۶۷ به ۹ رسید.
نویسنده تأکید میکنه که will-change رو نباید روی کل رابط کاربری پخش کرد. هر لایه حافظهٔ GPU میخوره و اگه کل تایملاین رو یه لایه کنی، باز هم المانهای متحرک با thumbnailها هملایه میمونن. درسی که گرفته اینه که باید زودتر صفحه رو ضبط میکرد و میفهمید ابزارهاش دارن مشکل رو بازتولید نمیکنن. هزینهٔ اصلی هم تو نقاشی مرورگر بود، نه تو کار JavaScript.
نکات کلیدی:
- تستهای headless با ۶۶۱ فریم سالم، چشمکزدنی که روی مانیتور دیده میشد رو نشون ندادن.
- برشهای خالی دقیقاً با ردیفهای tile کروم به ارتفاع ۲۲۴ پیکسل CSS جور درمیاومدن.
- با will-change: transform روی چند المان کوچیک، ریپینت لایهٔ اصلی از ۲۶۷ به ۹ رسید.
- هر لایهٔ جدا حافظهٔ GPU مصرف میکنه، پس این property فقط برای المانهای کوچیک و پرتغییر مناسبه.
- دلیل بدتر بودن درگ به عقب هنوز با اندازهگیری تأیید نشده.




