Menayangkan konten Firestore yang dipaketkan dari CDN

Banyak aplikasi menayangkan konten yang sama kepada semua pengguna saat pemuatan halaman pertama. Misalnya, situs berita dapat menampilkan berita terbaru, atau situs e-commerce dapat menampilkan item terlaris.

Jika konten ini ditayangkan dari Cloud Firestore, setiap pengguna akan mengeluarkan kueri baru untuk hasil yang sama saat mereka memuat aplikasi. Karena hasil ini tidak di-cache antara pengguna, aplikasi akan lebih lambat dan lebih mahal dari yang diperlukan.

Solusi: Paket

Dengan paket Cloud Firestore, Anda dapat menghimpun paket data dari hasil kueri umum di backend menggunakan Firebase Admin SDK, serta menayangkan blob yang telah dihitung sebelumnya dan di-cache di CDN. Hal ini memberikan pengalaman pemuatan pertama yang jauh lebih cepat kepada pengguna dan mengurangi biaya kueri Cloud Firestore Anda.

Dalam panduan ini, kita akan menggunakan Cloud Functions untuk membuat paket dan Firebase Hosting untuk meng-cache dan menayangkan konten paket secara dinamis. Informasi selengkapnya tentang paket tersedia di panduan.

Pertama-tama, buat fungsi HTTP publik yang sederhana untuk membuat kueri 50 "artikel" terbaru dan menayangkan hasilnya sebagai paket.

Node.js
exports.createBundle = functions.https.onRequest(async (request, response) => {
  // Query the 50 latest stories
  const latestStories = await db.collection('stories')
    .orderBy('timestamp', 'desc')
    .limit(50)
    .get();

  // Build the bundle from the query results
  const bundleBuffer = db.bundle('latest-stories')
    .add('latest-stories-query', latestStories)
    .build();

  // Cache the response for up to 5 minutes;
  // see https://firebase.google.com/docs/hosting/manage-cache
  response.set('Cache-Control', 'public, max-age=300, s-maxage=600');

  response.end(bundleBuffer);
});
      
Java

package com.example;

import com.google.auth.oauth2.GoogleCredentials;
import com.google.cloud.firestore.Firestore;
import com.google.cloud.firestore.FirestoreBundle;
import com.google.cloud.firestore.Query.Direction;
import com.google.cloud.firestore.QuerySnapshot;
import com.google.cloud.functions.HttpFunction;
import com.google.cloud.functions.HttpRequest;
import com.google.cloud.functions.HttpResponse;
import com.google.firebase.FirebaseApp;
import com.google.firebase.FirebaseOptions;
import com.google.firebase.cloud.FirestoreClient;
import java.io.BufferedWriter;
import java.io.IOException;

public class ExampleFunction implements HttpFunction {

  public static FirebaseApp initializeFirebase() throws IOException {
    if (FirebaseApp.getApps().isEmpty()) {
      FirebaseOptions options = FirebaseOptions.builder()
          .setCredentials(GoogleCredentials.getApplicationDefault())
          .setProjectId("YOUR-PROJECT-ID")
          .build();

      FirebaseApp.initializeApp(options);
    }

    return FirebaseApp.getInstance();
  }

  @Override
  public void service(HttpRequest request, HttpResponse response) throws Exception {
    // Get a Firestore instance
    FirebaseApp app = initializeFirebase();
    Firestore db = FirestoreClient.getFirestore(app);

    // Query the 50 latest stories
    QuerySnapshot latestStories = db.collection("stories")
        .orderBy("timestamp", Direction.DESCENDING)
        .limit(50)
        .get()
        .get();

    // Build the bundle from the query results
    FirestoreBundle bundle = db.bundleBuilder("latest-stores")
        .add("latest-stories-query", latestStories)
        .build();

    // Cache the response for up to 5 minutes
    // see https://firebase.google.com/docs/hosting/manage-cache
    response.appendHeader("Cache-Control", "public, max-age=300, s-maxage=600");

    // Write the bundle to the HTTP response
    BufferedWriter writer = response.getWriter();
    writer.write(new String(bundle.toByteBuffer().array()));
  }
}
      

Selanjutnya, konfigurasikan Firebase Hosting untuk menayangkan dan meng-cache Cloud Function ini dengan mengubah firebase.json. Dengan konfigurasi ini, CDN Firebase Hosting akan menayangkan konten paket sesuai dengan setelan cache yang ditetapkan oleh Cloud Function. Saat masa berlaku cache berakhir, konten akan dimuat ulang dengan cara memicu fungsi lagi.

firebase.json
{
  "hosting": {
    // ...
    "rewrites": [{
      "source": "/createBundle",
      "function": "createBundle"
    }]
  },
  // ...
}

Terakhir, di aplikasi web Anda, ambil konten yang dipaketkan dari CDN dan muat ke Firestore SDK.

// If you are using module bundlers.
import firebase from "firebase/app";
import "firebase/firestore";
import "firebase/firestore/bundle" // This line enables bundle loading as a side effect.

async function fetchFromBundle() {
  // Fetch the bundle from Firebase Hosting, if the CDN cache is hit the 'X-Cache'
  // response header will be set to 'HIT'
  const resp = await fetch('/createBundle');

  // Load the bundle contents into the Firestore SDK
  await db.loadBundle(resp.body);

  // Query the results from the cache
  // Note: omitting "source: cache" will query the Firestore backend.
  
  const query = await db.namedQuery('latest-stories-query');
  const storiesSnap = await query.get({ source: 'cache' });

  // Use the results
  // ...
}

Perkiraan Penghematan

Pertimbangkan situs berita yang memiliki 100.000 pengguna per hari dan setiap pengguna memuat 50 artikel teratas yang sama pada pemuatan awal. Tanpa menyimpan data ke dalam cache, tindakan ini akan menghasilkan 50 x 100.000 = 5.000.000 pembacaan dokumen per hari dari Cloud Firestore.

Sekarang asumsikan situs menggunakan teknik di atas dan meng-cache 50 hasil tersebut selama maksimal 5 menit. Jadi, alih-alih memuat hasil kueri untuk setiap pengguna, hasil akan dimuat persis 12 kali per jam. Berapa pun jumlah pengguna yang tiba di situs ini, jumlah kueri ke Cloud Firestore tetap sama. Alih-alih 5.000.000 pembacaan dokumen, halaman ini akan menggunakan 12 x 24 x 50 = 14.400 pembacaan dokumen per hari. Biaya tambahan yang kecil untuk Firebase Hosting dan Cloud Functions mudah diimbangi dengan penghematan biaya Cloud Firestore.

Meskipun developer mendapatkan keuntungan dari penghematan biaya, penerima manfaat terbesar adalah pengguna. Memuat 50 dokumen ini dari CDN Firebase Hosting, bukan langsung dari Cloud Firestore, dapat menurunkan waktu pemuatan konten halaman sebanyak 100-200 milidetik atau lebih. Penelitian berulang kali menunjukkan bahwa halaman yang dimuat dengan cepat menghasilkan pengguna yang lebih puas.