Cascading cache invalidation. Bagian 1

Selama beberapa tahun sekarang, karena hampir setiap artikel tentang pendekatan lanjutan untuk caching merekomendasikan penggunaan teknik berikut dalam produksi:

  • Menambahkan informasi nama file tentang versi data yang terkandung di dalamnya (biasanya dalam bentuk hash data dalam file).
  • Menyetel tajuk HTTP Cache-Control: max-age dan Expires , yang mengontrol waktu caching materi (yang menghilangkan validasi ulang materi yang relevan bagi pengunjung yang kembali ke sumber daya).



Semua alat untuk membangun proyek yang saya tahu mendukung menambahkan ke file hash nama isinya. Ini dilakukan dengan menggunakan aturan konfigurasi sederhana (seperti yang ditunjukkan di bawah):

 filename: '[name]-[contenthash].js' 

Dukungan luas seperti ini untuk teknologi ini telah mengarah pada fakta bahwa praktik ini telah menjadi sangat umum.

Pakar kinerja proyek web juga merekomendasikan menggunakan teknik pemisahan kode . Teknik-teknik ini memungkinkan memecah kode JavaScript menjadi bundel terpisah. Kumpulan tersebut dapat diunduh oleh browser secara paralel, atau bahkan hanya jika diperlukan, atas permintaan browser.

Salah satu dari banyak keuntungan pemisahan kode, khususnya, terkait dengan teknik caching terbaik, adalah bahwa perubahan yang dilakukan pada file terpisah dengan kode sumber tidak menyebabkan pembatalan cache seluruh bundel. Dengan kata lain, jika pembaruan keamanan dirilis untuk paket npm yang dibuat oleh pengembang "X", dan konten node_modules difragmentasi oleh pengembang, maka hanya fragmen yang berisi paket yang dibuat oleh "X" yang harus diubah.

Masalahnya di sini adalah jika semua ini digabungkan, maka ini jarang mengarah pada peningkatan efisiensi caching data jangka panjang.

Dalam praktiknya, perubahan pada salah satu file kode sumber hampir selalu mengakibatkan pembatalan lebih dari satu file output dari sistem paket rakitan. Dan ini justru karena fakta bahwa hash telah ditambahkan ke nama file yang mencerminkan versi isi file-file ini.

Masalah Versi Nama File


Bayangkan Anda telah membuat dan menggunakan situs web. Anda telah menggunakan pemisahan kode, sebagai akibatnya, sebagian besar kode JavaScript situs Anda dimuat berdasarkan permintaan.

Di diagram dependensi berikutnya, Anda dapat melihat titik entri basis kode - fragmen root main , serta tiga fragmen dependensi yang dimuat secara tidak sinkron - dep1 , dep3 dan dep3 . Ada juga fragmen vendor berisi semua dependensi situs dari node_modules . Semua nama file, sesuai dengan pedoman caching, termasuk hash dari isi file-file ini.


Pohon dependensi modul JavaScript tipikal

Karena dep3 dan dep3 mengimpor modul dari fragmen vendor , maka di bagian atas kode mereka dihasilkan oleh dep3 proyek, kami kemungkinan besar akan menemukan perintah impor yang terlihat seperti ini:

 import {...} from '/vendor-5e6f.mjs'; 

Sekarang mari kita pikirkan tentang apa yang akan terjadi jika isi dari fragmen vendor berubah.

Jika ini terjadi, hash dalam nama file yang sesuai juga akan berubah. Dan karena tautan ke nama file ini ada di perintah impor untuk dep3 dan dep3 , maka perintah-perintah impor ini perlu diubah:

 -import {...} from '/vendor-5e6f.mjs'; +import {...} from '/vendor-d4a1.mjs'; 

Namun, karena perintah impor ini adalah bagian dari isi dari dep3 dan dep3 , mengubahnya berarti bahwa hash dari isi file dep3 dan dep3 juga dep2 dep3 . Dan itu berarti bahwa nama-nama file ini juga akan berubah.

Tetapi ini tidak berakhir di sana. Karena fragmen main mengimpor fragmen dep3 dan dep3 , dan nama file mereka telah berubah, perintah impor di main juga akan berubah:

 -import {...} from '/dep2-3c4d.mjs'; +import {...} from '/dep2-2be5.mjs'; -import {...} from '/dep3-d4e5.mjs'; +import {...} from '/dep3-3c6f.mjs'; 

Dan akhirnya, karena isi file main telah berubah, nama file ini juga harus diubah.

Beginilah diagram dependensi sekarang akan terlihat.


Modul di pohon ketergantungan dipengaruhi oleh perubahan tunggal dalam kode salah satu simpul daun pohon

Contoh ini menunjukkan bagaimana perubahan kode kecil yang dibuat hanya dalam satu file menyebabkan pembatalan cache 80% dari fragmen bundel.

Meskipun memang benar bahwa tidak semua perubahan menyebabkan konsekuensi yang menyedihkan (misalnya, membatalkan cache node daun menyebabkan validasi cache semua node hingga ke root, tetapi membatalkan cache root tidak menyebabkan cascading invalidation mencapai tangkapan daun), di dunia yang ideal kami tidak perlu berurusan dengan pembatalan cache yang tidak perlu.

Ini membawa kita pada pertanyaan berikut: "Apakah mungkin untuk mendapatkan manfaat dari sumber daya yang tidak dapat diubah dan caching jangka panjang, sementara tidak menderita cascading cache invalidations?"

Pendekatan Pemecahan Masalah


Masalah dengan hash dari isi file dalam nama file, dari sudut pandang teknis, bukanlah bahwa hash ada dalam nama. Itu terletak pada fakta bahwa hash ini muncul di dalam file lain. Akibatnya, cache dari file-file ini dinonaktifkan ketika mengubah hash dalam nama-nama file di mana mereka bergantung.

Solusi untuk masalah ini adalah dengan menggunakan bahasa contoh di atas untuk memungkinkan untuk mengimpor fragmen vendor oleh dep3 dan dep3 tanpa menentukan informasi versi file fragmen vendor . Dengan demikian, Anda harus memastikan bahwa versi vendor diunduh sudah benar, dengan mempertimbangkan versi dep3 dan dep3 .

Ternyata, ada beberapa cara untuk mencapai tujuan ini:

  • Kartu impor.
  • Pekerja Layanan.
  • Skrip asli untuk memuat sumber daya.

Pertimbangkan mekanisme ini.

Pendekatan # 1: Impor Kartu


Mengimpor peta adalah solusi paling sederhana untuk cascading invalidation cache. Selain itu, mekanisme ini paling mudah diterapkan. Tetapi, sayangnya, hanya didukung di Chrome (fitur ini, terlebih lagi, harus diaktifkan secara eksplisit).

Meskipun demikian, saya ingin memulai dengan cerita tentang kartu impor, karena saya yakin keputusan ini akan menjadi yang paling umum di masa depan. Selain itu, deskripsi bekerja dengan kartu impor akan membantu menjelaskan fitur-fitur pendekatan lain untuk menyelesaikan masalah kita.

Menggunakan peta impor untuk mencegah cascading invalidation terdiri dari tiga langkah.

โ–Langkah 1


Anda perlu mengkonfigurasi bundler sehingga ketika membangun proyek itu tidak termasuk hash dari isi file dalam nama mereka.

Jika Anda merakit proyek yang modulnya diperlihatkan dalam diagram dari contoh sebelumnya, tanpa menyertakan hash dari isinya dalam nama file, file-file dalam direktori output proyek akan terlihat seperti ini:

 dep1.mjs dep2.mjs dep3.mjs main.mjs vendor.mjs 

Perintah impor dalam modul yang sesuai juga tidak akan menyertakan hash:

 import {...} from '/vendor.mjs'; 

โ–Langkah 2


Anda perlu menggunakan alat seperti rev-hash , dan menggunakannya untuk menghasilkan salinan setiap file dengan hash yang ditambahkan ke namanya yang menunjukkan versi isinya.
Setelah bagian pekerjaan ini selesai, isi direktori keluaran akan terlihat seperti yang ditunjukkan di bawah ini (perhatikan bahwa sekarang ada dua opsi untuk setiap file):

 dep1-b2c3.mjs", dep1.mjs dep2-3c4d.mjs", dep2.mjs dep3-d4e5.mjs", dep3.mjs main-1a2b.mjs", main.mjs vendor-5e6f.mjs", vendor.mjs 

โ–Langkah 3


Anda perlu membuat objek JSON yang menyimpan informasi tentang korespondensi setiap file yang namanya tidak ada hash untuk setiap file yang namanya hash. Objek ini perlu ditambahkan ke templat HTML.

Objek JSON ini adalah peta impor. Begini tampilannya:

 <script type="importmap"> {  "imports": {    "/main.mjs": "/main-1a2b.mjs",    "/dep1.mjs": "/dep1-b2c3.mjs",    "/dep2.mjs": "/dep2-3c4d.mjs",    "/dep3.mjs": "/dep3-d4e5.mjs",    "/vendor.mjs": "/vendor-5e6f.mjs",  } } </script> 

Setelah itu, setiap kali browser melihat perintah impor file yang terletak di alamat yang sesuai dengan salah satu kunci peta impor, browser akan mengimpor file yang cocok dengan nilai kunci.

Jika Anda menggunakan peta impor ini sebagai contoh, Anda dapat mengetahui bahwa perintah impor yang mereferensikan file /vendor.mjs sebenarnya akan meminta dan memuat file /vendor-5e6f.mjs :

 //    `/vendor.mjs`,  `/vendor-5e6f.mjs`. import {...} from '/vendor.mjs'; 

Ini berarti bahwa kode sumber modul dapat dengan mudah merujuk pada nama file dari modul yang tidak mengandung hash, dan browser akan selalu mengunduh file yang namanya berisi informasi tentang versi kontennya. Dan, karena tidak ada hash dalam kode sumber modul (mereka hanya hadir di peta impor), perubahan hash ini tidak akan menyebabkan pembatalan modul selain dari mereka yang isinya telah benar-benar berubah.

Mungkin Anda sekarang bertanya-tanya mengapa saya membuat salinan setiap file alih-alih hanya mengganti nama file. Ini diperlukan untuk mendukung browser yang tidak dapat berfungsi dengan impor peta. Pada contoh sebelumnya, browser semacam itu hanya akan melihat file /vendor.mjs dan cukup mengunduh file ini, melakukan seperti biasanya, menemui konstruksi yang serupa. Akibatnya, ternyata kedua file tersebut harus ada di server.

Jika Anda ingin melihat impor peta dalam aksi, berikut adalah sekumpulan contoh yang menunjukkan semua cara untuk menyelesaikan masalah pembatalan cache cascading yang ditunjukkan pada artikel ini. Juga, lihat konfigurasi perakitan proyek , jika Anda tertarik mempelajari bagaimana saya membuat peta impor dan hash versi untuk setiap file.

Dilanjutkan ...

Pembaca yang budiman! Apakah Anda mengetahui cascading invalidation cache?


Source: https://habr.com/ru/post/id472860/


All Articles