Transações do Cloud Datastore

Uma transação é um conjunto de operações do Cloud Datastore em uma ou mais entidades, em até 25 grupos de entidades. Todas as transações são atômicas, o que significa que elas nunca são aplicadas parcialmente. Ou todas as operações na transação são aplicadas ou nenhuma delas é aplicada.

Como usar transações

As transações têm uma duração máxima de 60 segundos, com um tempo de expiração por inatividade de 10 segundos após 30 segundos.

Uma operação poderá falhar quando:

  • muitas modificações simultâneas forem tentadas no mesmo grupo de entidades;
  • a transação exceder um limite de recursos;
  • o Cloud Datastore encontrar um erro interno.

Em todos esses casos, a Cloud Datastore API retornará um erro.

As transações são um recurso opcional do Cloud Datastore; você não é obrigado a usar as transações para executar operações do Cloud Datastore.

Um aplicativo pode executar um conjunto de instruções e operações do Cloud Datastore em uma única transação. Dessa forma, se qualquer instrução ou operação gerar uma exceção, nenhuma das operações do Cloud Datastore no conjunto será aplicada. O aplicativo define as ações a serem executadas na transação.

O snippet a seguir mostra como executar uma transação usando a Cloud Datastore API. Ele transfere dinheiro de uma conta para outra.

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#.

private void TransferFunds(Key fromKey, Key toKey, long amount)
{
    using (var transaction = _db.BeginTransaction())
    {
        var entities = transaction.Lookup(fromKey, toKey);
        entities[0]["balance"].IntegerValue -= amount;
        entities[1]["balance"].IntegerValue += amount;
        transaction.Update(entities);
        transaction.Commit();
    }
}

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 BankAccount struct {
	Balance int
}

const amount = 50
keys := []*datastore.Key{to, from}
tx, err := client.NewTransaction(ctx)
if err != nil {
	log.Fatalf("client.NewTransaction: %v", err)
}
accs := make([]BankAccount, 2)
if err := tx.GetMulti(keys, accs); err != nil {
	tx.Rollback()
	log.Fatalf("tx.GetMulti: %v", err)
}
accs[0].Balance += amount
accs[1].Balance -= amount
if _, err := tx.PutMulti(keys, accs); err != nil {
	tx.Rollback()
	log.Fatalf("tx.PutMulti: %v", err)
}
if _, err = tx.Commit(); err != nil {
	log.Fatalf("tx.Commit: %v", err)
}

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.

void transferFunds(Key fromKey, Key toKey, long amount) {
  Transaction txn = datastore.newTransaction();
  try {
    List<Entity> entities = txn.fetch(fromKey, toKey);
    Entity from = entities.get(0);
    Entity updatedFrom =
        Entity.newBuilder(from).set("balance", from.getLong("balance") - amount).build();
    Entity to = entities.get(1);
    Entity updatedTo = Entity.newBuilder(to).set("balance", to.getLong("balance") + amount)
        .build();
    txn.put(updatedFrom, updatedTo);
    txn.commit();
  } finally {
    if (txn.isActive()) {
      txn.rollback();
    }
  }
}

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 Datastore para Node.js.

async function transferFunds(fromKey, toKey, amount) {
  const transaction = datastore.transaction();
  await transaction.run();
  const results = await Promise.all([
    transaction.get(fromKey),
    transaction.get(toKey),
  ]);
  const accounts = results.map(result => result[0]);

  accounts[0].balance -= amount;
  accounts[1].balance += amount;

  transaction.save([
    {
      key: fromKey,
      data: accounts[0],
    },
    {
      key: toKey,
      data: accounts[1],
    },
  ]);

  return transaction.commit();
}

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.

/**
 * Update two entities in a transaction.
 *
 * @param DatastoreClient $datastore
 * @param Key $fromKey
 * @param Key $toKey
 * @param $amount
 */
function transfer_funds(
    DatastoreClient $datastore,
    Key $fromKey,
    Key $toKey,
    $amount
) {
    $transaction = $datastore->transaction();
    // The option 'sort' is important here, otherwise the order of the result
    // might be different from the order of the keys.
    $result = $transaction->lookupBatch([$fromKey, $toKey], ['sort' => true]);
    if (count($result['found']) != 2) {
        $transaction->rollback();
    }
    $fromAccount = $result['found'][0];
    $toAccount = $result['found'][1];
    $fromAccount['balance'] -= $amount;
    $toAccount['balance'] += $amount;
    $transaction->updateBatch([$fromAccount, $toAccount]);
    $transaction->commit();
}

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.

def transfer_funds(client, from_key, to_key, amount):
    with client.transaction():
        from_account = client.get(from_key)
        to_account = client.get(to_key)

        from_account['balance'] -= amount
        to_account['balance'] += amount

        client.put_multi([from_account, to_account])

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.

def transfer_funds from_key, to_key, amount
  datastore.transaction do |tx|
    from = tx.find from_key
    from["balance"] -= amount
    to = tx.find to_key
    to["balance"] += amount
    tx.save from, to
  end
end

Para manter nossos exemplos mais sucintos, às vezes omitimos o rollback se a transação falhar. Em código de produção, é importante garantir que cada transação seja executada explicitamente ou revertida.

O que pode ser feito em uma transação

Todas as operações do Cloud Datastore em uma transação podem operar, no máximo, em vinte e cinco grupos de entidades. Isso inclui fazer consultas por entidades pelo ancestral, recuperar entidades pela chave, atualizar as entidades e excluir as entidades.

Quando duas ou mais transações tentam, simultaneamente, modificar entidades em um ou mais grupos de entidades comuns, somente a primeira transação a executar commit das suas alterações pode ser bem-sucedida. Todas as outras falharão no commit. Devido a esse design, o uso de grupos de entidades limita o número de gravações simultâneas que você pode fazer em qualquer entidade nos grupos. Quando uma transação é iniciada, o Cloud Datastore usa controle de simultaneidade otimista verificando a hora da última atualização dos grupos de entidade usados na transação. Ao executar o commit de uma transação nos grupos de entidades, o Cloud Datastore verifica novamente o horário da última atualização dos grupos de entidades usados nessa transação. Se ela tiver sido alterada desde a verificação inicial, um erro será retornado. Para conferir uma explicação sobre grupos de entidades, veja Caminhos do ancestral.

Isolamento e consistência

Fora das transações, o nível de isolamento do Cloud Datastore é mais próximo da confirmação para leitura. Dentro das transações, o isolamento serializável é imposto. Isso significa que outra transação não pode modificar simultaneamente os dados lidos ou modificados por essa transação. Para mais informações sobre níveis de isolamento, leia a wiki sobre isolamento serializável e o artigo sobre isolamento de transação.

Em uma transação, todas as leituras refletem o estado atual e consistente do Cloud Datastore no momento em que a transação foi iniciada. As consultas e pesquisas em uma transação exibem um instantâneo único e consistente do Cloud Datastore desde o início da transação. Nos grupos de entidades da transação, as linhas de índice e entidades são totalmente atualizadas para que as consultas retornem o conjunto completo e correto de resultados da entidade. Isso evita os falsos positivos ou falsos negativos, descritos em Isolamento da transação, que podem ocorrer em consultas fora das transações.

A visualização desse instantâneo consistente também se estende a leituras após gravações dentro de transações. Ao contrário da maioria dos bancos de dados, as consultas e retornos dentro de uma transação do Cloud Datastore não exibem os resultados das gravações anteriores dentro dessa transação. Especificamente, se uma entidade é modificada ou excluída em uma transação, uma consulta ou pesquisa retorna a versão original da entidade no início da transação, ou nada, se a entidade não existia.

Usos das transações

Um dos usos das transações é a atualização de uma entidade com um novo valor de propriedade relativo ao seu valor atual. O exemplo transferFunds acima faz isso para duas entidades, retirando dinheiro de uma conta e transferindo para outra. A Cloud Datastore API não repete automaticamente as transações. No entanto, você pode adicionar sua própria lógica para repeti-las, por exemplo, para lidar com conflitos quando outra solicitação atualizar a mesma entidade ao mesmo tempo.

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#.

/// <summary>
/// Retry the action when a Grpc.Core.RpcException is thrown.
/// </summary>
private T RetryRpc<T>(Func<T> action)
{
    List<Grpc.Core.RpcException> exceptions = null;
    var delayMs = _retryDelayMs;
    for (int tryCount = 0; tryCount < _retryCount; ++tryCount)
    {
        try
        {
            return action();
        }
        catch (Grpc.Core.RpcException e)
        {
            if (exceptions == null)
                exceptions = new List<Grpc.Core.RpcException>();
            exceptions.Add(e);
        }
        System.Threading.Thread.Sleep(delayMs);
        delayMs *= 2;  // Exponential back-off.
    }
    throw new AggregateException(exceptions);
}

private void RetryRpc(Action action)
{
    RetryRpc(() => { action(); return 0; });
}

[Fact]
public void TestTransactionalRetry()
{
    int tryCount = 0;
    var keys = UpsertBalances();
    RetryRpc(() =>
    {
        using (var transaction = _db.BeginTransaction())
        {
            TransferFunds(keys[0], keys[1], 10, transaction);
            // Insert a conflicting transaction on the first try.
            if (tryCount++ == 0)
                TransferFunds(keys[1], keys[0], 5);
            transaction.Commit();
        }
    });
    Assert.Equal(2, tryCount);
}

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 BankAccount struct {
	Balance int
}

const amount = 50
_, err := client.RunInTransaction(ctx, func(tx *datastore.Transaction) error {
	keys := []*datastore.Key{to, from}
	accs := make([]BankAccount, 2)
	if err := tx.GetMulti(keys, accs); err != nil {
		return err
	}
	accs[0].Balance += amount
	accs[1].Balance -= amount
	_, err := tx.PutMulti(keys, accs)
	return err
})

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.

int retries = 5;
while (true) {
  try {
    transferFunds(fromKey, toKey, 10);
    break;
  } catch (DatastoreException e) {
    if (retries == 0) {
      throw e;
    }
    --retries;
  }
}
// Retry handling can also be configured and automatically applied using google-cloud-java.

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 Datastore para Node.js.

async function transferFundsWithRetry() {
  const maxTries = 5;

  async function tryRequest(currentAttempt, delay) {
    try {
      await transferFunds(fromKey, toKey, 10);
    } catch (err) {
      if (currentAttempt <= maxTries) {
        // Use exponential backoff
        setTimeout(async () => {
          await tryRequest(currentAttempt + 1, delay * 2);
        }, delay);
      }
      throw err;
    }
  }

  await tryRequest(1, 100);
}

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.

$retries = 5;
for ($i = 0; $i < $retries; $i++) {
    try {
        transfer_funds($datastore, $fromKey, $toKey, 10);
    } catch (Google\Cloud\Exception\ConflictException $e) {
        // if $i >= $retries, the failure is final
        continue;
    }
    // Succeeded!
    break;
}

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.

for _ in range(5):
    try:
        transfer_funds(client, account1.key, account2.key, 50)
        break
    except google.cloud.exceptions.Conflict:
        continue
else:
    print('Transaction failed.')

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.

(1..5).each do |i|
  begin
    return transfer_funds from_key, to_key, amount
  rescue Google::Cloud::Error => e
    raise e if i == 5
  end
end

Isso requer uma transação porque o valor de balance em uma entidade pode ser atualizado por outro usuário depois que esse código buscar o objeto, mas antes que ele salve o objeto modificado. Sem uma transação, a solicitação do usuário usa o valor de balance anterior à atualização do outro usuário e, ao salvar, o valor atualizado é substituído. Com uma transação, o aplicativo é informado sobre a atualização.

Outro uso comum das transações é buscar uma entidade com uma chave nomeada, ou criá-la, caso ela ainda não exista (este exemplo é baseado no exemplo da TaskList de como criar uma entidade):

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;
using (var transaction = _db.BeginTransaction())
{
    task = transaction.Lookup(_sampleTask.Key);
    if (task == null)
    {
        transaction.Insert(_sampleTask);
        transaction.Commit();
    }
}

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.

_, err := client.RunInTransaction(ctx, func(tx *datastore.Transaction) error {
	var task Task
	if err := tx.Get(key, &task); err != datastore.ErrNoSuchEntity {
		return err
	}
	_, err := tx.Put(key, &Task{
		Category:    "Personal",
		Done:        false,
		Priority:    4,
		Description: "Learn Cloud Datastore",
	})
	return err
})

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;
Transaction txn = datastore.newTransaction();
try {
  task = txn.get(taskKey);
  if (task == null) {
    task = Entity.newBuilder(taskKey).build();
    txn.put(task);
    txn.commit();
  }
} finally {
  if (txn.isActive()) {
    txn.rollback();
  }
}

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 Datastore para Node.js.

async function getOrCreate(taskKey, taskData) {
  const taskEntity = {
    key: taskKey,
    data: taskData,
  };
  const transaction = datastore.transaction();

  try {
    await transaction.run();
    const [task] = await transaction.get(taskKey);
    if (task) {
      // The task entity already exists.
      transaction.rollback();
    } else {
      // Create the task entity.
      transaction.save(taskEntity);
      transaction.commit();
    }
    return taskEntity;
  } catch (err) {
    transaction.rollback();
  }
}

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.

$transaction = $datastore->transaction();
$existed = $transaction->lookup($task->key());
if ($existed === null) {
    $transaction->insert($task);
    $transaction->commit();
}

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.

with client.transaction():
    key = client.key('Task', datetime.datetime.utcnow().isoformat())

    task = client.get(key)

    if not task:
        task = datastore.Entity(key)
        task.update({
            'description': 'Example task'
        })
        client.put(task)

    return task

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 = nil
datastore.transaction do |tx|
  task = tx.find task_key
  if task.nil?
    task = datastore.entity task_key do |t|
      t["category"] = "Personal"
      t["done"] = false
      t["priority"] = 4
      t["description"] = "Learn Cloud Datastore"
    end
    tx.save task
  end
end

Como antes, uma transação é necessária para lidar com uma situação em que outro usuário tenta criar ou atualizar uma entidade com o mesmo código de string. Sem uma transação, se a entidade não existir e dois usuários tentarem criá-la, a segunda substituirá a primeira sem que se saiba que isso aconteceu.

Quando uma transação falha, é possível fazer com que o aplicativo repita a transação até ter êxito, ou é possível deixar que os usuários lidem com o erro propagando-o até o nível de interface do usuário do aplicativo. Você não precisa criar um ciclo de repetições em torno de cada transação.

Finalmente, você pode usar uma transação para ler um instantâneo consistente do Cloud Datastore. Isso pode ser útil quando várias leituras forem necessárias para renderizar uma página ou para exportar dados que precisem ser consistentes. Esses tipos de transações são frequentemente chamados de transações somente leitura, uma vez que não realizam gravações. As transações somente leitura de um único grupo nunca falham devido a modificações simultâneas, portanto, você não precisa implementar repetições após falha. No entanto, transações de vários grupos de entidades podem falhar devido a modificações simultâneas, havendo necessidade de repetições.

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 taskList;
IReadOnlyList<Entity> tasks;
using (var transaction = _db.BeginTransaction(TransactionOptions.CreateReadOnly()))
{
    taskList = transaction.Lookup(taskListKey);
    var query = new Query("Task")
    {
        Filter = Filter.HasAncestor(taskListKey)
    };
    tasks = transaction.RunQuery(query).Entities;
    transaction.Commit();
}

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.

tx, err := client.NewTransaction(ctx, datastore.ReadOnly)
if err != nil {
	log.Fatalf("client.NewTransaction: %v", err)
}
defer tx.Rollback() // Transaction only used for read.

ancestor := datastore.NameKey("TaskList", "default", nil)
query := datastore.NewQuery("Task").Ancestor(ancestor).Transaction(tx)
var tasks []Task
_, err = client.GetAll(ctx, query, &tasks)

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 taskList;
QueryResults<Entity> tasks;
Transaction txn = datastore.newTransaction(
    TransactionOptions.newBuilder()
        .setReadOnly(ReadOnly.newBuilder().build())
        .build()
);
try {
  taskList = txn.get(taskListKey);
  Query<Entity> query = Query.newEntityQueryBuilder()
      .setKind("Task")
      .setFilter(PropertyFilter.hasAncestor(taskListKey))
      .build();
  tasks = txn.run(query);
  txn.commit();
} finally {
  if (txn.isActive()) {
    txn.rollback();
  }
}

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 Datastore para Node.js.

async function getTaskListEntities() {
  const transaction = datastore.transaction({readOnly: true});
  try {
    const taskListKey = datastore.key(['TaskList', 'default']);

    await transaction.run();
    const [taskList] = await datastore.get(taskListKey);
    const query = datastore.createQuery('Task').hasAncestor(taskListKey);
    const [taskListEntities] = await datastore.runQuery(query);
    await transaction.commit();
    return [taskList, taskListEntities];
  } catch (err) {
    transaction.rollback();
  }
}

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.

$transaction = $datastore->readOnlyTransaction();
$taskListKey = $datastore->key('TaskList', 'default');
$query = $datastore->query()
    ->kind('Task')
    ->hasAncestor($taskListKey);
$result = $transaction->runQuery($query);
$taskListEntities = [];
/* @var Entity $task */
foreach ($result as $task) {
    $taskListEntities[] = $task;
}
$transaction->commit();

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.

with client.transaction(read_only=True):
    task_list_key = client.key('TaskList', 'default')

    task_list = client.get(task_list_key)

    query = client.query(kind='Task', ancestor=task_list_key)
    tasks_in_list = list(query.fetch())

    return task_list, tasks_in_list

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_list_key = datastore.key "TaskList", "default"
datastore.read_only_transaction do |tx|
  task_list = tx.find task_list_key
  query = datastore.query("Task").ancestor(task_list)
  tasks_in_list = tx.run query
end

Transações e grupos de entidades

Um grupo de entidades é um conjunto de entidades conectadas através de ancestral a um elemento raiz comum. 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 em um único grupo de entidades. Essa limitação existe porque o Cloud Datastore executa a replicação síncrona sem mestre de cada grupo de entidades em uma ampla área geográfica para fornecer alta confiabilidade e tolerância a falhas.

Em muitos aplicativos, é aceitável usar a consistência eventual (por exemplo, uma consulta global abrangendo vários grupos de entidades, que algumas vezes pode retornar dados ligeiramente desatualizados) para ter uma visualização ampla dos dados não relacionados e depois usar a consistência forte (uma consulta de ancestral ou lookup de uma única entidade) ao visualizar ou editar um único conjunto de dados altamente relacionados. Em tais aplicativos, geralmente é uma boa abordagem usar um grupo de entidades separado para cada conjunto de dados altamente relacionados. Para mais informações, consulte Consistência de dados.

A seguir

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

Enviar comentários sobre…

Documentação do Cloud Datastore