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

Veicule conteúdo Firestore empacotado de um CDN

Muitos aplicativos veiculam o mesmo conteúdo para todos os usuários no carregamento da primeira página. Por exemplo, um site de notícias pode mostrar as histórias mais recentes ou um site de comércio eletrônico pode mostrar os itens mais vendidos.

Se esse conteúdo for servido do Cloud Firestore, cada usuário emitirá uma nova consulta para os mesmos resultados ao carregar o aplicativo. Como esses resultados não são armazenados em cache entre os usuários, o aplicativo é mais lento e caro do que precisa ser.

Solução: Pacotes

Os pacotes do Cloud Firestore permitem que você monte pacotes de dados a partir de resultados de consulta comuns no back-end usando o SDK Admin do Firebase e disponibilize esses blobs pré-calculados em cache em um CDN. Isso oferece aos usuários uma experiência de primeiro carregamento muito mais rápida e reduz os custos de consulta do Cloud Firestore.

Neste guia, usaremos Cloud Functions para gerar pacotes e Firebase Hosting para armazenar em cache e disponibilizar conteúdo de pacotes dinamicamente. Mais informações sobre pacotes está disponível nas guias .

Primeiro, crie uma função HTTP pública simples para consultar as 50 últimas "histórias" e servir o resultado como um pacote.

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

Em seguida configure Firebase hospedagem para servir e armazenar em cache esta Função Nuvem modificando firebase.json . Com essa configuração, o Firebase Hosting CDN veiculará o conteúdo do pacote de acordo com as configurações de cache definidas pelo Cloud Function. Quando o cache expira, ele atualiza o conteúdo, acionando a função novamente.

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

Finalmente, em seu aplicativo da web, busque o conteúdo empacotado do CDN e carregue-o no SDK do 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
  // ...
}

Economia estimada

Considere um site de notícias que recebe 100.000 usuários por dia e cada usuário carrega as mesmas 50 notícias principais no carregamento inicial. Sem qualquer armazenamento em cache, isso resultaria em 50 x 100.000 = 5.000.000 leituras de documentos por dia do Cloud Firestore.

Agora suponha que o site adote a técnica acima e armazene em cache esses 50 resultados por até 5 minutos. Portanto, em vez de carregar os resultados da consulta para cada usuário, os resultados são carregados exatamente 12 vezes por hora. Não importa quantos usuários cheguem ao site, o número de consultas ao Cloud Firestore permanece o mesmo. Em vez de 5.000.000 leituras de documentos, esta página usaria 12 x 24 x 50 = 14.400 leituras de documentos por dia. Os pequenos custos adicionais para Firebase Hosting e Cloud Functions são facilmente compensados ​​pela economia do Cloud Firestore.

Embora o desenvolvedor se beneficie da economia de custos, o maior beneficiado é o usuário. Carregar esses 50 documentos do Firebase Hosting CDN em vez do Cloud Firestore diretamente pode reduzir facilmente 100-200ms ou mais do tempo de carregamento do conteúdo da página. Estudos têm mostrado repetidamente que páginas rápidas significam usuários mais felizes.