Prácticas recomendadas

Puedes usar las prácticas recomendadas que se mencionan aquí como una referencia rápida de lo que debes tener en cuenta cuando compilas una aplicación que usa Cloud Firestore en modo Datastore. Si recién empiezas a usar el modo Datastore, es posible que en esta página no encuentres los conceptos básicos sobre cómo utilizarlo. Si eres un usuario nuevo, te sugerimos que comiences con Introducción a Cloud Firestore en modo Datastore.

General

  • Utiliza siempre caracteres UTF-8 para nombres de espacio de nombres, nombres de tipos, nombres de propiedad y nombres de claves personalizadas. Los caracteres que no son UTF-8 y que se utilizan en esos nombres pueden interferir en la funcionalidad del modo Datastore. Por ejemplo: Usar un carácter que no es UTF-8 en un nombre de propiedad podría impedir la creación de un índice utilizado por esta propiedad.
  • No uses una barra diagonal / en nombres de categorías o de claves personalizados. Las barras diagonales podrían interferir en funcionalidades futuras si se utilizan en estos nombres.
  • 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.

Llamadas a la API

  • Utiliza operaciones por lotes en lugar de operaciones individuales para tus lecturas, escrituras y eliminaciones. Las operaciones por lotes son más eficientes debido a que realizan operaciones múltiples con la misma sobrecarga que una operación individual.
  • Si una transacción falla, intenta revertir la transacción. La reversión minimiza la latencia de reintento de una solicitud diferente que compite por los mismos recursos en una transacción. Ten en cuenta que la reversión puede fallar, por lo tanto, debe considerarse solo como un último recurso.
  • Utiliza llamadas asíncronas en vez de síncronas cuando sea posible. Las llamadas asíncronas minimizan el impacto de la latencia. Por ejemplo, considera una aplicación que necesite el resultado de un lookup() síncrono y de una consulta antes de procesar una respuesta. Si el lookup() y la consulta no tienen una dependencia de datos no es necesario esperar de manera síncrona hasta que finalice el lookup() para iniciar la consulta.

Entidades

  • Evita escribir a una entidad más de una vez por segundo. Escribir a una frecuencia continua por encima de ese límite hace que se agoten los tiempos de espera y da lugar un rendimiento general más lento de tu aplicación.
  • No incluyas la misma entidad (por clave) varias veces en la misma confirmación. Incluir la misma entidad varias veces en la misma confirmación podría afectar la latencia.

Claves

  • Los nombres de las claves se generan de forma automática si no se proporcionaron al momento de la creación de la entidad. Se asignan de manera tal de estar distribuidas de forma uniforme en el espacio de claves.
  • Para una clave que use un nombre personalizado, siempre utiliza caracteres UTF-8, excepto la barra diagonal (/). Los caracteres que no son UTF-8 interfieren en varios procesos, como importar un archivo de exportación del modo Datastore en BigQuery. Una barra diagonal puede interferir en funcionalidades futuras.
  • Para una clave que use un ID numérico:
    • No utilices un número negativo para el ID. Un ID negativo puede interferir en el ordenamiento.
    • No utilices el valor 0(cero) para el ID. Si lo haces, obtendrás un ID asignado de manera automática.
    • Si deseas asignar manualmente tus propios ID numéricos a las entidades que creaste, haz que tu aplicación obtenga un bloque de ID con el método allocateIds(). Eso evitará que el modo Datastore asigne uno de tus ID numéricos manuales a otra entidad.
  • Si asignas tu propio ID numérico manual o nombre personalizado a las entidades que creaste, no utilices valores monótonamente crecientes como:

    1, 2, 3, …,
    "Customer1", "Customer2", "Customer3", ….
    "Product 1", "Product 2", "Product 3", ….
    

    Si una aplicación genera un tráfico alto, esa numeración secuencial podría dar lugar a hotspots que afectan la latencia del modo Datastore. Para evitar el problema de los ID numéricos secuenciales, obtén ID numéricos con el método allocateIds(). El método allocateIds() genera secuencias de ID numéricos bien distribuidas.

  • Cuando especificas una clave o almacenas el nombre generado, puedes realizar más adelante un lookup() en esa entidad, sin necesidad de emitir una consulta para encontrar la entidad.

Índices

Propiedades

  • Utiliza siempre caracteres UTF-8 para propiedades de tipo string. Un carácter que no sea UTF-8 en una propiedad de tipo string podría interferir en las consultas. Si necesitas guardar datos que contengan caracteres que no son UTF-8, utiliza un byte string.
  • No utilices puntos en los nombres de las propiedades. Los puntos en los nombres de la propiedad interfieren en la indexación de propiedades de entidad incorporadas.

Consultas

  • Si necesitas acceder solo a la clave desde los resultados de la consulta, utiliza una consulta de solo claves. Una consulta de solo claves muestra los resultados a una latencia y costo más bajos que cuando se recuperan las entidades completas.
  • Si solo necesitas acceder a propiedades específicas de una entidad, utiliza una consulta de proyección. Una consulta de proyección muestra los resultados a una latencia y costo más bajos que cuando se recuperan las entidades completas.
  • Del mismo modo, si solo necesitas acceder a las propiedades que están incluidas en el filtro de consulta (por ejemplo, aquellas que se muestran en una cláusula order by), utiliza una consulta de proyección.
  • No uses compensaciones. En su lugar, utiliza cursores. Utilizar una compensación solo evita que las entidades omitidas se recuperen en tu aplicación, pero igual se recuperan de forma interna. Las entidades omitidas afectan la latencia de la consulta y tu aplicación se factura de acuerdo con las operaciones de lectura requeridas para recuperarlas.

Diseñar para escalamiento

Actualizaciones de una entidad

Una entidad única en el modo Datastore no debe actualizarse con demasiada rapidez.

Si estás utilizando el modo Datastore, diseña tu aplicación de manera tal que no necesite actualizar una entidad más de una vez por segundo. Si actualizas una entidad con demasiada rapidez, tus operaciones de escritura del modo Datastore registrarán tiempos de espera y latencia mayores, así como otros tipos de errores. Esto se conoce como contención.

A veces, las tasas de escritura del modo Datastore para una entidad pueden exceder el límite de una por segundo, por lo que las pruebas de carga podrían no mostrar este problema.

Tasas altas de lectura/escritura para un rango pequeño de claves

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 experimentarlo si realiza alguna de las siguientes acciones:

  • Crea entidades nuevas a una tasa muy alta y asigna sus propios ID que aumentan monótonamente.

    El modo Datastore asigna claves con un algoritmo de dispersión. Si creas entidades nuevas con asignación automática de ID de entidad, no deberías encontrar generación de hotspots durante las operaciones de escritura.

  • Crea entidades nuevas a una tasa muy alta con la política de asignación de ID secuencial heredada.

  • Crea entidades nuevas a una tasa alta para un tipo con pocas entidades.

  • Crea entidades nuevas con un valor de propiedad indexado y que aumenta monótonamente, como una marca de tiempo, a una tasa muy alta.

  • Borra entidades de un tipo a una tasa alta.

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

Si usas el modo Datastore, puedes obtener lecturas lentas debido a un hotspot si experimentas un aumento repentino en la velocidad de escritura de un rango pequeño de claves. En algún momento, Cloud Firestore en modo Datastore dividirá el espacio de claves para admitir una carga alta.

El límite de operaciones de lectura suele ser más alto que el de las de escritura, a menos que la lectura sea desde una clave única a una tasa alta.

Los hotspots se pueden aplicar a los rangos de clave utilizados por las claves de entidad y por los índices.

En algunos casos, un hotspot puede causar un impacto mayor en una aplicación que prevenir operaciones de lectura o escritura en un rango pequeño de claves. Por ejemplo, las teclas de acceso rápido se pueden leer o escribir durante el inicio de la instancia y eso causaría que la carga de solicitudes falle.

Si tienes una clave o una propiedad indexada que aumentará monótonamente, entonces puedes agregar un hash al azar para asegurarte de que las claves se compartan a múltiples tablets.

De la misma manera, si necesitas consultar una propiedad que aumenta (o disminuye) monótonamente con un orden o un filtro, podrías indexar una nueva propiedad, para la cual debes prefijar el valor monótono con un valor que tenga cardinalidad alta a lo largo del conjunto de datos, pero que es común a todas las entidades en el alcance de la consulta que quieres realizar. Por ejemplo, si quieres consultar entradas por marcas de tiempo, pero solo necesitas que muestre resultados para un usuario a la vez, puedes prefijar la marca de tiempo con el ID del usuario y, luego, indexar esa nueva propiedad. Esto permitirá consultas y ofrecerá los resultados en orden para ese usuario, pero la presencia del ID del usuario garantizará que el índice esté bien fragmentado.

Acelera el tráfico

Acelera gradualmente el tráfico hacia nuevos tipos o partes del espacio de claves.

Debes acelerar el tráfico de forma gradual a nuevos tipos con el propósito de dar a Cloud Firestore en modo Datastore suficiente tiempo a fin de prepararse para el aumento del tráfico. Recomendamos realizar un máximo de 500 operaciones por segundo en un tipo nuevo y, luego, aumentar el tráfico en un 50% cada 5 minutos. En teoría, puedes alcanzar 740,000 operaciones por segundo después de utilizar este programa de aceleración por 90 minutos. Asegúrate de que las operaciones de escritura se encuentren distribuidas de manera uniforme en todo el rango de claves. Nuestros SRE lo llaman la regla "500/50/5".

Este patrón de aceleración gradual es muy importante si cambias tu código para dejar de utilizar el tipo A y comienzas a utilizar el tipo B. Una manera simple de realizar esta migración es cambiar tu código para que lea el tipo B y, si no existe, lea el tipo A. Sin embargo, esto puede causar un incremento repentino en el tráfico en un nuevo tipo con una porción muy pequeña del espacio de claves.

El mismo problema puede surgir si migras tus entidades para utilizar un rango de claves diferente dentro del mismo tipo.

La estrategia que utilices para migrar entidades a una nueva categoría o clave dependerá de tu nuevo modelo de datos. A continuación, puedes encontrar una estrategia de ejemplo conocida como "Lecturas paralelas". Tendrás que determinar si esta estrategia es eficaz para tus datos o no. Un punto importante a tener en cuenta será el impacto del costo de las operaciones paralelas durante la migración.

Lee primero la entidad o la clave antigua. Si esa falta, entonces puedes leer desde la entidad o clave nueva. Una tasa alta de lecturas de entidades no existentes puede causar una generación de hotspots, por lo tanto, debes asegurarte de incrementar la carga de manera gradual. Una mejor estrategia es copiar la entidad antigua a la nueva y luego borrar la antigua. Acelera las lecturas paralelas de manera gradual para asegurarte de que el nuevo espacio de claves esté divido correctamente.

Una estrategia posible para acelerar lecturas o escrituras de manera gradual en una categoría nueva es utilizar un hash determinista del ID del usuario para obtener un porcentaje aleatorio de usuarios que escriban nuevas entidades. Asegúrate de que el resultado del hash del ID de usuario no se encuentre sesgado ni por tu función aleatoria ni por el comportamiento de tu usuario.

Mientras tanto, ejecuta un trabajo de Dataflow para copiar todos tus datos de las entidades o claves antiguas a las nuevas. Este trabajo en lotes debe evitar escrituras en claves secuenciales para prevenir la generación de hotspots. Cuando tu trabajo en lotes está completo, solo puedes leer desde la nueva ubicación.

Una mejor manera de realizar esta estrategia es migrar lotes pequeños de usuarios al mismo tiempo. Agrega un campo a la entidad de usuario que rastrea el estado de la migración de ese usuario. Selecciona un lote de usuarios que deseas migrar basado en un hash del ID de usuario. Un trabajo de Mapreduce o Dataflow migrará las claves de esos lotes de usuarios. Los usuarios que tengan una migración en curso utilizarán lecturas paralelas.

Ten en cuenta que no es fácil revertir el proceso, a menos que realices operaciones de escritura duales de las entidades antiguas y nuevas durante la fase de migración. Esto incrementará los costos del modo Datastore en que se incurrió.

Eliminaciones

Evita borrar una gran cantidad de entidades en un rango pequeño de claves.

Cloud Firestore en modo Datastore vuelve a escribir sus tablas de forma periódica para quitar las entradas borradas y reorganizar los datos a fin de que las operaciones de lectura y escritura sean más eficientes. Este proceso se conoce como una compactación.

Si borras una gran cantidad de entidades del modo Datastore en un pequeño rango de claves, las consultas en esta parte del índice serán más lentas hasta que se complete la compactación. En casos extremos, tus consultas pueden agotar el tiempo de espera antes de mostrar resultados.

Es un antipatrón utilizar un valor de marca de tiempo de un campo indexado para representar el tiempo de caducidad de una entidad. Para recuperar entidades vencidas, necesitarás consultar campos indexados que probablemente se encuentren en una parte superpuesta del espacio de claves con entradas de índice para las entidades borradas más recientes.

Puedes mejorar el rendimiento con "consultas de fragmentación" que agregan una string de longitud fija a la marca de tiempo de vencimiento. El índice está ordenado en la string completa para que las entidades con las mismas marcas de tiempo se puedan localizar a lo largo del rango de claves del índice. Puedes ejecutar múltiples consultas en paralelo para recuperar resultados de cada fragmentación.

Una solución más completa para el problema de la marca de tiempo de vencimiento es un "número de generación", un contador global que se actualiza de manera periódica. El número de generación está agregado en la marca de tiempo de vencimiento para que las consultas se ordenen por número de generación, luego por fragmentación y luego por marca de tiempo. La eliminación de las entidades antiguas ocurre en una generación anterior. El número de generación de una entidad que no se borró debería aumentar. Una vez que la eliminación está completa, avanzas a la próxima generación. Las consultas por una generación antigua no se realizarán correctamente hasta que la compactación esté completa. Podrías tener que esperar que varias generaciones se completen para poder consultar el índice y obtener la lista de entidades que borrar a fin de reducir el riesgo de que falten resultados debido a una coherencia eventual.

Fragmentación y replicación

Usa la fragmentación o la replicación para lidiar con los hotspots.

Puedes utilizar la replicación si necesitas leer una parte del rango de claves a una tasa más alta que la que permite Cloud Firestore en modo Datastore. Si utilizas esta estrategia, almacenarás N copias de la misma entidad, lo cual permite una tasa de lectura N veces más alta que la admitida por una entidad única.

Puedes utilizar la fragmentación si necesitas escribir una parte del rango de claves a una tasa más alta que la que permite Cloud Firestore en modo Datastore. La fragmentación divide una entidad en unidades más pequeñas.

Algunos de los errores más comunes de fragmentación son:

  • Fragmentar con un prefijo de tiempo. Luego de que el tiempo se desplaza al siguiente prefijo, la parte nueva sin dividir se convierte en un hotspot. En cambio, debes desplazar de manera gradual una parte de tu escritura al nuevo prefijo.

  • Fragmentar solo las entidades más sobrecargadas. Si fragmentas una pequeña proporción del número total de entidades, podría no haber filas suficientes entre las entidades sobrecargadas para asegurar de que permanezcan en divisiones diferentes.

Pasos siguientes

¿Te sirvió esta página? Envíanos tu opinión:

Enviar comentarios sobre…

Documentación de Cloud Datastore