Prácticas recomendadas

Usa estas prácticas recomendadas como una referencia rápida cuando compiles una aplicación que use Firestore.

Ubicación de la base de datos

Cuando crees tu instancia de base de datos, selecciona la ubicación más cercana a los usuarios y recursos de procesamiento. Los saltos de red de largo alcance son más propensos a errores y aumentan la latencia de las consultas.

Para maximizar la disponibilidad y durabilidad de tu aplicación, selecciona una ubicación multirregional y coloca recursos de procesamiento críticos en al menos dos regiones.

Selecciona una ubicación regional para disminuir los costos o la latencia de las operaciones de escritura si tu aplicación es sensible a la latencia o si deseas una ubicación conjunta con otros recursos de GCP.

IDs de documento

  • No uses . y .. en los IDs de documento.
  • No uses barras diagonales / en los ID de documento.
  • No uses ID de documento que aumenten monótonamente, como los siguientes ejemplos:

    • Customer1, Customer2, Customer3
    • Product 1, Product 2, Product 3

    Estos IDs secuenciales pueden generar hotspots que afectan la latencia.

Nombres de campos

  • No uses los siguientes caracteres en los nombres de campos, ya que requieren escapes adicionales:

    • . punto
    • [ corchete izquierdo
    • ] corchete derecho
    • * asterisco
    • ` acento grave

Índices

Reduce la latencia de escritura

El factor principal que contribuye a la latencia de escritura es el fanout de índices. Estas son las prácticas recomendadas para reducir el fanout de índices:

  • Establece exenciones de índices a nivel de la colección. Una opción predeterminada sencilla es inhabilitar el orden descendente y el indexado de array. Quitar los valores indexados sin usar también reducirá los costos de almacenamiento.

  • Reduce la cantidad de documentos incluidos en una transacción. Para escribir una gran cantidad de documentos, considera usar un escritor masivo en lugar del escritor de lotes atómicos.

Exenciones de índices

En la mayoría de las apps, puedes usar la indexación automática y los vínculos de mensajes de error para administrar tus índices. Sin embargo, tal vez debas agregar exenciones de campo único en los siguientes casos:

Caso Descripción
Campos de string grandes

Si tienes un campo de string que suele tener valores de string largos que no usas para realizar consultas, exime al campo de la indexación a fin de reducir los costos de almacenamiento.

Tasas altas de escritura en una colección que tiene documentos con valores secuenciales

Si indexas un campo que aumenta o disminuye secuencialmente entre los documentos de una colección, como una marca de tiempo, la tasa máxima de escritura en la colección es de 500 operaciones de escritura por segundo. Si no realizas consultas basadas en campos con valores secuenciales, puedes eximir al campo de la indexación para pasar ese límite.

Por ejemplo, en un caso de uso de IoT con una tasa alta de escritura, es posible que una colección de documentos con un campo de marca de tiempo se acerque al límite de 500 operaciones de escritura por segundo.

Campos TTL

Si usas políticas de TTL (tiempo de actividad), ten en cuenta que el campo TTL debe ser una marca de tiempo. La indexación en los campos TTL está habilitada de forma predeterminada y puede afectar el rendimiento con tasas de tráfico más altas. Como práctica recomendada, agrega exenciones de campo único para tus campos TTL.

Campos grandes de array o de mapa

Los campos grandes de array o de mapa pueden acercarse al límite de 40,000 entradas de índice por documento. Si no realizas consultas basadas en un campo grande de array o de mapa, debes excluir el campo de la indexación.

Operaciones de lectura y escritura

  • La frecuencia máxima exacta con la que una app puede actualizar un solo documento depende en gran medida de la carga de trabajo. Para obtener más información, consulta Actualizaciones en un solo documento.

  • Usa llamadas asíncronas en vez de síncronas cuando sea posible. Las llamadas asíncronas minimizan el impacto en la latencia. Por ejemplo, considera una aplicación que necesite el resultado de una consulta y de la búsqueda de un documento antes de procesar una respuesta. Si la búsqueda y la consulta no tienen una dependencia de datos, no es necesario esperar de manera síncrona hasta que finalice la búsqueda para iniciar la consulta.

  • No uses desplazamientos. En su lugar, usa cursores. Usar un desplazamiento solo evita que los documentos omitidos se muestren a tu aplicación, pero aún se recuperan internamente. Los documentos omitidos afectan la latencia de la consulta, y tu aplicación se factura por las operaciones de lectura necesarias para recuperarlos.

Reintentos de transacciones

Los SDK y las bibliotecas cliente de Firestore vuelven a intentar realizar de forma automática las transacciones fallidas para lidiar con los errores transitorios. Si tu aplicación accede a Firestore a través de las API de REST o RPC directamente en lugar de hacerlo a través de un SDK, debes implementar reintentos de transacción en ella para aumentar la confiabilidad.

Actualizaciones en tiempo real

Para conocer las prácticas recomendadas relacionadas con las actualizaciones en tiempo real, consulta Información sobre las consultas en tiempo real a gran escala.

Diseña para el escalamiento

En las siguientes recomendaciones, se describe cómo evitar situaciones que generen problemas de contención.

Actualizaciones en un solo documento

Mientras diseñas tu app, ten en cuenta la rapidez con la que esta actualiza los documentos individuales. La mejor manera de definir el rendimiento de tu carga de trabajo es realizar pruebas de carga. La frecuencia máxima exacta con la que una app puede actualizar un solo documento depende en gran medida de la carga de trabajo. Los factores incluyen la tasa de escritura, la competencia entre las solicitudes y la cantidad de índices afectados.

Una operación de escritura de documento actualiza el documento y los índices asociados, y Firestore aplica de manera síncrona la operación de escritura en un quórum de réplicas. Con tasas de escritura lo suficientemente altas, la base de datos comenzará a encontrar contención, mayor latencia o cualquier otro error.

Tasas altas de lectura, escritura y eliminación en un rango pequeño de documentos

Evita las tasas altas de lectura o escritura en documentos que se encuentren cerca lexicográficamente. De lo contrario, tu aplicación experimentará errores de contención. Este problema se conoce como generación de hotspots, y tu aplicación puede sufrirlo si realiza alguna de las siguientes acciones:

  • Crea documentos nuevos a una tasa muy alta y asigna sus propios IDs que incrementan de forma monótona.

    Firestore asigna los ID de documento con un algoritmo de dispersión. Si creas documentos nuevos con ID automáticos, no deberías encontrar hotspots durante las operaciones de escritura.

  • Crea documentos nuevos a una tasa alta en una colección que tiene pocos documentos.

  • Crea documentos nuevos con un campo que incrementa monótonamente, como una marca de tiempo, a una tasa muy alta.

  • Borra documentos de una colección a una tasa alta.

  • Escribe en la base de datos con una frecuencia muy alta sin aumentar el tráfico de forma gradual.

Evita omitir los datos borrados

Evita las consultas que omitan datos borrados recientemente. Es posible que una consulta deba omitir una gran cantidad de entradas de índice si los primeros resultados se borraron recientemente.

Por ejemplo, si una carga de trabajo busca los elementos más antiguos de la cola, seguramente deba omitir muchos datos borrados. La consulta podría verse de la siguiente manera:

docs = db.collection('WorkItems').order_by('created').limit(100)
delete_batch = db.batch()
for doc in docs.stream():
  finish_work(doc)
  delete_batch.delete(doc.reference)
delete_batch.commit()

Cada vez que se ejecuta esta consulta, se escanean las entradas de índice del campo created en los documentos borrados recientemente. Esto ralentiza las consultas.

Si deseas mejorar el rendimiento, usa el método start_at para encontrar el mejor punto de partida. Por ejemplo:

completed_items = db.collection('CompletionStats').document('all stats').get()
docs = db.collection('WorkItems').start_at(
    {'created': completed_items.get('last_completed')}).order_by(
        'created').limit(100)
delete_batch = db.batch()
last_completed = None
for doc in docs.stream():
  finish_work(doc)
  delete_batch.delete(doc.reference)
  last_completed = doc.get('created')

if last_completed:
  delete_batch.update(completed_items.reference,
                      {'last_completed': last_completed})
  delete_batch.commit()

NOTA: En el ejemplo anterior, se usa un campo que incrementa monótonamente, que es un antipatrón para tasas altas de escritura.

Acelera el tráfico

Debes acelerar el tráfico de manera gradual en las colecciones nuevas o en los documentos que se encuentren cerca de manera lexicográfica. Esto permite que Firestore tenga tiempo suficiente para preparar los documentos según el aumento de tráfico. Te recomendamos que empieces con un máximo de 500 operaciones por segundo en una colección nueva. Luego, puedes incrementar el tráfico un 50% cada 5 minutos. Asimismo, puedes acelerar el tráfico de escritura, pero ten en cuenta los límites estándar de Firestore. Asegúrate de que las operaciones se encuentren distribuidas de manera relativamente uniforme en todo el rango de claves. Esto se conoce como la regla “500/50/5”.

Migra el tráfico a una colección nueva

Una aceleración gradual es muy importante si migras el tráfico de la app de una colección a otra. Una forma sencilla de controlar esta migración es leer la colección antigua, y, si el documento no existe, leer la colección nueva. Sin embargo, esto podría causar un aumento repentino del tráfico de los documentos que se encuentran cerca lexicográficamente en la colección nueva. Es posible que Firestore no pueda preparar de manera eficiente la colección nueva para el aumento del tráfico, en especial si contiene pocos documentos.

Es posible que ocurra un problema similar si cambias los ID de muchos documentos que están en la misma colección.

La mejor estrategia para migrar el tráfico a una colección nueva depende de tu modelo de datos. A continuación, se muestra un ejemplo de estrategia que se conoce como lecturas paralelas. Deberás determinar si esta estrategia es eficaz para tus datos y tener en cuenta el impacto del costo de las operaciones paralelas durante la migración.

Lecturas paralelas

Para implementar lecturas paralelas mientras migras el tráfico a una colección nueva, lee primero el documento de la colección antigua. Si no existe, léelo de la colección nueva. Una tasa alta de lecturas de documentos que no existen puede causar una generación de hotspots, por lo que debes asegurarte de aumentar la carga gradualmente en la colección nueva. Una mejor estrategia es copiar el documento antiguo a la colección nueva y, luego, borrarlo. Acelera las lecturas paralelas de manera gradual para asegurarte de que Firestore pueda controlar el tráfico hacia la colección nueva.

Una estrategia útil para acelerar de forma gradual las lecturas o escrituras hacia una colección nueva es usar un hash determinista del ID de usuario a fin de seleccionar un porcentaje aleatorio de usuarios que tratan de escribir documentos nuevos. Asegúrate de que el resultado del hash del ID de usuario no se encuentre sesgado por tu función o el comportamiento del usuario.

Mientras tanto, ejecuta un trabajo por lotes que copie todos tus datos de los documentos antiguos a la colección nueva. Este trabajo debe evitar las operaciones de escritura en los IDs de documento secuenciales para prevenir la generación de hotspots. Cuando se complete el trabajo por lotes, solo podrás leer la colección nueva.

Una versión mejorada de esta estrategia consiste en migrar lotes pequeños de usuarios a la vez. Primero, agrega un campo al documento del usuario que realice un seguimiento del estado de la migración de ese usuario. Luego, selecciona un lote de usuarios para migrar en función de un hash del ID de usuario. Por último, usa un trabajo por lotes a fin de migrar los documentos de ese lote de usuarios y usa lecturas paralelas para los usuarios que estén en el proceso de migración.

Ten en cuenta que no es fácil revertir el proceso, a menos que realices operaciones de escritura dobles de las entidades antiguas y nuevas durante la fase de migración. Esto aumentaría los costos generados en Firestore.

Privacidad

  • Evita almacenar información sensible en un ID del proyecto de Cloud. El ID del proyecto de Cloud podría conservarse aún después de que finalice tu proyecto.
  • Como práctica recomendada de cumplimiento de datos, sugerimos que no almacenes información sensible en los nombres de los documentos y campos.