Interpretar consultas en tiempo real a gran escala
Lee este documento para obtener información sobre cómo escalar tu aplicación sin servidor más allá de los miles de operaciones por segundo o los cientos de miles de usuarios simultáneos. Este documento incluye temas avanzados para ayudarte a comprender el sistema en profundidad. Si acabas de empezar a usar Firestore, consulta la guía de inicio rápido.
Firestore y los SDKs móviles y web de Firebase proporcionan un modelo eficaz para desarrollar aplicaciones sin servidor en las que el código del lado del cliente accede directamente a la base de datos. Los SDKs permiten que los clientes escuchen las actualizaciones de los datos en tiempo real. Puedes usar las actualizaciones en tiempo real para crear aplicaciones adaptables que no requieran infraestructura de servidor. Aunque es muy fácil poner en marcha algo, es útil conocer las restricciones de los sistemas que componen Firestore para que tu aplicación sin servidor se escale y funcione bien cuando aumente el tráfico.
Consulte las siguientes secciones para obtener consejos sobre cómo escalar su aplicación.
Elige una ubicación de la base de datos que esté cerca de tus usuarios
En el siguiente diagrama se muestra la arquitectura de una aplicación en tiempo real:
Cuando una aplicación que se ejecuta en el dispositivo de un usuario (móvil o web) establece una conexión con Firestore, la conexión se dirige a un servidor frontend de Firestore en la misma región en la que se encuentra tu base de datos. Por ejemplo, si tu base de datos está en us-east1
, la conexión también se dirige a un frontend de Firestore que también está en us-east1
. Estas conexiones son duraderas y permanecen abiertas hasta que la aplicación las cierra explícitamente. El frontend lee los datos de los sistemas de almacenamiento de Firestore subyacentes.
La distancia entre la ubicación física de un usuario y la ubicación de la base de datos de Firestore afecta a la latencia que experimenta el usuario. Por ejemplo, un usuario de la India cuya aplicación se comunica con una base de datos de una Google Cloud región de Norteamérica puede notar que la experiencia es más lenta y que la aplicación responde con menos rapidez que si la base de datos estuviera más cerca, por ejemplo, en la India o en otra parte de Asia.
Diseño para la fiabilidad
En los siguientes temas se explica cómo mejorar o afectar a la fiabilidad de tu aplicación:
Habilitar el modo sin conexión
Los SDKs de Firebase proporcionan persistencia de datos sin conexión. Si la aplicación del dispositivo del usuario no puede conectarse a Firestore, se podrá seguir usando con los datos almacenados en caché de forma local. De esta forma, se garantiza el acceso a los datos incluso cuando los usuarios tienen una conexión a Internet inestable o pierden el acceso por completo durante varias horas o días. Para obtener más información sobre el modo sin conexión, consulta el artículo Habilitar datos sin conexión.
Acerca de los reintentos automáticos
Los SDKs de Firebase se encargan de reintentar las operaciones y restablecer las conexiones interrumpidas. Esto ayuda a evitar errores transitorios causados por el reinicio de servidores o problemas de red entre el cliente y la base de datos.
Elegir entre ubicaciones regionales y multirregionales
Hay varias ventajas e inconvenientes a la hora de elegir entre ubicaciones regionales y multirregionales. La principal diferencia es cómo se replican los datos. Esto determina las garantías de disponibilidad de tu aplicación. Una instancia multirregional ofrece una mayor fiabilidad de servicio y aumenta la durabilidad de tus datos, pero el coste es mayor.
Información sobre el sistema de consultas en tiempo real
Las consultas en tiempo real, también llamadas listeners de instantáneas, permiten que la aplicación escuche los cambios en la base de datos y reciba notificaciones con baja latencia en cuanto cambien los datos. Una aplicación puede obtener el mismo resultado consultando periódicamente la base de datos para ver si hay actualizaciones, pero suele ser más lento, más caro y requiere más código. Para ver ejemplos de cómo configurar y usar consultas en tiempo real, consulte Recibir actualizaciones en tiempo real. En las siguientes secciones se explica cómo funcionan los listeners de instantáneas y se describen algunas de las prácticas recomendadas para escalar las consultas en tiempo real sin perder rendimiento.
Imagina que dos usuarios se conectan a Firestore a través de una aplicación de mensajería creada con uno de los SDKs para móviles.
El cliente A escribe en la base de datos para añadir y actualizar documentos en una colección llamada chatroom
:
collection chatroom:
document message1:
from: 'Sparky'
message: 'Welcome to Firestore!'
document message2:
from: 'Santa'
message: 'Presents are coming'
El cliente B escucha las actualizaciones de la misma colección mediante un listener de snapshot. El cliente B recibe una notificación inmediata cada vez que alguien crea un mensaje nuevo. En el siguiente diagrama se muestra la arquitectura de un listener de snapshots:
Cuando el cliente B conecta un listener de instantáneas a la base de datos, se produce la siguiente secuencia de eventos:
- El cliente B abre una conexión con Firestore y registra un
listener llamando a
onSnapshot(collection("chatroom"))
a través del SDK de Firebase. Este receptor puede permanecer activo durante horas. - El frontend de Firestore consulta el sistema de almacenamiento subyacente para inicializar el conjunto de datos. Carga todo el conjunto de resultados de los documentos coincidentes. A esto lo llamamos consulta de sondeo. A continuación, el sistema evalúa las reglas de seguridad de Firebase de la base de datos para verificar que el usuario puede acceder a estos datos. Si el usuario está autorizado, la base de datos le devuelve los datos.
- La consulta del cliente B pasa al modo de escucha. El receptor se registra con un controlador de suscripciones y espera las actualizaciones de los datos.
- El cliente A envía una operación de escritura para modificar un documento.
- La base de datos confirma el cambio del documento en su sistema de almacenamiento.
- De forma transaccional, el sistema confirma la misma actualización en un registro de cambios interno. El registro de cambios establece un orden estricto de los cambios a medida que se producen.
- El registro de cambios, a su vez, distribuye los datos actualizados a un grupo de controladores de suscripciones.
- Se ejecuta un matcher de consultas inversas para comprobar si el documento actualizado coincide con algún listener de instantáneas registrado. En este ejemplo, el documento coincide con el listener de la vista general del cliente B. Como su nombre indica, puedes considerar el buscador de consultas inversas como una consulta de base de datos normal, pero al revés. En lugar de buscar en los documentos aquellos que coincidan con una consulta, busca de forma eficiente en las consultas aquellas que coincidan con un documento entrante. Cuando encuentra una coincidencia, el sistema reenvía el documento en cuestión a los listeners de la captura. A continuación, el sistema evalúa las reglas de seguridad de Firebase de la base de datos para asegurarse de que solo los usuarios autorizados reciban los datos.
- El sistema reenvía la actualización del documento al SDK del dispositivo del cliente B y se activa la retrollamada
onSnapshot
. Si la persistencia local está habilitada, el SDK también aplica la actualización a la caché local.
Una parte fundamental de la escalabilidad de Firestore depende de la distribución de los cambios del registro de cambios a los controladores de suscripción y a los servidores frontend. La distribución permite que un solo cambio de datos se propague de forma eficiente para atender millones de consultas en tiempo real y usuarios conectados. Al ejecutar muchas réplicas de todos estos componentes en varias zonas (o varias regiones en el caso de una implementación multirregional), Firestore consigue una alta disponibilidad y escalabilidad.
Es importante tener en cuenta que todas las operaciones de lectura emitidas desde SDKs para móviles y web siguen el modelo anterior. Realizan una consulta de sondeo seguida del modo de escucha para mantener las garantías de coherencia. Esto también se aplica a los procesadores en tiempo real, las llamadas para obtener un documento y las consultas únicas. Puedes considerar las recuperaciones de un solo documento y las consultas únicas como listeners de instantáneas de corta duración que tienen restricciones similares en cuanto al rendimiento.
Aplicar prácticas recomendadas para escalar consultas en tiempo real
Aplica las siguientes prácticas recomendadas para diseñar consultas en tiempo real escalables.
Entender el tráfico de escritura alto en el sistema
En esta sección se explica cómo responde el sistema a un número creciente de solicitudes de escritura.
Los registros de cambios de Firestore que controlan las consultas en tiempo real se escalan horizontalmente de forma automática a medida que aumenta el tráfico de escritura. A medida que la tasa de escritura de una base de datos supera lo que puede gestionar un solo servidor, el registro de cambios se divide en varios servidores y el procesamiento de consultas empieza a consumir datos de varios controladores de suscripción en lugar de uno. Desde el punto de vista del cliente y del SDK, todo esto es transparente y la aplicación no tiene que hacer nada cuando se producen divisiones. En el siguiente diagrama se muestra cómo se escalan las consultas en tiempo real:
El escalado automático te permite aumentar el tráfico de escritura sin límites, pero, a medida que el tráfico aumenta, el sistema puede tardar un poco en responder. Sigue las recomendaciones de la regla 5-5-5 para evitar crear un punto de acceso de escritura. Key Visualizer es una herramienta útil para analizar los hotspots de escritura.
Muchas aplicaciones tienen un crecimiento orgánico predecible, que Firestore puede gestionar sin precauciones. Sin embargo, las cargas de trabajo por lotes, como la importación de un conjunto de datos grande, pueden aumentar las escrituras demasiado rápido. Cuando diseñes tu aplicación, ten en cuenta de dónde procede tu tráfico de escritura.
Entender cómo interactúan las escrituras y las lecturas
Puedes considerar el sistema de consultas en tiempo real como una canalización que conecta las operaciones de escritura con los lectores. Cada vez que se crea, actualiza o elimina un documento, el cambio se propaga del sistema de almacenamiento a los listeners registrados. La estructura del registro de cambios de Firestore garantiza una consistencia sólida, lo que significa que tu aplicación nunca recibe notificaciones de actualizaciones que no estén en orden en comparación con el momento en que la base de datos confirmó los cambios en los datos. Esto simplifica el desarrollo de aplicaciones al eliminar los casos límite relacionados con la coherencia de los datos.
Esta canalización conectada significa que una operación de escritura que provoque puntos de acceso o contención de bloqueos puede afectar negativamente a las operaciones de lectura. Cuando las operaciones de escritura fallan o experimentan una limitación, una lectura puede detenerse a la espera de datos coherentes del registro de cambios. Si esto ocurre en tu aplicación, es posible que observes operaciones de escritura lentas y tiempos de respuesta lentos correlacionados para las consultas. Evitar los puntos de acceso es la clave para no tener este problema.
Mantén los documentos y las operaciones de escritura pequeños
Cuando creas aplicaciones con listeners de instantáneas, normalmente quieres que los usuarios se enteren de los cambios en los datos rápidamente. Para ello, intenta que los elementos sean pequeños. El sistema puede enviar documentos pequeños con decenas de campos a través del sistema muy rápidamente. Los documentos más grandes, con cientos de campos y grandes cantidades de datos, tardan más en procesarse.
Del mismo modo, prioriza las operaciones de confirmación y escritura cortas y rápidas para mantener una latencia baja. Los lotes grandes pueden ofrecerte un mayor rendimiento desde el punto de vista del escritor, pero pueden aumentar el tiempo de notificación de los listeners de la instantánea. A menudo, esto es contrario a lo que se espera en comparación con otros sistemas de bases de datos, en los que se pueden usar lotes para mejorar el rendimiento.
Usar listeners eficientes
A medida que aumentan las tasas de escritura de tu base de datos, Firestore divide el procesamiento de datos en varios servidores. El algoritmo de partición de Firestore intenta colocar los datos de la misma colección o grupo de colecciones en el mismo servidor de registro de cambios. El sistema intenta maximizar el rendimiento de escritura posible y, al mismo tiempo, mantener el número de servidores implicados en el procesamiento de una consulta lo más bajo posible.
Sin embargo, algunos patrones pueden seguir provocando un comportamiento no óptimo en los listeners de instantáneas. Por ejemplo, si tu aplicación almacena la mayoría de sus datos en una colección grande, es posible que el listener tenga que conectarse a muchos servidores para recibir todos los datos que necesita. Esto sigue siendo así aunque aplique un filtro de consulta. Conectarse a muchos servidores aumenta el riesgo de que las respuestas sean más lentas.
Para evitar estas respuestas más lentas, diseña tu esquema y tu aplicación de forma que el sistema pueda servir a los oyentes sin tener que ir a muchos servidores diferentes. Puede que sea mejor dividir los datos en colecciones más pequeñas con frecuencias de escritura más bajas.
Es similar a pensar en las consultas de rendimiento de una base de datos relacional que requieren análisis de tabla completa. En una base de datos relacional, una consulta que requiere un análisis completo de la tabla es el equivalente a un listener de instantáneas que monitoriza una colección con mucha actividad. Puede que el rendimiento sea lento en comparación con una consulta que la base de datos pueda atender con un índice más específico. Una consulta con un índice más específico es como un procesador de capturas que monitoriza un solo documento o una colección que cambia con menos frecuencia. Deberías hacer una prueba de carga de tu aplicación para entender mejor el comportamiento y las necesidades de tu caso práctico.
Mantener la rapidez de las consultas de comprobación
Otra parte fundamental de las consultas en tiempo real adaptables es asegurarse de que la consulta de sondeo para inicializar los datos sea rápida y eficiente. La primera vez que se conecta un nuevo listener de instantáneas, este debe cargar todo el conjunto de resultados y enviarlo al dispositivo del usuario. Las consultas lentas hacen que tu aplicación responda peor. Por ejemplo, esto incluye las consultas que intentan leer muchos documentos o las que no usan los índices adecuados.
En algunas circunstancias, un oyente también puede pasar de un estado de escucha a un estado de sondeo. Esto ocurre automáticamente y de forma transparente para los SDKs y tu aplicación. Las siguientes condiciones pueden activar un estado de sondeo:
- El sistema reequilibra un registro de cambios debido a los cambios en la carga.
- Los puntos calientes provocan que las operaciones de escritura en la base de datos fallen o se retrasen.
- Los reinicios transitorios del servidor afectan temporalmente a los oyentes.
Si las consultas de sondeo son lo suficientemente rápidas, el estado de sondeo se vuelve transparente para los usuarios de tu aplicación.
Prioriza los listeners de larga duración
Abrir y mantener activos los listeners el mayor tiempo posible suele ser la forma más rentable de crear una aplicación que use Firestore. Cuando usas Firestore, se te cobra por los documentos devueltos a tu aplicación y no por mantener una conexión abierta. Un procesador de capturas de larga duración solo lee los datos que necesita para atender la consulta durante su ciclo de vida. Esto incluye una operación de sondeo inicial seguida de notificaciones cuando los datos cambian. Por otro lado, las consultas únicas vuelven a leer datos que puede que no hayan cambiado desde la última vez que la aplicación ejecutó la consulta.
En los casos en los que tu aplicación deba consumir una gran cantidad de datos, es posible que los listeners de capturas no sean adecuados. Por ejemplo, si tu caso práctico envía muchos documentos por segundo a través de una conexión durante un periodo prolongado, puede que sea mejor optar por consultas únicas que se ejecuten con una frecuencia menor.
Siguientes pasos
- Consulta cómo usar los listeners de instantáneas.
- Consulta más prácticas recomendadas.