Compreenda as consultas em tempo real em grande escala

Leia este documento para obter orientações sobre como dimensionar a sua app sem servidor para além de milhares de operações por segundo ou centenas de milhares de utilizadores simultâneos. Este documento inclui tópicos avançados para ajudar a compreender o sistema em profundidade. Se estiver a começar a usar o Firestore, consulte o guia de início rápido.

O Firestore e os SDKs Firebase para dispositivos móveis/Web oferecem um modelo eficaz para desenvolver apps sem servidor em que o código do lado do cliente acede diretamente à base de dados. Os SDKs permitem que os clientes ouçam atualizações dos dados em tempo real. Pode usar atualizações em tempo real para criar apps responsivas que não requerem infraestrutura de servidor. Embora seja muito fácil pôr algo em funcionamento, é útil compreender as restrições nos sistemas que compõem o Firestore para que a sua app sem servidor seja dimensionada e tenha um bom desempenho quando o tráfego aumenta.

Consulte as secções seguintes para obter sugestões sobre como dimensionar a sua app.

Escolha uma localização da base de dados perto dos seus utilizadores

O diagrama seguinte demonstra a arquitetura de uma app em tempo real:

Exemplo de arquitetura de app em tempo real

Quando uma app que está a ser executada no dispositivo de um utilizador (dispositivo móvel ou Web) estabelece uma ligação ao Firestore, a ligação é encaminhada para um servidor de front-end do Firestore na mesma região onde a sua base de dados está localizada. Por exemplo, se a sua base de dados estiver em us-east1, a ligação também vai para um frontend do Firestore também em us-east1. Estas ligações são duradouras e permanecem abertas até serem explicitamente fechadas pela app. O front-end lê dados dos sistemas de armazenamento do Firestore subjacentes.

A distância entre a localização física de um utilizador e a localização da base de dados do Firestore afeta a latência sentida pelo utilizador. Por exemplo, um utilizador na Índia cuja app comunica com uma base de dados numa Google Cloud região na América do Norte pode achar a experiência mais lenta e a app menos ágil do que se a base de dados estivesse localizada mais perto, como na Índia ou noutra parte da Ásia.

Crie conteúdo a pensar na fiabilidade

Os seguintes tópicos melhoram ou afetam a fiabilidade da sua app:

Ative o modo offline

Os SDKs do Firebase oferecem persistência de dados offline. Se a app no dispositivo do utilizador não conseguir estabelecer ligação ao Firestore, a app permanece utilizável através da interação com os dados em cache localmente. Isto garante o acesso aos dados, mesmo quando os utilizadores têm ligações à Internet instáveis ou perdem completamente o acesso durante várias horas ou dias. Para mais detalhes sobre o modo offline, consulte o artigo Ative os dados offline.

Compreenda as novas tentativas automáticas

Os SDKs do Firebase encarregam-se de repetir as operações e restabelecer as ligações interrompidas. Isto ajuda a contornar erros transitórios causados pelo reinício de servidores ou problemas de rede entre o cliente e a base de dados.

Escolha entre localizações regionais e multirregionais

Existem várias compensações quando escolhe entre localizações regionais e multirregionais. A principal diferença é a forma como os dados são replicados. Isto determina as garantias de disponibilidade da sua app. Uma instância de várias regiões oferece uma fiabilidade de publicação mais forte e aumenta a durabilidade dos seus dados, mas o compromisso é o custo.

Compreenda o sistema de consultas em tempo real

As consultas em tempo real, também denominadas ouvintes de instantâneos, permitem que a app ouça as alterações na base de dados e receba notificações de baixa latência assim que os dados mudarem. Uma app pode obter o mesmo resultado consultando periodicamente a base de dados para verificar se existem atualizações, mas, muitas vezes, é mais lento, mais caro e requer mais código. Para ver exemplos de como configurar e usar consultas em tempo real, consulte o artigo Receba atualizações em tempo real. As secções seguintes abordam os detalhes do funcionamento dos ouvintes de instantâneos e descrevem algumas das práticas recomendadas para dimensionar as consultas em tempo real, mantendo o desempenho.

Imagine dois utilizadores que se ligam ao Firestore através de uma app de mensagens criada com um dos SDKs para dispositivos móveis.

O cliente A escreve na base de dados para adicionar e atualizar documentos numa coleção denominada chatroom:

collection chatroom:
    document message1:
      from: 'Sparky'
      message: 'Welcome to Firestore!'

    document message2:
      from: 'Santa'
      message: 'Presents are coming'

O cliente B ouve atualizações na mesma coleção através de um ouvinte de instantâneos. O cliente B recebe uma notificação imediata sempre que alguém cria uma nova mensagem. O diagrama seguinte mostra a arquitetura por detrás de um ouvinte de instantâneos:

Arquitetura de uma ligação de ouvinte de instantâneos

A seguinte sequência de eventos ocorre quando o cliente B liga um ouvinte de instantâneos à base de dados:

  1. O cliente B abre uma ligação ao Firestore e regista um ouvinte fazendo uma chamada para onSnapshot(collection("chatroom")) através do SDK do Firebase. Este ouvinte pode permanecer ativo durante horas.
  2. O front-end do Firestore consulta o sistema de armazenamento subjacente para iniciar o conjunto de dados. Carrega o conjunto de resultados completo dos documentos correspondentes. Referimo-nos a isto como uma consulta de sondagem. Em seguida, o sistema avalia as regras de segurança do Firebase da base de dados para verificar se o utilizador pode aceder a estes dados. Se o utilizador estiver autorizado, a base de dados devolve os dados ao utilizador.
  3. A consulta do cliente B passa então para o modo de escuta. O ouvinte regista-se num controlador de subscrições e aguarda atualizações dos dados.
  4. O cliente A envia agora uma operação de escrita para modificar um documento.
  5. A base de dados compromete a alteração do documento ao respetivo sistema de armazenamento.
  6. Em termos transacionais, o sistema compromete a mesma atualização a um registo de alterações interno. O registo de alterações estabelece uma ordenação rigorosa das alterações à medida que ocorrem.
  7. Por sua vez, o registo de alterações distribui os dados atualizados a um conjunto de processadores de subscrições.
  8. É executado um correspondente de consultas inverso para ver se o documento atualizado corresponde a algum ouvinte de instantâneos atualmente registado. Neste exemplo, o documento corresponde ao ouvinte de instantâneos do cliente B. Como o nome indica, pode considerar o correspondente de consultas inversas como uma consulta de base de dados normal, mas feita ao contrário. Em vez de pesquisar documentos para encontrar os que correspondem a uma consulta, pesquisa eficientemente consultas para encontrar as que correspondem a um documento recebido. Quando encontra uma correspondência, o sistema encaminha o documento em questão para os ouvintes de instantâneos. Em seguida, o sistema avalia as Regras de segurança do Firebase da base de dados para garantir que apenas os utilizadores autorizados recebem os dados.
  9. O sistema encaminha a atualização do documento para o SDK no dispositivo do cliente B e o callback onSnapshot é acionado. Se a persistência local estiver ativada, o SDK também aplica a atualização à cache local.

Uma parte fundamental da escalabilidade do Firestore depende da distribuição a partir do registo de alterações para os controladores de subscrição e os servidores de front-end. A distribuição permite que uma única alteração de dados se propague de forma eficiente para publicar milhões de consultas em tempo real e utilizadores ligados. Ao executar muitas réplicas de todos estes componentes em várias zonas (ou várias regiões no caso de uma implementação multirregional), o Firestore alcança uma elevada disponibilidade e escalabilidade.

Tenha em atenção que todas as operações de leitura emitidas a partir de SDKs para dispositivos móveis e Web seguem o modelo acima. Executam uma consulta de sondagem seguida do modo de escuta para manter as garantias de consistência. Isto também se aplica a ouvintes em tempo real, chamadas para obter um documento e consultas únicas. Pode considerar as obtenções de documentos únicos e as consultas únicas como ouvintes de instantâneos de curta duração que têm restrições semelhantes em termos de desempenho.

Aplique práticas recomendadas para dimensionar consultas em tempo real

Aplique as seguintes práticas recomendadas para criar consultas em tempo real escaláveis.

Compreenda o tráfego de escrita elevado no sistema

Esta secção ajuda a compreender como o sistema responde a um número crescente de pedidos de gravação.

Os registos de alterações do Firestore que geram as consultas em tempo real são dimensionados automaticamente na horizontal à medida que o tráfego de gravação aumenta. À medida que a taxa de gravação de uma base de dados aumenta para além do que um único servidor consegue processar, o histórico de alterações é dividido por vários servidores e o processamento de consultas começa a consumir dados de vários processadores de subscrições em vez de um. Do ponto de vista do cliente e do SDK, tudo isto é transparente e não é necessária nenhuma ação por parte da app quando ocorrem divisões. O diagrama seguinte demonstra como as consultas em tempo real são dimensionadas:

Arquitetura de distribuição do registo de alterações

O dimensionamento automático permite-lhe aumentar o tráfego de escrita sem limites, mas, à medida que o tráfego aumenta, o sistema pode demorar algum tempo a responder. Siga as recomendações da regra 5-5-5 para evitar a criação de um ponto crítico de gravação. O Key Visualizer é uma ferramenta útil para analisar hotspots de escrita.

Muitas apps têm um crescimento orgânico previsível, que o Firestore pode acomodar sem precauções. No entanto, os fluxos de trabalho em lote, como a importação de um conjunto de dados grande, podem aumentar as escritas demasiado rapidamente. À medida que cria a sua app, tenha em atenção a origem do tráfego de gravação.

Compreenda como as escritas e as leituras interagem

Pode pensar no sistema de consultas em tempo real como um pipeline que liga as operações de escrita aos leitores. Sempre que um documento é criado, atualizado ou eliminado, a alteração propaga-se do sistema de armazenamento para os ouvintes atualmente registados. A estrutura do registo de alterações do Firestore garante uma consistência forte, o que significa que a sua app nunca recebe notificações de atualizações desordenadas em comparação com o momento em que a base de dados confirmou as alterações aos dados. Isto simplifica o desenvolvimento de apps ao remover casos extremos relacionados com a consistência dos dados.

Esta conduta ligada significa que uma operação de escrita que cause pontos críticos ou contenção de bloqueios pode afetar negativamente as operações de leitura. Quando as operações de escrita falham ou sofrem limitação, uma leitura pode ficar parada à espera de dados consistentes do registo de alterações. Se isto acontecer na sua app, pode ver operações de escrita lentas e tempos de resposta lentos correlacionados para consultas. Evitar os hotspots é fundamental para evitar este problema.

Mantenha os documentos e as operações de escrita pequenos

Quando cria apps com ouvintes de instantâneos, normalmente, quer que os utilizadores fiquem a saber rapidamente das alterações aos dados. Para o conseguir, tente manter as coisas pequenas. O sistema pode enviar documentos pequenos com dezenas de campos através do sistema muito rapidamente. Os documentos maiores com centenas de campos e grandes quantidades de dados demoram mais tempo a serem processados.

Da mesma forma, prefira operações de confirmação e escrita curtas e rápidas para manter a latência baixa. Os grandes lotes podem dar-lhe um débito mais elevado do ponto de vista do escritor, mas podem aumentar o tempo de notificação para os ouvintes de instantâneos. Isto é frequentemente contrário à intuição em comparação com a utilização de outros sistemas de base de dados em que pode usar o processamento em lote para melhorar o desempenho.

Use ouvintes eficientes

À medida que as taxas de gravação da sua base de dados aumentam, o Firestore divide o processamento de dados por vários servidores. O algoritmo de divisão do Firestore tenta colocar os dados da mesma coleção ou grupo de coleções no mesmo servidor de registo de alterações. O sistema tenta maximizar o débito de gravação possível, mantendo o número de servidores envolvidos no processamento de uma consulta o mais baixo possível.

No entanto, determinados padrões podem continuar a originar um comportamento abaixo do ideal para os ouvintes de instantâneos. Por exemplo, se a sua app armazenar a maioria dos dados numa grande coleção, o ouvinte pode ter de se ligar a muitos servidores para receber todos os dados de que precisa. Isto continua a ser verdade mesmo que aplique um filtro de consulta. A ligação a muitos servidores aumenta o risco de respostas mais lentas.

Para evitar estas respostas mais lentas, crie o seu esquema e app de forma que o sistema possa publicar ouvintes sem aceder a muitos servidores diferentes. Pode ser melhor dividir os dados em coleções mais pequenas com taxas de gravação mais baixas.

Isto é semelhante a pensar nas consultas de desempenho numa base de dados relacional que requerem análises completas de tabelas. Numa base de dados relacional, uma consulta que requer uma análise completa da tabela é o equivalente a um ouvinte de instantâneos que monitoriza uma coleção com uma elevada taxa de alteração. Pode ter um desempenho lento em comparação com uma consulta que a base de dados pode fornecer através de um índice mais específico. Uma consulta com um índice mais específico é como um ouvinte de instantâneos que monitoriza um único documento ou uma coleção que muda com menos frequência. Deve testar o carregamento da sua app para compreender melhor o comportamento e a necessidade do seu exemplo de utilização.

Mantenha as consultas de sondagem rápidas

Outra parte fundamental das consultas em tempo real com capacidade de resposta envolve garantir que a consulta de sondagem para inicializar os dados é rápida e eficiente. Quando um novo ouvinte de instantâneos se liga pela primeira vez, tem de carregar o conjunto de resultados completo e enviá-lo para o dispositivo do utilizador. As consultas lentas tornam a sua app menos reativa. Isto inclui, por exemplo, consultas que tentam ler muitos documentos ou consultas que não usam os índices adequados.

Um ouvinte também pode passar de um estado de audição para um estado de sondagem em algumas circunstâncias. Isto acontece automaticamente e é transparente para os SDKs e a sua app. As seguintes condições podem acionar um estado de sondagem:

  • O sistema reequilibra um registo de alterações devido a alterações na carga.
  • As zonas Wi-Fi causam falhas ou atrasos nas gravações na base de dados.
  • Os reinícios transitórios do servidor afetam temporariamente os ouvintes.

Se as suas consultas de sondagem forem suficientemente rápidas, o estado de sondagem torna-se transparente para os utilizadores da sua app.

Privilegie os ouvintes de longa duração

Abrir e manter os ouvintes ativos durante o maior tempo possível é, muitas vezes, a forma mais rentável de criar uma app que usa o Firestore. Quando usa o Firestore, a faturação é feita com base nos documentos devolvidos à sua app e não na manutenção de uma ligação aberta. Um ouvinte de instantâneos de longa duração lê apenas os dados de que precisa para responder à consulta durante a respetiva duração. Isto inclui uma operação de sondagem inicial seguida de notificações quando os dados mudam efetivamente. Por outro lado, as consultas únicas voltam a ler os dados que podem não ter sido alterados desde a última execução da consulta pela app.

Nos casos em que a sua app tem de consumir uma taxa elevada de dados, os ouvintes de instantâneos podem não ser adequados. Por exemplo, se o seu exemplo de utilização enviar muitos documentos por segundo através de uma ligação durante um período prolongado, pode ser melhor optar por consultas únicas que são executadas com uma frequência mais baixa.

O que se segue