Akselerasi instagram.com. Bagian 3

Hari ini kami menerbitkan terjemahan bagian ketiga dari serangkaian bahan akselerasi instagram.com. Di bagian pertama, kami berbicara tentang preloading data, di bagian kedua , tentang pengiriman data ke klien atas inisiatif server. Ini tentang caching.



Pekerjaan dimulai dengan cache


Kami sudah mengirimkan data ke aplikasi klien, melakukan ini selama pemuatan halaman sedini mungkin. Ini berarti bahwa satu-satunya cara tercepat untuk mengirimkan data adalah cara yang tidak menyediakan langkah-langkah yang terlibat dalam meminta informasi dari klien atau mengirimkannya ke klien atas inisiatif server. Ini dapat dilakukan dengan menggunakan pendekatan ini untuk pembentukan halaman, di mana cache muncul kedepan. Ini, bagaimanapun, berarti bahwa kita harus, meskipun sangat singkat, menunjukkan informasi yang sudah ketinggalan zaman kepada pengguna. Dengan menggunakan pendekatan ini, setelah memuat halaman, kami segera menunjukkan kepada pengguna salinan cache dari feed dan ceritanya, dan kemudian, setelah data terbaru tersedia, kami mengganti semua ini dengan data tersebut.

Kami menggunakan Redux untuk mengelola status instagram.com. Akibatnya, keseluruhan rencana implementasi skema di atas terlihat seperti ini. Kami menyimpan subset dari repositori Redux pada klien, dalam tabel indexedDB, mengisi repositori itu saat pertama kali memuat halaman. Namun, bekerja dengan indexedDB, mengunduh data dari server, dan interaksi pengguna dengan halaman adalah proses yang tidak sinkron. Akibatnya, kita dapat mengalami masalah. Mereka terdiri dari fakta bahwa pengguna bekerja dengan keadaan cache yang lama, dan kita perlu membuat tindakan pengguna berlaku untuk keadaan baru ketika menerimanya dari server.

Misalnya, jika kita menggunakan mekanisme standar untuk bekerja dengan cache, kita mungkin menghadapi masalah berikut. Kami mulai memuat data secara paralel dari cache dan dari jaringan. Karena data dari cache akan lebih cepat siap dari data jaringan, kami menunjukkannya kepada pengguna. Pengguna kemudian, misalnya, menyukai posting, tetapi setelah respon server, yang membawa informasi terbaru, tiba di klien, informasi ini menimpa informasi tentang posting yang disukai. Dalam data segar ini tidak akan ada informasi tentang suka bahwa pengguna telah menetapkan versi cache posting. Ini tampilannya.


Status ras yang terjadi saat pengguna berinteraksi dengan data yang di-cache (tindakan Redux disorot dalam warna hijau, status berwarna abu-abu)

Untuk mengatasi masalah ini, kami perlu mengubah status cache sesuai dengan tindakan pengguna dan menyimpan informasi tentang tindakan ini, yang akan memungkinkan kami untuk mereproduksi mereka sebagaimana diterapkan pada negara baru yang diterima dari server. Jika Anda pernah menggunakan Git atau sistem kontrol versi lain, maka tugas ini mungkin terasa familier bagi Anda. Misalkan keadaan cache yang direkam adalah cabang repositori lokal, dan respons server dengan data terbaru adalah cabang master. Jika demikian, maka kita dapat mengatakan bahwa kita ingin melakukan operasi relokasi, yaitu, kita ingin mengambil perubahan yang direkam di satu cabang (misalnya, suka, komentar, dan sebagainya) dan menerapkannya ke yang lain.

Ide ini membawa kita ke arsitektur sistem berikut:

  • Ketika halaman dimuat, kami mengirim permintaan ke server untuk mengunduh data baru (atau menunggu itu dikirim atas inisiatif server).
  • Buat subset menengah (bertahap) dari kondisi Redux.
  • Dalam proses menunggu data dari server, kami menyimpan tindakan yang dikirimkan.
  • Setelah menerima data dari server, kami melakukan tindakan dengan data baru dan memutar tindakan yang tersimpan pada data baru, menerapkannya ke kondisi perantara.
  • Setelah itu, kami melakukan perubahan dan mengganti keadaan saat ini dengan yang sedang.


Memecahkan masalah yang disebabkan oleh kondisi balapan menggunakan kondisi antara (aksi Redux disorot dalam warna hijau, status berwarna abu-abu)

Berkat kondisi perantara, kami dapat menggunakan kembali semua reduksi yang ada. Ini, sebagai tambahan, memungkinkan Anda untuk menyimpan status perantara (yang berisi data terbaru) secara terpisah dari status saat ini. Dan, karena bekerja dengan kondisi perantara diimplementasikan menggunakan Redux, cukup bagi kami untuk mengirim tindakan untuk menggunakan kondisi ini!

API


API negara perantara terdiri dari dua fungsi utama. Ini adalah stagingAction dan stagingCommit :

 function stagingAction(    key: string,    promise: Promise<Action>, ): AsyncAction<State, Action> function stagingCommit(key: string): AsyncAction<State, Action> 

Ada beberapa fungsi lain di sana, misalnya, untuk membatalkan perubahan dan untuk menangani kasus perbatasan, tetapi kami tidak akan mempertimbangkannya di sini.

Fungsi stagingAction menerima janji untuk menyelesaikan suatu peristiwa yang perlu dikirim ke negara perantara. Fungsi ini menginisialisasi keadaan perantara dan memantau tindakan yang telah dikirim sejak inisialisasi. Jika kita membandingkan ini dengan sistem kontrol versi, ternyata kita sedang berhadapan dengan penciptaan cabang lokal. Tindakan yang sedang berlangsung akan diantrekan dan diterapkan ke negara sementara setelah data baru tiba.

Fungsi stagingCommit menggantikan keadaan saat ini dengan yang sedang. Selain itu, jika diharapkan bahwa beberapa operasi asinkron selesai yang dilakukan pada kondisi peralihan, sistem akan menunggu operasi ini selesai sebelum menggantinya. Ini mirip dengan operasi relokasi, ketika perubahan lokal (dari cabang yang menyimpan cache) diterapkan di atas cabang master (di atas data baru yang diterima dari server), yang mengarah pada fakta bahwa versi lokal dari negara relevan.

Untuk mengaktifkan sistem kerja dengan kondisi peralihan, kami membungkus reducer root dengan kemampuan extender reducer. Ini memproses tindakan stagingCommit dan menerapkan tindakan yang disimpan sebelumnya ke negara baru. Untuk memanfaatkan semua ini, kita hanya perlu mengirim tindakan, dan semua yang lain akan dilakukan secara otomatis. Misalnya, jika kita ingin memuat rekaman baru dan membawa datanya ke kondisi perantara, kita dapat melakukan sesuatu seperti ini:

 function fetchAndStageFeed() {    return stagingAction(        'feed',        (async () => {            const {data} = await fetchFeedTimeline();            return {                type: FEED_LOADED,                ...data,            };        })(),    ); } //          store.dispatch(fetchAndStageFeed()); //   ,    stagingCommit, //      'feed' //      store.dispatch(stagingCommit('feed')); 

Penggunaan pendekatan rendering untuk feed dan cerita, di mana cache muncul, telah mempercepat output material masing-masing sebesar 2,5% dan 11%. Ini, sebagai tambahan, berkontribusi pada fakta bahwa, dalam persepsi pengguna, versi web dari sistem semakin mendekati klien Instagram untuk iOS dan Android.

Pembaca yang budiman! Apakah Anda menggunakan pendekatan apa pun untuk mengoptimalkan caching saat mengerjakan proyek Anda?


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


All Articles