سایتهای استاتیک حالا هنگام انتشار مجدد هم بالا میمانند
انتشار مجدد یک سایت استاتیک در Mythex پیشتر فایلهای زنده را پاک میکرد و بعد build جدید را آپلود میکرد؛ پس در تمام مدت آپلود، برخی بازدیدکنندگان صفحهی 404 یا صفحهای را میدیدند که اسکریپتهایش گم شده بودند. یک آپلود ناموفق هم میتوانست سایت را خالی یا نیمهجدید باقی بگذارد. حالا build جدید روی build قدیمی نوشته میشود، صفحات آخر، و باقیماندهها چند دقیقه بعد حذف میشوند. یک اصلاح دوم باعث میشود انتشار مجدد به جای یک دقیقه در چند ثانیه دیده شود — یا برای فایلهایی مثل لوگو، تا یک روز.
سایت استاتیک چطور سرو میشود
سایت استاتیک سایتی است که فقط فایل است: صفحات HTML، جاوااسکریپت، CSS و تصویر. وقتی یکی را در Mythex منتشر میکنید، آن را build میکنیم، خروجی را در یک پوشهی مخصوص سایتتان روی object storage آپلود میکنیم، و از طریق یک شبکهی edge سرو میکنیم — سرورهایی نزدیک بازدیدکنندگان که فایلها را از storage میگیرند و برای مدتی نگه میدارند. (اگر تازهکار هستید، راهنمای ما دربارهی CDN چیست این ایده را توضیح میدهد.)
دو چیز اشتباه بود: ترتیب تغییر فایلها در انتشار مجدد، و مدت نگه داشتن نسخههای قدیمی.
مشکل ۱: آپلود اول سایت را خالی میکرد
انتشار مجدد همهچیز را در پوشهی سایت پاک میکرد و بعد build جدید را آپلود میکرد. تا وقتی آپلود طول میکشید — ۱۲ ثانیه برای یک سایت ۲۵۰ فایلی در تست محلی ما — بازدیدکنندهای که درخواستش هنوز در لبه کش نشده بود، یک 404 یا یک صفحهی HTML میگرفت که اسکریپتهایش ۴۰۴ برمیگرداندند.
آپلود ناموفق بدتر بود. پوشه از قبل خالی شده بود، و در یکی از دو مسیر آپلود ممکن بود index.html جدید پیش از فایلی که شکست خورده بود قرار بگیرد. یک انتشار که گزارش شکست داده بود میتوانست یک سایت نیمهجدید را زنده بگذارد.
راهحل: روی هم بنویس، صفحات آخر، حذف بعداً
- اول پاک نکنید. build جدید روی build قدیمی آپلود میشود.
- داراییها پیش از صفحات. صفحه نام اسکریپتها و استایلهایی را که لازم دارد میبرد، پس صفحات آخر آپلود میشوند. تا رسیدن صفحات جدید، بازدیدکننده صفحهی قدیمی را میگیرد، که نام داراییهای قدیمی را میبرد، و آنها هنوز سر جایشان هستند. وقتی صفحات جدید برسند، داراییهای جدیدی که نامشان برده میشود از قبل سر جایشان هستند.
- باقیماندهها را بعداً حذف کنید. قبل از آپلود، فهرست میگیریم که سایت از چه چیزهایی تشکیل شده. سه دقیقه پس از آپلود موفق، هر چیزی از آن فهرست که build جدید بازنویسی نکرده حذف میشود. سه دقیقه بیشتر از ۶۰ ثانیهای است که یک صفحه میتواند در لبه و مرورگر کش شود، پس بازدیدکنندهای که هنوز صفحهی قدیمی را دارد میتواند داراییهایی را که نام میبرد پیدا کند.
- اگر شک دارید، هیچ چیز را حذف نکنید. اگر فهرستگیری قبل از آپلود شکست بخورد، هیچ چیز حذف نمیشود. فایلهای باقیمانده بیضررند و انتشار بعدی پاکشان میکند. حذف بدون دانستن اینکه چه چیزی بوده، راهی است که سایت خالی میشود.
یک آپلود ناموفق حالا سایت قبلی را دقیقاً همانطور که بود باقی میگذارد.
این را از ابتدا تا انتها بهصورت محلی آزمایش کردیم، با اسکریپت واقعی آپلود، مسیر واقعی آپلود و سرو کردن از storage، و بازدیدکنندگانی با کش سرد لبه. با کد قدیمی، ۵۵ مورد از ۹۱ بازدید در انتشار مجدد خراب بود و ۶۱ مورد از ۹۲ در یک انتشار ناموفق. با کد جدید، صفر مورد خراب بود، در حدود ۳٬۵۰۰ بازدید که هر دو مسیر آپلود، یک آپلود ناموفق و انتشار بعد از آن را پوشش میدادند — و دقیقاً فایلهای باقیمانده حذف شدند.
مشکل ۲: انتشار مجدد دیر دیده میشد
حتی وقتی آپلود درست پیش میرفت، تغییرات دیر ظاهر میشدند. لبهی شبکه هر صفحه را ۶۰ ثانیه و هر فایل دیگر را یک روز نگه میداشت، و همین را به مرورگرها هم میگفت. فایلهایی با نام ثابت — logo.png، یک فونت، هر چیزی از پوشهی public/ — پس از انتشار مجدد تا یک روز میتوانستند نسخهی قدیمی را نشان دهند.
راهحل: نسخههای فایل زیر یک نشانهی build
وقتی لبه از API ما میپرسد سایت کجاست، پاسخ حالا یک نشانهی build دارد که هر بار انتشار جدیدی برسد تغییر میکند. لبه نسخههای خود را زیر آن نشانه ذخیره میکند. پس انتشار، به محض اینکه جستوجوی ۳۰ ثانیهای لبه منقضی شود، تبدیل به مجموعهای کاملاً تازه از کلیدهای کش میشود. نسخههای قدیمی دیگر هرگز پیدا نمیشوند، پس لبه میتواند نسخهها را یک روز نگه دارد بیآنکه هرگز نسخهای را بعد از انتشاری که جایگزینش شده سرو کند.
مرورگرها دو قاعدهی متفاوت میگیرند:
- خروجی build با هش — فایلهایی مثل
assets/index-CzLcxDO8.jsکه نامشان با تغییر محتوا عوض میشود — یک سال کش میشوند و immutable علامت میخورند. زیر آن نام هرگز نمیتواند کهنه باشد. - بقیه — صفحات و فایلهای با نام ثابت مثل لوگو — با
no-cacheعلامت میخورند، یعنی «قبل از استفاده بررسی کن». اگر چیزی عوض نشده باشد، پاسخ یک304 Not Modifiedکوچک و بدون بدنه است، چون حالا بررسیIf-None-Matchمرورگر را رعایت میکنیم.
تشخیص این دو از هم دقت میخواهد: فایلی که از public/assets/ کپی شده نام خودش را نگه میدارد، و کش یکسالهی assets/logo.png لوگوی قدیمی را یک سال پین میکند. پس یک فایل فقط وقتی هشدار حساب میشود که در پوشهی خروجی build باشد و نامش بخشی داشته باشد که شبیه هش باشد، نه یک کلمهی معمولی.
بهصورت محلی، صفحه و لوگوی جدید ۶ ثانیه پس از زنده شدن انتشار دیده میشدند. پیشتر ۶۰ ثانیه برای صفحه و یک روز برای لوگو بود.
دو اصلاح کوچکتر در این مسیر
- صفحات خالی از درخواستهای HEAD. درخواست
HEADفقط هدرهای صفحه را بدون بدنه میخواهد. پاسخهای خالی آن را کش میکردیم، پس بازدیدکنندهی بعدی آن صفحه، صفحهی خالی میدید. پاسخهایHEADدیگر کش نمیشوند. - یک صفحهی واقعی برای «اینجا چیزی نیست». نشانیای که هیچ اپی نداشت پیشتر یک متن ساده با عبارت «This published app is offline or the link was not found» و کد خطای 502 برمیگرداند. حالا یک صفحهی درست نشان میدهد، «There's no app here»، با وضعیت 404 و راهی برگشت به Mythex برای صاحب سایت. دامنههای سفارشی ناشناخته هم همین صفحه را میگیرند.
آنچه از آن برداشتیم
- ترتیب بخشی از درستی است. همان فایلها اگر در ترتیب دیگری آپلود شوند، فرق بین سایت خراب و سایت سالم است.
- دیر حذف کنید، و فقط چیزی را که میدانید. حذف با فهرستی که پیش از آپلود گرفته شده امن است؛ خالی کردن یک پوشه و امید به تمام شدن آپلود امن نیست.
- بر اساس نسخه کش کنید، نه بر اساس زمان. زمان انقضا حدس است دربارهی اینکه محتوا کی عوض میشود. کلیدی که با build عوض میشود یک واقعیت است.
مرتبط: سایت منتشرشدهی شما حالا هنگام انتشار مجدد بالا میماند. برای مطالب بیشتر دربارهی انتشار، مستندات را ببینید.