Catch up on everthing we announced at this year's Firebase Summit. Learn more

Ofrezca contenido de Firestore incluido desde un CDN

Muchas aplicaciones ofrecen el mismo contenido a todos los usuarios en la carga de la primera página. Por ejemplo, un sitio de noticias puede mostrar las últimas noticias o un sitio de comercio electrónico puede mostrar los artículos más vendidos.

Si este contenido se entrega desde Cloud Firestore, cada usuario emitirá una nueva consulta para los mismos resultados cuando carguen la aplicación. Debido a que estos resultados no se almacenan en caché entre usuarios, la aplicación es más lenta y costosa de lo necesario.

Solución: paquetes

Los paquetes de Cloud Firestore te permiten ensamblar paquetes de datos a partir de resultados de consultas comunes en el backend con el SDK de Firebase Admin y entregar estos blobs precalculados almacenados en caché en una CDN. Esto les brinda a sus usuarios una experiencia de primera carga mucho más rápida y reduce los costos de consulta de Cloud Firestore.

En esta guía, usaremos Cloud Functions para generar paquetes y Firebase Hosting para almacenar en caché y entregar contenido de paquetes de manera dinámica. Más información acerca de los lotes está disponible en las guías .

Primero cree una función HTTP pública simple para consultar las 50 últimas "historias" y distribuya el resultado como un paquete.

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

A continuación, configure Firebase Hosting para servir y almacenar en caché a esta función mediante la modificación de la nube firebase.json . Con esta configuración, Firebase Hosting CDN entregará el contenido del paquete de acuerdo con la configuración de caché establecida por la función de nube. Cuando la caché expira, actualizará el contenido activando la función nuevamente.

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

Finalmente, en su aplicación web, obtenga el contenido empaquetado de la CDN y cárguelo en el SDK de Firestore.

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

Ahorros estimados

Considere un sitio web de noticias que recibe 100,000 usuarios por día y cada usuario carga las mismas 50 historias principales en la carga inicial. Sin almacenamiento en caché, esto daría como resultado 50 x 100,000 = 5,000,000 lecturas de documentos por día desde Cloud Firestore.

Ahora suponga que el sitio adopta la técnica anterior y almacena en caché esos 50 resultados durante un máximo de 5 minutos. Entonces, en lugar de cargar los resultados de la consulta para cada usuario, los resultados se cargan exactamente 12 veces por hora. No importa cuántos usuarios lleguen al sitio, la cantidad de consultas a Cloud Firestore permanece igual. En lugar de 5.000.000 de lecturas de documentos, esta página utilizaría 12 x 24 x 50 = 14.400 lecturas de documentos por día. Los pequeños costos adicionales de Firebase Hosting y Cloud Functions se compensan fácilmente con los ahorros de costos de Cloud Firestore.

Mientras que el desarrollador se beneficia del ahorro de costes, el mayor beneficiario es el usuario. La carga de estos 50 documentos desde Firebase Hosting CDN en lugar de desde Cloud Firestore directamente puede reducir fácilmente 100-200ms o más del tiempo de carga de contenido de la página. Los estudios han demostrado repetidamente que las páginas rápidas significan usuarios más felices.