Estructurar los datos para lograr una coherencia sólida

Cloud 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 y sin instancia maestra en un área geográfica amplia. Sin embargo, una compensación en este diseño es que el rendimiento de las operaciones de escritura para cualquier grupo de entidad está limitado a una confirmación por segundo. También existen algunas limitaciones para las consultas o transacciones que abarcan varios grupos de entidad. 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.

Niveles de coherencia

Las consultas en Datastore pueden arrojar resultados en uno de los dos niveles de coherencia:

  • Coherencia sólida: Las consultas garantizan los resultados más actualizados, pero pueden tomar más tiempo o pueden no ser compatibles en algunos casos.
  • Coherencia eventual: Las consultas, en general, se ejecutan más rápido, pero a veces pueden mostrar resultados obsoletos.

En una consulta de coherencia eventual, se accede a los índices utilizados para reunir los resultados con coherencia eventual. Como consecuencia, esas consultas pueden a veces mostrar entidades que ya no coinciden con el criterio de la consulta y, además, pueden omitir entidades que coinciden con el criterio de la consulta. Las consultas de coherencia sólida son consistentes de forma transaccional, lo que significa que los resultados se basan en una sola instantánea consistente de los datos.

Garantías de coherencia

Las consultas muestran sus resultados con distintos niveles de garantía de coherencia, según la naturaleza de la consulta:

  • Las consultas principales (que se ejecutan en un grupo de entidad) son de coherencia sólida según la configuración predeterminada, pero se pueden volver de coherencia eventual si se configura la política de lectura de Datastore (detallada a continuación).
  • Las consultas generales (que no se ejecutan en un grupo de entidad) son siempre de coherencia eventual.

En muchas aplicaciones es aceptable utilizar coherencia eventual (es decir, una consulta general que abarca varios grupos de entidad, que a veces pueden mostrar datos obsoletos) cuando obtenemos una vista general de datos no relacionados y, después, utilizar coherencia sólida (una consulta principal o una búsqueda de una entidad única) cuando observamos o editamos un solo conjunto de datos muy relacionados entre sí. En esas aplicaciones, por lo general, es una buena estrategia ubicar los datos muy relacionados entre sí en grupos de entidades. Un número mayor de grupos de entidad aumenta la capacidad de procesamiento, mientras que un número menor de grupos de entidad aumenta el volumen de las entidades que se pueden leer en una sola consulta principal. La aplicación debe tener esto en cuenta para determinar el balance correcto de capacidad de procesamiento y coherencia.

Política de lectura de Datastore

Para mejorar el rendimiento, puedes establecer una política de lectura de consulta a fin de que los resultados sean de coherencia eventual (la API de Datastore también te permite establecer de forma explícita una política de coherencia sólida, aunque esta configuración no tenga efectos prácticos porque las consultas globales siempre son de coherencia eventual, sin importar la política).

Puedes habilitar lecturas de coherencia eventual mediante las opciones de operaciones de lectura del objeto de consulta:

C#

Para obtener información sobre cómo instalar y usar la biblioteca cliente de Cloud Datastore, consulta las bibliotecas cliente de Cloud Datastore. Si quieres obtener más información, consulta la documentación de referencia de la API de Cloud Datastore para C#.

Query query = new Query("Task")
{
    Filter = Filter.HasAncestor(_db.CreateKeyFactory("TaskList")
        .CreateKey(keyName))
};
var results = _db.RunQuery(query,
    ReadOptions.Types.ReadConsistency.Eventual);

Go

Para obtener información sobre cómo instalar y usar la biblioteca cliente de Cloud Datastore, consulta las bibliotecas cliente de Cloud Datastore. Si quieres obtener más información, consulta la documentación de referencia de la API de Cloud Datastore para Go.

ancestor := datastore.NameKey("TaskList", "default", nil)
query := datastore.NewQuery("Task").Ancestor(ancestor).EventualConsistency()

Java

Para obtener información sobre cómo instalar y usar la biblioteca cliente de Cloud Datastore, consulta las bibliotecas cliente de Cloud Datastore. Si quieres obtener más información, consulta la documentación de referencia de la API de Cloud Datastore para Java.

Query<Entity> query = Query.newEntityQueryBuilder()
    .setKind("Task")
    .setFilter(PropertyFilter.hasAncestor(
        datastore.newKeyFactory().setKind("TaskList").newKey("default")))
    .build();
datastore.run(query, ReadOption.eventualConsistency());

Node.js

Para obtener información sobre cómo instalar y usar la biblioteca cliente de Cloud Datastore, consulta las bibliotecas cliente de Cloud Datastore. Si quieres obtener más información, consulta la documentación de referencia de la API de Cloud Datastore para Node.js.

const ancestorKey = datastore.key(['TaskList', 'default']);
const query = datastore.createQuery('Task').hasAncestor(ancestorKey);

query.run({consistency: 'eventual'});

PHP

Para obtener información sobre cómo instalar y usar la biblioteca cliente de Cloud Datastore, consulta las bibliotecas cliente de Cloud Datastore. Si quieres obtener más información, consulta la documentación de referencia de la API de Cloud Datastore para PHP.

$query = $datastore->query()
    ->kind('Task')
    ->hasAncestor($datastore->key('TaskList', 'default'));
$result = $datastore->runQuery($query, ['readConsistency' => 'EVENTUAL']);

Python

Para obtener información sobre cómo instalar y usar la biblioteca cliente de Cloud Datastore, consulta las bibliotecas cliente de Cloud Datastore. Si quieres obtener más información, consulta la documentación de referencia de la API de Cloud Datastore para Python.

# Read consistency cannot be specified in google-cloud-python.

Ruby

Para obtener información sobre cómo instalar y usar la biblioteca cliente de Cloud Datastore, consulta las bibliotecas cliente de Cloud Datastore. Si quieres obtener más información, consulta la documentación de referencia de la API de Cloud Datastore para Ruby.

ancestor_key = datastore.key "TaskList", "default"

query = datastore.query("Task")
                 .ancestor(ancestor_key)

tasks = datastore.run query, consistency: :eventual

Consideraciones sobre las transacciones y la coherencia

Las confirmaciones de Datastore son transaccionales, lo que significa que tienen lugar en el contexto de una transacción y el conjunto de mutaciones de la transacción se aplica en su totalidad o no se aplica ninguna, o no transaccional, lo que significa que el conjunto de mutaciones no se puede aplicar en su totalidad ni a ninguna.

Una sola transacción puede incluir cualquier número de operaciones de creación, actualización y borrado de mutaciones. Para mantener la coherencia de los datos, la transacción se asegura de que todas las mutaciones que contiene se apliquen en Datastore como una unidad o, si ninguna mutación falla, que ninguna de ellas se aplique. Además, todas las operaciones de lecturas de coherencia sólida (operaciones lookup o consultas principales) que se realizan dentro de la misma transacción se basan en una sola instantánea coherente de los datos. Las consultas de coherencia sólida deben especificar un filtro principal. Las consultas que participan en una transacción son siempre de coherencia sólida. Las transacciones pueden incluir como máximo a 25 grupos de entidades. Las lecturas de consistencia eventual no tienen esas limitaciones y son adecuadas para muchos casos. Utilizar lecturas de coherencia eventual te puede permite distribuir los datos entre más grupos de entidad, lo que te posibilita 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 de 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 al momento en que se ejecuta la consulta.
  • Una transacción confirmada que incluyó varios grupos de entidad puede parecer que se haya aplicado a algunas entidades, pero no a otras. Sin embargo, ten en cuenta que nunca parecerá que se aplicó una transacción de manera parcial dentro de una sola entidad.
  • Los resultados de la consulta pueden incluir entidades que no deberían haber sido incluidas según el criterio del filtro y puede excluir entidades que sí deberían haber sido incluidas. Esto puede suceder porque la versión de la instantánea que se utilizó para leer los índices puede ser distinta a la versión de la instantánea utilizada al momento de leer la entidad.

Estructura tus datos para generar coherencia

Si quieres aprender a estructurar los datos para lograr una coherencia sólida, compara dos estrategias distintas para una aplicación simple de lista de tareas. La primera estrategia crea cada entidad en su propio grupo de entidad nuevo (es decir, cada entidad es una entidad raíz):

C#

Para obtener información sobre cómo instalar y usar la biblioteca cliente de Cloud Datastore, consulta las bibliotecas cliente de Cloud Datastore. Si quieres obtener más información, consulta la documentación de referencia de la API de Cloud Datastore para C#.

Entity task = new Entity()
{
    Key = _db.CreateKeyFactory("Task").CreateKey("sampleTask"),
    ["category"] = "Personal",
    ["done"] = false,
    ["priority"] = 4,
    ["description"] = "Learn Cloud Datastore"
};

Go

Para obtener información sobre cómo instalar y usar la biblioteca cliente de Cloud Datastore, consulta las bibliotecas cliente de Cloud Datastore. Si quieres obtener más información, consulta la documentación de referencia de la API de Cloud Datastore para Go.

type Task struct {
	Category        string
	Done            bool
	Priority        float64
	Description     string `datastore:",noindex"`
	PercentComplete float64
	Created         time.Time
}
task := &Task{
	Category:        "Personal",
	Done:            false,
	Priority:        4,
	Description:     "Learn Cloud Datastore",
	PercentComplete: 10.0,
	Created:         time.Now(),
}

Java

Para obtener información sobre cómo instalar y usar la biblioteca cliente de Cloud Datastore, consulta las bibliotecas cliente de Cloud Datastore. Si quieres obtener más información, consulta la documentación de referencia de la API de Cloud Datastore para Java.

Key taskKey = datastore.newKeyFactory()
    .setKind("Task")
    .newKey("sampleTask");
Entity task = Entity.newBuilder(taskKey)
    .set("category", "Personal")
    .set("done", false)
    .set("priority", 4)
    .set("description", "Learn Cloud Datastore")
    .build();

Node.js

Para obtener información sobre cómo instalar y usar la biblioteca cliente de Cloud Datastore, consulta las bibliotecas cliente de Cloud Datastore. Si quieres obtener más información, consulta la documentación de referencia de la API de Cloud Datastore para Node.js.

const task = {
  category: 'Personal',
  done: false,
  priority: 4,
  description: 'Learn Cloud Datastore',
};

PHP

Para obtener información sobre cómo instalar y usar la biblioteca cliente de Cloud Datastore, consulta las bibliotecas cliente de Cloud Datastore. Si quieres obtener más información, consulta la documentación de referencia de la API de Cloud Datastore para PHP.

$task = $datastore->entity('Task', [
    'category' => 'Personal',
    'done' => false,
    'priority' => 4,
    'description' => 'Learn Cloud Datastore'
]);

Python

Para obtener información sobre cómo instalar y usar la biblioteca cliente de Cloud Datastore, consulta las bibliotecas cliente de Cloud Datastore. Si quieres obtener más información, consulta la documentación de referencia de la API de Cloud Datastore para Python.

task = datastore.Entity(client.key('Task'))
task.update({
    'category': 'Personal',
    'done': False,
    'priority': 4,
    'description': 'Learn Cloud Datastore'
})

Ruby

Para obtener información sobre cómo instalar y usar la biblioteca cliente de Cloud Datastore, consulta las bibliotecas cliente de Cloud Datastore. Si quieres obtener más información, consulta la documentación de referencia de la API de Cloud Datastore para Ruby.

task = datastore.entity "Task" do |t|
  t["category"] = "Personal"
  t["done"] = false
  t["priority"] = 4
  t["description"] = "Learn Cloud Datastore"
end

Luego consulta sobre la categoría de entidad Task para las tareas aún no realizadas con prioridades mayores o iguales a 4, ordenadas en orden descendente según la prioridad:

C#

Para obtener información sobre cómo instalar y usar la biblioteca cliente de Cloud Datastore, consulta las bibliotecas cliente de Cloud Datastore. Si quieres obtener más información, consulta la documentación de referencia de la API de Cloud Datastore para C#.

Query query = new Query("Task")
{
    Filter = Filter.And(Filter.Equal("done", false),
        Filter.GreaterThanOrEqual("priority", 4)),
    Order = { { "priority", PropertyOrder.Types.Direction.Descending } }
};

Go

Para obtener información sobre cómo instalar y usar la biblioteca cliente de Cloud Datastore, consulta las bibliotecas cliente de Cloud Datastore. Si quieres obtener más información, consulta la documentación de referencia de la API de Cloud Datastore para Go.

query := datastore.NewQuery("Task").
	Filter("Done =", false).
	Filter("Priority >=", 4).
	Order("-Priority")

Java

Para obtener información sobre cómo instalar y usar la biblioteca cliente de Cloud Datastore, consulta las bibliotecas cliente de Cloud Datastore. Si quieres obtener más información, consulta la documentación de referencia de la API de Cloud Datastore para Java.

Query<Entity> query = Query.newEntityQueryBuilder()
    .setKind("Task")
    .setFilter(CompositeFilter.and(
        PropertyFilter.eq("done", false), PropertyFilter.ge("priority", 4)))
    .setOrderBy(OrderBy.desc("priority"))
    .build();

Node.js

Para obtener información sobre cómo instalar y usar la biblioteca cliente de Cloud Datastore, consulta las bibliotecas cliente de Cloud Datastore. Si quieres obtener más información, consulta la documentación de referencia de la API de Cloud Datastore para Node.js.

const query = datastore
  .createQuery('Task')
  .filter('done', '=', false)
  .filter('priority', '>=', 4)
  .order('priority', {
    descending: true,
  });

PHP

Para obtener información sobre cómo instalar y usar la biblioteca cliente de Cloud Datastore, consulta las bibliotecas cliente de Cloud Datastore. Si quieres obtener más información, consulta la documentación de referencia de la API de Cloud Datastore para PHP.

$query = $datastore->query()
    ->kind('Task')
    ->filter('done', '=', false)
    ->filter('priority', '>=', 4)
    ->order('priority', Query::ORDER_DESCENDING);

Python

Para obtener información sobre cómo instalar y usar la biblioteca cliente de Cloud Datastore, consulta las bibliotecas cliente de Cloud Datastore. Si quieres obtener más información, consulta la documentación de referencia de la API de Cloud Datastore para Python.

query = client.query(kind='Task')
query.add_filter('done', '=', False)
query.add_filter('priority', '>=', 4)
query.order = ['-priority']

Ruby

Para obtener información sobre cómo instalar y usar la biblioteca cliente de Cloud Datastore, consulta las bibliotecas cliente de Cloud Datastore. Si quieres obtener más información, consulta la documentación de referencia de la API de Cloud Datastore para Ruby.

query = datastore.query("Task")
                 .where("done", "=", false)
                 .where("priority", ">=", 4)
                 .order("priority", :desc)

Sin embargo, por utilizar una consulta de coherencia eventual (en lugar de una consulta principal) los resultados de la consulta pueden no contener la entidad nueva. De todas formas, casi todas las operaciones de escritura estarán disponibles para consultas de coherencia eventual apenas después de la confirmación. En el caso de muchas aplicaciones, bastará con una solución que proporcione resultados de una consulta de coherencia eventual en el contexto de los cambios propios del usuario actual para hacer que esas latencias disminuyan a un nivel completamente aceptable.

Para lograr coherencia sólida, una estrategia mejor es crear las entidades con una ruta principal. La ruta principal identifica a la entidad raíz común en la que se agrupan las entidades creadas. Este ejemplo utiliza una ruta de acceso principal de categoría TaskList con nombre default:

C#

Para obtener información sobre cómo instalar y usar la biblioteca cliente de Cloud Datastore, consulta las bibliotecas cliente de Cloud Datastore. Si quieres obtener más información, consulta la documentación de referencia de la API de Cloud Datastore para C#.

Key taskListKey = _db.CreateKeyFactory("TaskList").CreateKey(TestUtil.RandomName());
Key taskKey = new KeyFactory(taskListKey, "Task").CreateKey("sampleTask");
Entity task = new Entity()
{
    Key = taskKey,
    ["category"] = "Personal",
    ["done"] = false,
    ["priority"] = 4,
    ["description"] = "Learn Cloud Datastore"
};

Go

Para obtener información sobre cómo instalar y usar la biblioteca cliente de Cloud Datastore, consulta las bibliotecas cliente de Cloud Datastore. Si quieres obtener más información, consulta la documentación de referencia de la API de Cloud Datastore para Go.

parentKey := datastore.NameKey("TaskList", "default", nil)
key := datastore.IncompleteKey("Task", parentKey)

task := Task{
	Category:    "Personal",
	Done:        false,
	Priority:    4,
	Description: "Learn Cloud Datastore",
}

// A complete key is assigned to the entity when it is Put.
var err error
key, err = client.Put(ctx, key, &task)

Java

Para obtener información sobre cómo instalar y usar la biblioteca cliente de Cloud Datastore, consulta las bibliotecas cliente de Cloud Datastore. Si quieres obtener más información, consulta la documentación de referencia de la API de Cloud Datastore para Java.

Key taskKey = datastore.newKeyFactory()
    .addAncestors(PathElement.of("TaskList", "default"))
    .setKind("Task")
    .newKey("sampleTask");
Entity task = Entity.newBuilder(taskKey)
    .set("category", "Personal")
    .set("done", false)
    .set("priority", 4)
    .set("description", "Learn Cloud Datastore")
    .build();

Node.js

Para obtener información sobre cómo instalar y usar la biblioteca cliente de Cloud Datastore, consulta las bibliotecas cliente de Cloud Datastore. Si quieres obtener más información, consulta la documentación de referencia de la API de Cloud Datastore para Node.js.

const task = {
  key: taskKey,
  data: {
    category: 'Personal',
    done: false,
    priority: 4,
    description: 'Learn Cloud Datastore',
  },
};

PHP

Para obtener información sobre cómo instalar y usar la biblioteca cliente de Cloud Datastore, consulta las bibliotecas cliente de Cloud Datastore. Si quieres obtener más información, consulta la documentación de referencia de la API de Cloud Datastore para PHP.

$parentKey = $datastore->key('TaskList', 'default');
$key = $datastore->key('Task')->ancestorKey($parentKey);
$task = $datastore->entity(
    $key,
    [
        'Category' => 'Personal',
        'Done' => false,
        'Priority' => 4,
        'Description' => 'Learn Cloud Datastore'
    ]
);

Python

Para obtener información sobre cómo instalar y usar la biblioteca cliente de Cloud Datastore, consulta las bibliotecas cliente de Cloud Datastore. Si quieres obtener más información, consulta la documentación de referencia de la API de Cloud Datastore para Python.

key_with_parent = client.key(
    'TaskList', 'default', 'Task', 'sample_task')

task = datastore.Entity(key=key_with_parent)

task.update({
    'category': 'Personal',
    'done': False,
    'priority': 4,
    'description': 'Learn Cloud Datastore'
})

Ruby

Para obtener información sobre cómo instalar y usar la biblioteca cliente de Cloud Datastore, consulta las bibliotecas cliente de Cloud Datastore. Si quieres obtener más información, consulta la documentación de referencia de la API de Cloud Datastore para Ruby.

task_key = datastore.key [["TaskList", "default"], ["Task", "sampleTask"]]

task = datastore.entity task_key do |t|
  t["category"] = "Personal"
  t["done"] = false
  t["priority"] = 4
  t["description"] = "Learn Cloud Datastore"
end

Después, podrás realizar una consulta principal de coherencia sólida dentro del grupo de identidad identificado por la entidad raíz común.

C#

Para obtener información sobre cómo instalar y usar la biblioteca cliente de Cloud Datastore, consulta las bibliotecas cliente de Cloud Datastore. Si quieres obtener más información, consulta la documentación de referencia de la API de Cloud Datastore para C#.

Query query = new Query("Task")
{
    Filter = Filter.HasAncestor(_db.CreateKeyFactory("TaskList")
        .CreateKey(keyName))
};

Go

Para obtener información sobre cómo instalar y usar la biblioteca cliente de Cloud Datastore, consulta las bibliotecas cliente de Cloud Datastore. Si quieres obtener más información, consulta la documentación de referencia de la API de Cloud Datastore para Go.

ancestor := datastore.NameKey("TaskList", "default", nil)
query := datastore.NewQuery("Task").Ancestor(ancestor)

Java

Para obtener información sobre cómo instalar y usar la biblioteca cliente de Cloud Datastore, consulta las bibliotecas cliente de Cloud Datastore. Si quieres obtener más información, consulta la documentación de referencia de la API de Cloud Datastore para Java.

Query<Entity> query = Query.newEntityQueryBuilder()
    .setKind("Task")
    .setFilter(PropertyFilter.hasAncestor(
        datastore.newKeyFactory().setKind("TaskList").newKey("default")))
    .build();

Node.js

Para obtener información sobre cómo instalar y usar la biblioteca cliente de Cloud Datastore, consulta las bibliotecas cliente de Cloud Datastore. Si quieres obtener más información, consulta la documentación de referencia de la API de Cloud Datastore para Node.js.

const ancestorKey = datastore.key(['TaskList', 'default']);

const query = datastore.createQuery('Task').hasAncestor(ancestorKey);

PHP

Para obtener información sobre cómo instalar y usar la biblioteca cliente de Cloud Datastore, consulta las bibliotecas cliente de Cloud Datastore. Si quieres obtener más información, consulta la documentación de referencia de la API de Cloud Datastore para PHP.

$ancestorKey = $datastore->key('TaskList', 'default');
$query = $datastore->query()
    ->kind('Task')
    ->hasAncestor($ancestorKey);

Python

Para obtener información sobre cómo instalar y usar la biblioteca cliente de Cloud Datastore, consulta las bibliotecas cliente de Cloud Datastore. Si quieres obtener más información, consulta la documentación de referencia de la API de Cloud Datastore para Python.

# Query filters are omitted in this example as any ancestor queries with a
# non-key filter require a composite index.
ancestor = client.key('TaskList', 'default')
query = client.query(kind='Task', ancestor=ancestor)

Ruby

Para obtener información sobre cómo instalar y usar la biblioteca cliente de Cloud Datastore, consulta las bibliotecas cliente de Cloud Datastore. Si quieres obtener más información, consulta la documentación de referencia de la API de Cloud Datastore para Ruby.

ancestor_key = datastore.key "TaskList", "default"

query = datastore.query("Task")
                 .ancestor(ancestor_key)

Esta estrategia logra una coherencia sólida por medio de la escritura en un solo grupo de entidad por lista de tarea, pero también limita los cambios a las listas de tareas a no más de 1 escritura por segundo (el límite admitido para los grupos de entidad). Si tu aplicación es propensa utilizar operaciones de escritura más pesadas, considera utilizar otros métodos. Por ejemplo, si tu aplicación es un libro de visitas que permite a los usuarios publicar mensajes en una panel de mensajes público, podrías poner las publicaciones recientes en Memcache con un vencimiento y mostrar una combinación de publicaciones recientes desde Memcache y Datastore, o podrías almacenarlos en la caché de una cookie, poner un estado en la URL o algo completamente distinto. El objetivo es encontrar una solución de almacenamiento en caché que brinde datos al usuario actual durante el momento en el que el usuario publica en tu aplicación. Recuerda, si haces un lookup, una consulta principal (si la política de lectura no está configurada en coherencia eventual), o cualquier operación dentro de una transacción, siempre verás los datos escritos más recientes.

Para ver más ejemplos sobre cómo utilizar las transacciones, visita este vínculo.

Limitaciones de grupos de entidades en las transacciones

La organización de los datos en grupos de entidad puede limitar qué transacciones se pueden realizar:

  • Todos los datos a los que accede una transacción deben estar contenidos en 25 grupos de entidades como máximo.
  • Si quieres usar consultas dentro de una transacción, los datos deben estar organizados en grupos de entidad de manera que puedas especificar los filtros de entidades principales que coincidan con los datos correctos.
  • Existe un límite de capacidad de rendimiento de escritura de alrededor de una transacción por segundo para un solo grupo de entidad. Esta limitación existe porque Datastore realiza una replicación síncrona sin instancia maestra de cada grupo de entidad sobre una gran área geográfica para brindar alta fiabilidad y tolerancia a errores.

Para obtener más información sobre cómo se actualizan las entidades y los índices, consulta el artículo Aislamiento de transacción.

¿Te ha resultado útil esta página? Enviar comentarios:

Enviar comentarios sobre...

Documentación de Cloud Datastore