Estructura los datos para lograr una coherencia sólida

Nota: Se recomienda enfáticamente a los desarrolladores que compilan aplicaciones nuevas que usen la biblioteca cliente de NDB, ya que tiene muchas ventajas en comparación con esta biblioteca cliente, como el almacenamiento en caché automático de entidades mediante la API de Memcache. Si por el momento usas la biblioteca cliente de DB anterior, lee la Guía de migración de DB a NDB.

Datastore proporciona alta disponibilidad, escalabilidad y durabilidad mediante la distribución de datos en muchas máquinas y el uso de una replicación síncrona en un área geográfica amplia. Sin embargo, este diseño tiene la desventaja de que la capacidad de procesamiento de las operaciones de escritura de cualquier grupo de entidades se limita a alrededor de una confirmación por segundo. También hay limitaciones para las consultas o las transacciones que pueden alcanzar varios grupos de entidades. En esta página, se describen esas limitaciones en mayor profundidad y se analizan las recomendaciones para estructurar los datos a fin de lograr una coherencia sólida y cumplir con los requisitos de rendimiento de escritura de la aplicación.

Las lecturas de coherencia sólida siempre muestran datos actuales y, si se realizan dentro de una transacción, parecerán provenir de una única instantánea coherente. Sin embargo, las consultas deben especificar un filtro principal para tener coherencia sólida o participar en una transacción, y las transacciones pueden involucrar un máximo de 25 grupos de entidades. Las lecturas con coherencia eventual no tienen esas limitaciones y son adecuadas en muchos casos. Utilizar lecturas con coherencia eventual puede permitirte distribuir tus datos entre más grupos de entidades, lo que hace posible obtener una mayor capacidad de procesamiento de escritura a través de la ejecución de confirmaciones en paralelo en los distintos grupos de entidades. Sin embargo, debes comprender las características de las lecturas con coherencia eventual a fin de determinar si son adecuadas para tu aplicación:

  • Los resultados de esas lecturas pueden no reflejar las transacciones más actuales. Esto puede suceder porque esas lecturas no se aseguran de que la réplica sobre la que se ejecutan esté actualizada. En cambio, utilizan cualquier dato disponible en esa réplica cuando se ejecuta la consulta. La latencia de replicación es casi siempre inferior a unos pocos segundos.
  • Puede parecer que una transacción confirmada en múltiples entidades se aplicó solo a algunas de ellas. Sin embargo, nunca parecerá que una transacción se aplicó de manera parcial dentro de una sola entidad.
  • Los resultados de una consulta pueden contener entidades que no deberían haberse incluido según los criterios del filtro y es posible que se excluyan entidades que sí deberían haberse incluido. Esto puede ocurrir porque los índices pueden leerse en una versión diferente de la que se lee en la entidad.

Si deseas comprender cómo estructurar tus datos para una coherencia sólida, compara dos enfoques diferentes correspondientes a una aplicación de libro de visitas simple. Con el primer enfoque, se crea una entidad raíz nueva para cada entidad:

import webapp2
from google.appengine.ext import db

class Guestbook(webapp2.RequestHandler):
  def post(self):
    greeting = Greeting()
    ...

A continuación, consulta el tipo de entidad Greeting para obtener los diez saludos más recientes.

import webapp2
from google.appengine.ext import db

class MainPage(webapp2.RequestHandler):
  def get(self):
    self.response.out.write('<html><body>')
    greetings = db.GqlQuery("SELECT * "
                            "FROM Greeting "
                            "ORDER BY date DESC LIMIT 10")

Sin embargo, debido a que usas una consulta no principal, es posible que la réplica usada para realizar la consulta en este esquema no haya visto el saludo nuevo en el momento en que se ejecuta la consulta. No obstante, casi todas las escrituras estarán disponibles para consultas no principales pocos segundos después de su confirmación. En el caso de muchas aplicaciones, una solución que proporcione los resultados de una consulta no principal en el contexto de los cambios propios del usuario actual será generalmente suficiente para hacer que las latencias de replicación sean completamente aceptables.

Si es importante que tu aplicación tenga coherencia sólida, un enfoque alternativo es escribir entidades con una ruta de acceso principal que identifique la misma entidad raíz en todas las entidades que deben leerse en una consulta principal única con coherencia sólida:

import webapp2
from google.appengine.ext import db

class Guestbook(webapp2.RequestHandler):
  def post(self):
    guestbook_name=self.request.get('guestbook_name')
    greeting = Greeting(parent=guestbook_key(guestbook_name))
    ...

Luego podrás realizar una consulta principal con coherencia sólida dentro del grupo de entidades identificado por la entidad raíz común.

import webapp2
from google.appengine.ext import db

class MainPage(webapp2.RequestHandler):
  def get(self):
    self.response.out.write('<html><body>')
    guestbook_name=self.request.get('guestbook_name')

    greetings = db.GqlQuery("SELECT * "
                            "FROM Greeting "
                            "WHERE ANCESTOR IS :1 "
                            "ORDER BY date DESC LIMIT 10",
                            guestbook_key(guestbook_name))

Este enfoque logra una coherencia sólida por medio de la escritura en un solo grupo de entidad por libro de visitas, pero también limita los cambios al libro de visitas a no más de 1 escritura por segundo (el límite admitido para los grupos de entidad). Si es probable que la aplicación tenga un uso más intenso de la escritura, podría ser conveniente que uses otros medios: por ejemplo, puedes colocar publicaciones recientes en un Memcache con vencimiento y mostrar una mezcla de publicaciones recientes de Memcache y Datastore, o podrías almacenarlas en caché en una cookie, poner algún estado en la URL, o una opción completamente distinta. El objetivo es encontrar una solución de almacenamiento en caché que le brinde datos al usuario actual durante el período en el que el usuario publica en tu aplicación. Recuerda que, si realizas una operación get, una consulta principal o cualquier operación dentro de una transacción, siempre verás los datos escritos más recientes.