Como estruturar dados para consistência forte

O Cloud Datastore fornece alta disponibilidade, escalonabilidade e durabilidade ao distribuir dados em muitas máquinas e usar replicação síncrona sem mestre em uma ampla área geográfica. A desvantagem deste modelo é que a capacidade de gravação de um grupo de entidades único é limitada a aproximadamente um commit por segundo. Há também limitações em consultas ou transações que abrangem vários grupos de entidades. Esta página descreve essas limitações com mais detalhes e discute as práticas recomendadas para estruturar os dados de modo a oferecer suporte a uma consistência forte e satisfazer os requisitos de capacidade de gravação do aplicativo.

Níveis de consistência

As consultas do Datastore podem gerar resultados em dois níveis de consistência:

  • Consultas com consistência forte garantem os resultados mais atualizados, mas pode ter uma demora na conclusão e falta de compatibilidade em determinados casos.
  • Consultas com consistência eventual normalmente são mais rápidas, mas podem retornar resultados desatualizados.

Em uma consulta com consistência eventual, os índices usados para coletar os resultados também são acessados com esse tipo de consistência. Consequentemente, essas consultas podem, algumas vezes, retornar entidades que não correspondem mais aos critérios de consulta, além de omitir algumas que correspondem. As consultas com consistência forte são consistentes de forma transacional, o que significa que os resultados são baseados em um instantâneo único e consistente dos dados.

Garantias de consistência

As consultas retornam resultados com diferentes níveis de garantia de consistência, dependendo da natureza da consulta:

  • Consultas de ancestrais, executadas em um grupo de entidades, têm consistência forte, por padrão. No entanto, para que tenham consistência eventual, configure a política de leitura do Datastore abordada abaixo.
  • As consultas globais, que não são executadas em um grupo de entidades, sempre têm consistência eventual.

Em muitos aplicativos, é aceitável usar a consistência eventual para ter uma visualização ampla dos dados não relacionados e depois usar a consistência forte ao visualizar ou editar um único conjunto de dados altamente relacionados. Por exemplo, uma consulta global abrangendo vários grupos de entidades, que algumas vezes pode retornar dados ligeiramente desatualizados, seguida de uma consulta de ancestral ou uma pesquisa de uma única entidade. Em tais aplicativos, é recomendado colocar os dados altamente relacionados em grupos de entidades. Um número maior desses grupos aumenta a capacidade, enquanto um número menor aumenta o volume de entidades que podem ser lidas em uma única consulta de ancestral. O aplicativo precisa levar isso em conta para determinar o equilíbrio certo entre capacidade e consistência.

Política de leitura do Datastore

Para aumentar o desempenho, você pode definir a política de leitura de uma consulta para que os resultados tenham consistência eventual. A API Datastore também permite definir explicitamente uma política de consistência forte, mas essa configuração não tem efeito prático, já que as consultas globais sempre têm consistência eventual, independentemente da política.

Você pode ativar leituras com consistência eventual nas opções de leitura do objeto de consulta:

C#

Para aprender a instalar e usar a biblioteca de cliente para o Cloud Datastore, consulte as bibliotecas de cliente do Cloud Datastore. Para mais informações, consulte a documentação de referência da API do 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 aprender a instalar e usar a biblioteca de cliente para o Cloud Datastore, consulte as bibliotecas de cliente do Cloud Datastore. Para mais informações, consulte a documentação de referência da API do Cloud Datastore para Go.

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

Java

Para aprender a instalar e usar a biblioteca de cliente para o Cloud Datastore, consulte as bibliotecas de cliente do Cloud Datastore. Para mais informações, consulte a documentação de referência da API do 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 aprender a instalar e usar a biblioteca de cliente para o Cloud Datastore, consulte as bibliotecas de cliente do Cloud Datastore. Para mais informações, consulte a documentação de referência da API do Cloud Storage para Node.js.

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

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

PHP

Para aprender a instalar e usar a biblioteca de cliente para o Cloud Datastore, consulte as bibliotecas de cliente do Cloud Datastore. Para mais informações, consulte a documentação de referência da API do Cloud Datastore para PHP.

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

Python

Para aprender a instalar e usar a biblioteca de cliente para o Cloud Datastore, consulte as bibliotecas de cliente do Cloud Datastore. Para mais informações, consulte a documentação de referência da API do Cloud Datastore para Python.

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

Ruby

Para aprender a instalar e usar a biblioteca de cliente para o Cloud Datastore, consulte as bibliotecas de cliente do Cloud Datastore. Para mais informações, consulte a documentação de referência da API do Cloud Datastore para Ruby.

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

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

tasks = datastore.run query, consistency: :eventual

Considerações sobre transações e consistência

As confirmações do Datastore são transacionais quando ocorrem no contexto de uma transação, e o conjunto de mutações dessa transação é completamente aplicado ou não aplicado. Elas são não transacionais quando não há possibilidade de aplicar o conjunto de mutações como tudo ou nada.

Uma única transação pode incluir qualquer número de mudanças de criação, atualização ou exclusão. Para manter a consistência dos dados, a transação assegura que todas as mudanças que ela contém sejam aplicadas ao Datastore como uma unidade ou, se alguma das mudanças apresentar falhas, que nenhuma delas seja aplicada. Além disso, todas as leituras com consistência forte (consultas de ancestral ou operações de lookup) executadas na mesma transação dependem de um único instantâneo consistente dos dados. As consultas com consistência forte precisam especificar um filtro de ancestral. As consultas que participam de uma transação sempre têm consistência forte. As transações podem envolver no máximo 25 grupos de entidades. As leituras com consistência eventual não têm essas limitações e são adequadas em muitos casos. O uso de leituras com consistência eventual pode permitir que você distribua os dados entre um número maior de grupos de entidades, permitindo que você obtenha maior capacidade de gravação executando commits paralelamente nos diferentes grupos de entidades. No entanto, você precisa compreender as características das leituras com consistência eventual para determinar se elas são adequadas para o seu aplicativo:

  • Os resultados dessas leituras podem não refletir as transações mais recentes. Isso pode ocorrer porque essas leituras não garantem que a réplica na qual elas estão sendo executadas está atualizada. Em vez disso, elas usam os dados disponíveis na réplica no momento da execução da consulta.
  • Uma transação com commit que abrange vários grupos de entidades pode parecer ter sido aplicada a algumas entidades, e não a outras. Observe, no entanto, que uma transação nunca parecerá ter sido parcialmente aplicada em uma única entidade.
  • Os resultados da consulta podem incluir entidades que não deveriam ter sido incluídas de acordo com os critérios de filtro e podem excluir entidades que deveriam ter sido incluídas. Isso pode ocorrer porque a versão do instantâneo usada para ler índices pode diferir da versão do instantâneo usada para ler a entidade.

Como estruturar os dados para consistência

Para compreender como estruturar os dados para uma consistência forte, compare duas abordagens diferentes para um simples aplicativo de lista de tarefas. A primeira abordagem cria cada entidade no seu próprio grupo de entidades novo (isto é, cada entidade é uma entidade raiz):

C#

Para aprender a instalar e usar a biblioteca de cliente para o Cloud Datastore, consulte as bibliotecas de cliente do Cloud Datastore. Para mais informações, consulte a documentação de referência da API do 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 aprender a instalar e usar a biblioteca de cliente para o Cloud Datastore, consulte as bibliotecas de cliente do Cloud Datastore. Para mais informações, consulte a documentação de referência da API do 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 aprender a instalar e usar a biblioteca de cliente para o Cloud Datastore, consulte as bibliotecas de cliente do Cloud Datastore. Para mais informações, consulte a documentação de referência da API do Cloud Datastore para Java.

Entity task = Entity.newBuilder(taskKey)
    .set("category", "Personal")
    .set("done", false)
    .set("priority", 4)
    .set("description", "Learn Cloud Datastore")
    .build();

Node.js

Para aprender a instalar e usar a biblioteca de cliente para o Cloud Datastore, consulte as bibliotecas de cliente do Cloud Datastore. Para mais informações, consulte a documentação de referência da API do Cloud Storage para Node.js.

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

PHP

Para aprender a instalar e usar a biblioteca de cliente para o Cloud Datastore, consulte as bibliotecas de cliente do Cloud Datastore. Para mais informações, consulte a documentação de referência da API do Cloud Datastore para PHP.

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

Python

Para aprender a instalar e usar a biblioteca de cliente para o Cloud Datastore, consulte as bibliotecas de cliente do Cloud Datastore. Para mais informações, consulte a documentação de referência da API do Cloud Datastore para Python.

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

Ruby

Para aprender a instalar e usar a biblioteca de cliente para o Cloud Datastore, consulte as bibliotecas de cliente do Cloud Datastore. Para mais informações, consulte a documentação de referência da API do 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

Essa abordagem consulta no tipo de entidade Task para as tarefas que ainda não foram realizadas com prioridades maiores ou iguais a 4, classificadas em ordem decrescente por prioridade:

C#

Para aprender a instalar e usar a biblioteca de cliente para o Cloud Datastore, consulte as bibliotecas de cliente do Cloud Datastore. Para mais informações, consulte a documentação de referência da API do 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 aprender a instalar e usar a biblioteca de cliente para o Cloud Datastore, consulte as bibliotecas de cliente do Cloud Datastore. Para mais informações, consulte a documentação de referência da API do Cloud Datastore para Go.

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

Java

Para aprender a instalar e usar a biblioteca de cliente para o Cloud Datastore, consulte as bibliotecas de cliente do Cloud Datastore. Para mais informações, consulte a documentação de referência da API do 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 aprender a instalar e usar a biblioteca de cliente para o Cloud Datastore, consulte as bibliotecas de cliente do Cloud Datastore. Para mais informações, consulte a documentação de referência da API do Cloud Storage para Node.js.

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

PHP

Para aprender a instalar e usar a biblioteca de cliente para o Cloud Datastore, consulte as bibliotecas de cliente do Cloud Datastore. Para mais informações, consulte a documentação de referência da API do Cloud Datastore para PHP.

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

Python

Para aprender a instalar e usar a biblioteca de cliente para o Cloud Datastore, consulte as bibliotecas de cliente do Cloud Datastore. Para mais informações, consulte a documentação de referência da API do Cloud Datastore para Python.

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

Ruby

Para aprender a instalar e usar a biblioteca de cliente para o Cloud Datastore, consulte as bibliotecas de cliente do Cloud Datastore. Para mais informações, consulte a documentação de referência da API do Cloud Datastore para Ruby.

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

No entanto, como estamos usando uma consulta de consistência eventual em vez de uma consulta de ancestral, os resultados talvez não contenham a nova entidade. Contudo, quase todas as gravações estarão disponíveis para consultas com consistência eventual logo após o commit. Para muitos aplicativos, uma solução que fornece os resultados de uma consulta com consistência eventual no contexto das próprias alterações do usuário atual normalmente será suficiente para tornar essas latências completamente aceitáveis.

Para alcançar uma consistência forte, a melhor abordagem seria criar as entidades com um caminho de ancestral. Esse caminho identifica a entidade raiz comum na qual as entidades criadas são agrupadas. Este exemplo usa um caminho de ancestral do tipo TaskList nomeado como default:

C#

Para aprender a instalar e usar a biblioteca de cliente para o Cloud Datastore, consulte as bibliotecas de cliente do Cloud Datastore. Para mais informações, consulte a documentação de referência da API do 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 aprender a instalar e usar a biblioteca de cliente para o Cloud Datastore, consulte as bibliotecas de cliente do Cloud Datastore. Para mais informações, consulte a documentação de referência da API do 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 aprender a instalar e usar a biblioteca de cliente para o Cloud Datastore, consulte as bibliotecas de cliente do Cloud Datastore. Para mais informações, consulte a documentação de referência da API do 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 aprender a instalar e usar a biblioteca de cliente para o Cloud Datastore, consulte as bibliotecas de cliente do Cloud Datastore. Para mais informações, consulte a documentação de referência da API do Cloud Storage para Node.js.

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

PHP

Para aprender a instalar e usar a biblioteca de cliente para o Cloud Datastore, consulte as bibliotecas de cliente do Cloud Datastore. Para mais informações, consulte a documentação de referência da API do 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 aprender a instalar e usar a biblioteca de cliente para o Cloud Datastore, consulte as bibliotecas de cliente do Cloud Datastore. Para mais informações, consulte a documentação de referência da API do 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 aprender a instalar e usar a biblioteca de cliente para o Cloud Datastore, consulte as bibliotecas de cliente do Cloud Datastore. Para mais informações, consulte a documentação de referência da API do 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

Você conseguirá executar uma consulta de ancestral de consistência forte dentro do grupo de entidades identificado pela entidade raiz comum:

C#

Para aprender a instalar e usar a biblioteca de cliente para o Cloud Datastore, consulte as bibliotecas de cliente do Cloud Datastore. Para mais informações, consulte a documentação de referência da API do Cloud Datastore para C#.

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

Go

Para aprender a instalar e usar a biblioteca de cliente para o Cloud Datastore, consulte as bibliotecas de cliente do Cloud Datastore. Para mais informações, consulte a documentação de referência da API do Cloud Datastore para Go.

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

Java

Para aprender a instalar e usar a biblioteca de cliente para o Cloud Datastore, consulte as bibliotecas de cliente do Cloud Datastore. Para mais informações, consulte a documentação de referência da API do Cloud Datastore para Java.

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

Node.js

Para aprender a instalar e usar a biblioteca de cliente para o Cloud Datastore, consulte as bibliotecas de cliente do Cloud Datastore. Para mais informações, consulte a documentação de referência da API do Cloud Storage para Node.js.

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

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

PHP

Para aprender a instalar e usar a biblioteca de cliente para o Cloud Datastore, consulte as bibliotecas de cliente do Cloud Datastore. Para mais informações, consulte a documentação de referência da API do Cloud Datastore para PHP.

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

Python

Para aprender a instalar e usar a biblioteca de cliente para o Cloud Datastore, consulte as bibliotecas de cliente do Cloud Datastore. Para mais informações, consulte a documentação de referência da API do 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 aprender a instalar e usar a biblioteca de cliente para o Cloud Datastore, consulte as bibliotecas de cliente do Cloud Datastore. Para mais informações, consulte a documentação de referência da API do Cloud Datastore para Ruby.

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

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

Essa abordagem alcança consistência forte ao gravar em um único grupo de entidades por lista de tarefas. No entanto, ela limita as alterações na lista de tarefas a até 1 gravação por segundo, que é o limite permitido para grupos de entidades. É provável que o aplicativo encontre um uso mais intensivo de gravação. Nesse caso, analise o uso de outros meios. Por exemplo, se o aplicativo for um livro de visitas que permite que os usuários postem mensagens em um quadro de mensagens público, você poderá colocar as postagens recentes no memcache com uma expiração e exibir uma combinação de postagens recentes do memcache e do Datastore ou poderá armazená-las em cache em um cookie, colocar algum estado no URL ou outra opção. O objetivo é encontrar uma solução de armazenamento em cache que forneça os dados para o usuário atual durante o período de tempo em que o usuário está postando no aplicativo. Lembre-se de que você sempre verá os dados gravados mais recentes ao realizar uma operação lookup, uma consulta de ancestral ou qualquer operação em uma transação. No caso da consulta de ancestral, supõe-se que a política de leitura não esteja definida para consistência eventual.

Para ver mais exemplos de como usar transações, acesse este link.

Limitações de grupos de entidades em transações

A organização dos dados em grupos de entidades pode limitar quais transações podem ser realizadas:

  • Todos os dados acessados por uma transação devem estar contidos em, no máximo, 25 grupos de entidades.
  • Se você quiser usar consultas em uma transação, os dados precisam estar organizados em grupos de entidades para que você possa especificar filtros de ancestral que corresponderão aos dados corretos.
  • Há um limite de capacidade de gravação de aproximadamente uma transação por segundo para um único grupo de entidades. Essa limitação existe porque, para fornecer alta confiabilidade e tolerância a falhas, o Datastore executa a replicação síncrona sem mestre de cada grupo de entidades em uma ampla área geográfica.

Para mais informações sobre como as entidades e os índices são atualizados, leia o artigo Isolamento da transação.

Esta página foi útil? Conte sua opinião sobre:

Enviar comentários sobre…

Documentação do Cloud Datastore