Paketlenmiş Firestore içeriklerini CDN'den yayınlama

Birçok uygulama, ilk sayfa yüklemesinde tüm kullanıcılara aynı içeriği sunar. Örneğin, bir haber sitesi en son haberleri veya bir e-ticaret sitesinde en çok satan öğeleri gösterebilir.

Bu içerik Cloud Firestore'dan sunuluyorsa her kullanıcı uygulamayı yüklediğinde aynı sonuçlar için yeni bir sorgu gönderir. Bu sonuçlar kullanıcılar arasında önbelleğe alınmadığından uygulama, olması gerekenden daha yavaş ve daha pahalıdır.

Çözüm: Paketler

Cloud Firestore paketleri, Firebase Admin SDK'yı kullanarak arka uçta ortak sorgu sonuçlarından veri paketleri derlemenize ve bu önceden hesaplanmış blob'ları CDN'de önbelleğe alınmış olarak sunmanıza olanak tanır. Bu, kullanıcılarınıza çok daha hızlı bir ilk yükleme deneyimi sunar ve Cloud Firestore sorgu maliyetlerinizi azaltır.

Bu kılavuzda, paket oluşturmak için Cloud Functions'ı, paket içeriğini dinamik olarak önbelleğe alıp sunmak için Firebase Hosting'i kullanacağız. Paketler hakkında daha fazla bilgiyi kılavuzlarda bulabilirsiniz.

Öncelikle son 50 "hikayeyi" sorgulamak ve sonucu bir paket olarak sunmak için basit bir herkese açık HTTP işlevi oluşturun.

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()));
  }
}
      

Ardından, firebase.json adresini değiştirerek Firebase Hosting'i bu Cloud Functions işlevini sunulacak ve önbelleğe alacak şekilde yapılandırın. Bu yapılandırmayla Firebase Hosting CDN, paket içeriğini Cloud Functions işlevi tarafından belirlenen önbellek ayarlarına göre yayınlar. Önbelleğin süresi dolduğunda işlevi tekrar tetikleyerek içerik yenilenir.

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

Son olarak web uygulamanızda, gruplandırılmış içeriği CDN'den alın ve Firestore SDK'sına yükleyin.

// 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
  // ...
}

Tahmini Tasarruf

Günde 100.000 kullanıcıya ulaşan ve her kullanıcının ilk yüklemede aynı 50 popüler haberi yükleyen bir haber web sitesi düşünün. Önbelleğe alma olmadan Cloud Firestore'da günde 50 x 100.000 = 5.000.000 belge okuma yapılır.

Şimdi sitenin yukarıdaki tekniği kullandığını ve bu 50 sonucu 5 dakikaya kadar önbelleğe aldığını varsayalım. Böylece her kullanıcı için sorgu sonuçlarını yüklemek yerine, sonuçlar saatte tam olarak 12 kez yüklenir. Siteye kaç kullanıcı gelirse gelsin Cloud Firestore'a gönderilen sorgu sayısı aynı kalır. Bu sayfa, günlük 5.000.000 belge okuma yerine 12 x 24 x 50 = 14.400 belge okumasını kullanır. Firebase Hosting ve Cloud Functions'ın küçük ek maliyetleri, Cloud Firestore'daki maliyet tasarrufları sayesinde kolayca dengelenir.

Geliştirici maliyet tasarrufundan faydalansa da en büyük fayda, kullanıcıdır. Bu 50 belgenin doğrudan Cloud Firestore yerine Firebase Hosting CDN'den yüklenmesi, sayfanın içerik yükleme süresinden 100-200 ms veya daha uzun bir süre kısaltılmasına neden olabilir. Araştırmalar, hızlı sayfaların kullanıcıları daha mutlu hale getirdiğini defalarca gösteriyor.