トランザクション

トランザクションは、1 つ以上のエンティティに対する一連のオペレーションです。トランザクションのオペレーションは不可分で、部分的に実行されることはありません。トランザクション内のオペレーションは、すべて適用されるか 1 つも適用されないかのどちらかとなります。

トランザクションの使用

トランザクションは 270 秒後またはアイドル状態が 60 秒間続くと期限切れになります。

次のような場合は、オペレーションが失敗することがあります。

  • 1 つのエンティティに対し、過剰な数の変更が同時に行われようとしている場合。
  • トランザクションがリソース制限を超過している場合。
  • Datastore モードのデータベースで内部エラーが発生した場合。

上記のいずれの場合も、Datastore API はエラーを返します。

トランザクションはオプション機能です。データベース オペレーションの実行のために、必ずしもトランザクションを使用する必要はありません。

アプリケーションは、ステートメントとオペレーションのセットを 1 つのトランザクションで実行できます。そのため、ステートメントまたはオペレーションのいずれかで例外が発生すると、そのセット内のデータベース オペレーションは一切反映されません。トランザクションで実行するアクションは、アプリケーションによって定義されます。

次のスニペットは、トランザクションを実行する方法を示しています。これは、ある口座のお金を別の口座に移すトランザクションです。

C#

Cloud Datastore 用のクライアント ライブラリをインストールして使用する方法については、Cloud Datastore のクライアント ライブラリをご覧ください。 詳細については、Cloud Datastore C# API のリファレンス ドキュメントをご覧ください。

Cloud Datastore への認証を行うには、アプリケーションのデフォルト認証情報を設定します。詳細については、ローカル開発環境の認証を設定するをご覧ください。

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

Cloud Datastore 用のクライアント ライブラリをインストールして使用する方法については、Cloud Datastore のクライアント ライブラリをご覧ください。 詳細については、Cloud Datastore Go API のリファレンス ドキュメントをご覧ください。

Cloud Datastore への認証を行うには、アプリケーションのデフォルト認証情報を設定します。詳細については、ローカル開発環境の認証を設定するをご覧ください。

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

Cloud Datastore 用のクライアント ライブラリをインストールして使用する方法については、Cloud Datastore のクライアント ライブラリをご覧ください。 詳細については、Cloud Datastore Java API のリファレンス ドキュメントをご覧ください。

Cloud Datastore への認証を行うには、アプリケーションのデフォルト認証情報を設定します。詳細については、ローカル開発環境の認証を設定するをご覧ください。

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

Cloud Datastore 用のクライアント ライブラリをインストールして使用する方法については、Cloud Datastore のクライアント ライブラリをご覧ください。 詳細については、Cloud Datastore Node.js API のリファレンス ドキュメントをご覧ください。

Cloud Datastore への認証を行うには、アプリケーションのデフォルト認証情報を設定します。詳細については、ローカル開発環境の認証を設定するをご覧ください。

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 await transaction.commit();
}

PHP

Cloud Datastore 用のクライアント ライブラリをインストールして使用する方法については、Cloud Datastore のクライアント ライブラリをご覧ください。 詳細については、Cloud Datastore PHP API のリファレンス ドキュメントをご覧ください。

Cloud Datastore への認証を行うには、アプリケーションのデフォルト認証情報を設定します。詳細については、ローカル開発環境の認証を設定するをご覧ください。

/**
 * Update two entities in a transaction.
 *
 * @param string $fromKeyId
 * @param string $toKeyId
 * @param int $amount
 * @param string $namespaceId
 */
function transfer_funds(
    string $fromKeyId,
    string $toKeyId,
    int $amount,
    string $namespaceId = null
) {
    $datastore = new DatastoreClient(['namespaceId' => $namespaceId]);
    $transaction = $datastore->transaction();
    $fromKey = $datastore->key('Account', $fromKeyId);
    $toKey = $datastore->key('Account', $toKeyId);
    // 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

Cloud Datastore 用のクライアント ライブラリをインストールして使用する方法については、Cloud Datastore のクライアント ライブラリをご覧ください。 詳細については、Cloud Datastore Python API のリファレンス ドキュメントをご覧ください。

Cloud Datastore への認証を行うには、アプリケーションのデフォルト認証情報を設定します。詳細については、ローカル開発環境の認証を設定するをご覧ください。

from google.cloud import datastore

# For help authenticating your client, visit
# https://cloud.google.com/docs/authentication/getting-started
client = datastore.Client()

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

Cloud Datastore 用のクライアント ライブラリをインストールして使用する方法については、Cloud Datastore のクライアント ライブラリをご覧ください。 詳細については、Cloud Datastore Ruby API のリファレンス ドキュメントをご覧ください。

Cloud Datastore への認証を行うには、アプリケーションのデフォルト認証情報を設定します。詳細については、ローカル開発環境の認証を設定するをご覧ください。

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

例をわかりやすく単純にするため、トランザクションが失敗した場合に rollback を省略していることがあります。本番環境のコードでは、すべてのトランザクションが明示的に commit またはロールバックされるようにすることが重要です。

トランザクションで実行可能な処理

トランザクションでは任意の数のエンティティにクエリを実行したり、検索したりできます。 トランザクションの最大サイズは 10 MiB です。読み取り / 書き込みトランザクションまたは読み取り専用トランザクションを使用できます。

分離と整合性

Datastore モードのデータベースでは、シリアル化可能な分離が強制的に適用されます。つまり、トランザクションによって読み取られた、または変更されたデータを同時に変更することはできません。

トランザクション内の各クエリおよび検索は、データベースの状態の整合性のあるスナップショットを参照します。このスナップショットは、トランザクションの開始前に完了したすべてのトランザクションと書き込みの効果が反映されることが保証されています。

こうした一貫性のあるスナップショット ビューは、トランザクション内の書き込み後の読み取りにまで拡張されています。ほとんどのデータベースと異なり、Datastore モードのトランザクション内の query と lookup 処理は、トランザクション内の以前の書き込み結果を参照しません。つまり、トランザクション内でエンティティの変更または削除が行われてからクエリまたはルックアップを実行した場合、トランザクションを開始した当初のバージョンのエンティティが返されます。開始したときにエンティティが存在しなければ、何も返されません。

トランザクション以外でも、クエリと検索にはシリアル化可能な分離が適用されます。

同時実行モード

Datastore モードの Firestore では、3 つの同時実行モードがサポートされています。同時実行モードは、同時実行トランザクションの相互作用方法を決定するデータベース設定です。次のいずれかの同時実行モードを選択できます。

  • 悲観的

    読み取り / 書き込みトランザクションでは、分離とシリアル化を強制的に適用するために、リーダー / ライターロックが使用されます。2 つ以上の読み取り / 書き込みトランザクションを同時に実行して、同じデータの読み取りまたは書き込みを行う際には、一方のトランザクションでロックを保持して、他方のトランザクションを遅延させることがあります。トランザクションで書き込みが不要であれば、読み取り専用トランザクションを使用することで、パフォーマンスを改善して他のトランザクションとの競合を回避できます。読み取り専用トランザクションではロックは必要ありません。

    Datastore モードの Firestore データベースは、デフォルトで悲観的同時実行モードを使用します。

  • 楽観的

    2 つ以上の同時読み取り / 書き込みトランザクションが同じデータを読み書きする場合、最初に commit されたトランザクションのみが変更を commit します。書き込みを実行する他のトランザクションは、commit 時に失敗します。

  • エンティティ グループによるオプティミスティック

    この同時実行モードは、アプリが以前の Cloud Datastore のエンティティ グループのトランザクション セマンティクスに依存している場合にのみ使用してください。この同時実行モードでは、トランザクションに追加の制限が適用されます。

    • トランザクションは 25 のエンティティ グループに制限されます。
    • エンティティ グループへの書き込みは 1 秒あたり 1 回に制限されます。
    • トランザクション内のクエリは祖先クエリでなければなりません。

    OPTIMISTIC_WITH_ENTITY_GROUPS のクエリ、トランザクション、書き込みスループットの制限を取り除くには、プロジェクトの同時実行モードを「楽観的」に変更します。この変更がプロジェクトに適合していることを確認するには、次の手順に従います。

    1. Datastore モードの Firestore でテスト プロジェクトを作成します。

    2. テスト プロジェクトの同時実行モードを OPTIMISTIC に変更します。次に示すように、HTTP PATCH リクエストを発行します。

    3. テスト プロジェクトでテストを実行し、エンティティ グループがなくてもワークロードが期待どおりに動作することを確認します。

    4. メイン プロジェクトの同時実行モードOPTIMISTIC_WITH_ENTITY_GROUPS から OPTIMISTIC に変更します。

同時実行モードを表示する

Firestore の projects.databases REST リソースを使用して、データベースの同時実行モードを表示します。

curl -X GET -H "Authorization: Bearer "$(gcloud auth print-access-token) \
"https://firestore.googleapis.com/v1/projects/PROJECT_ID/databases"

同時実行モードを変更する

データベースの同時実行モードを変更するには、PATCH リクエストを Firestore の projects.databases REST リソースに送信します。

curl --request PATCH \
--header "Authorization: Bearer "$(gcloud auth print-access-token) \
--header 'Accept: application/json' \
--header 'Content-Type: application/json' \
--data '{"concurrencyMode":"CONCURRENCY_MODE"}' \
"https://firestore.googleapis.com/v1/projects/PROJECT_ID/databases/(default)?updateMask=concurrencyMode"

ここで

  • CONCURRENCY_MODEPESSIMISTICOPTIMISTIC または OPTIMISTIC_WITH_ENTITY_GROUPS です。
  • PROJECT_ID は、Google Cloud プロジェクトの ID です。

トランザクションの用法

トランザクションの用法の 1 つは、現在の値より新しいプロパティ値で、エンティティを更新することです。上記の transferFunds の例では、2 つのエンティティに対してまさにこのオペレーションを実行しています。一方の口座からお金を引き出し、他方の口座に送金するというオペレーションです。Datastore API では、トランザクションの再試行は自動的に行われません。しかし、トランザクションを再試行するように独自のロジックを追加することはできます。たとえば、別のリクエストによって同一エンティティが同時に更新される場合に競合を処理するためなどです。

C#

Cloud Datastore 用のクライアント ライブラリをインストールして使用する方法については、Cloud Datastore のクライアント ライブラリをご覧ください。 詳細については、Cloud Datastore C# API のリファレンス ドキュメントをご覧ください。

Cloud Datastore への認証を行うには、アプリケーションのデフォルト認証情報を設定します。詳細については、ローカル開発環境の認証を設定するをご覧ください。

/// <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

Cloud Datastore 用のクライアント ライブラリをインストールして使用する方法については、Cloud Datastore のクライアント ライブラリをご覧ください。 詳細については、Cloud Datastore Go API のリファレンス ドキュメントをご覧ください。

Cloud Datastore への認証を行うには、アプリケーションのデフォルト認証情報を設定します。詳細については、ローカル開発環境の認証を設定するをご覧ください。

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

Cloud Datastore 用のクライアント ライブラリをインストールして使用する方法については、Cloud Datastore のクライアント ライブラリをご覧ください。 詳細については、Cloud Datastore Java API のリファレンス ドキュメントをご覧ください。

Cloud Datastore への認証を行うには、アプリケーションのデフォルト認証情報を設定します。詳細については、ローカル開発環境の認証を設定するをご覧ください。

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

Cloud Datastore 用のクライアント ライブラリをインストールして使用する方法については、Cloud Datastore のクライアント ライブラリをご覧ください。 詳細については、Cloud Datastore Node.js API のリファレンス ドキュメントをご覧ください。

Cloud Datastore への認証を行うには、アプリケーションのデフォルト認証情報を設定します。詳細については、ローカル開発環境の認証を設定するをご覧ください。

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

Cloud Datastore 用のクライアント ライブラリをインストールして使用する方法については、Cloud Datastore のクライアント ライブラリをご覧ください。 詳細については、Cloud Datastore PHP API のリファレンス ドキュメントをご覧ください。

Cloud Datastore への認証を行うには、アプリケーションのデフォルト認証情報を設定します。詳細については、ローカル開発環境の認証を設定するをご覧ください。

$retries = 5;
for ($i = 0; $i < $retries; $i++) {
    try {
        require_once __DIR__ . '/transfer_funds.php';
        transfer_funds($fromKeyId, $toKeyId, 10, $namespaceId);
    } catch (\Google\Cloud\Core\Exception\ConflictException $e) {
        // if $i >= $retries, the failure is final
        continue;
    }
    // Succeeded!
    break;
}

Python

Cloud Datastore 用のクライアント ライブラリをインストールして使用する方法については、Cloud Datastore のクライアント ライブラリをご覧ください。 詳細については、Cloud Datastore Python API のリファレンス ドキュメントをご覧ください。

Cloud Datastore への認証を行うには、アプリケーションのデフォルト認証情報を設定します。詳細については、ローカル開発環境の認証を設定するをご覧ください。

from google.cloud import datastore

# For help authenticating your client, visit
# https://cloud.google.com/docs/authentication/getting-started
client = datastore.Client()

import google.cloud.exceptions

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

Cloud Datastore 用のクライアント ライブラリをインストールして使用する方法については、Cloud Datastore のクライアント ライブラリをご覧ください。 詳細については、Cloud Datastore Ruby API のリファレンス ドキュメントをご覧ください。

Cloud Datastore への認証を行うには、アプリケーションのデフォルト認証情報を設定します。詳細については、ローカル開発環境の認証を設定するをご覧ください。

(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

これにはトランザクションが必要です。このコードがオブジェクトをフェッチしてから、変更済みオブジェクトを保存するまでの間に、エンティティ内の balance の値が別のユーザーによって更新されてしまう可能性があるからです。トランザクションを使用しないと、最初のユーザーのリクエストに対し、もう 1 人のユーザーによる更新前の balance 値が使用され、保存によって新しい値が上書きされてしまいます。トランザクションを使用することで、もう 1 人のユーザーによる更新がアプリケーションに通知されます。

トランザクションのもう 1 つの一般的な用法は、名前付きキーによってエンティティを取得することです。名前付きキーがまだ存在しない場合は、新規に作成します(次の例はエンティティの作成で使用した TaskList サンプルを基にしています)。

C#

Cloud Datastore 用のクライアント ライブラリをインストールして使用する方法については、Cloud Datastore のクライアント ライブラリをご覧ください。 詳細については、Cloud Datastore C# API のリファレンス ドキュメントをご覧ください。

Cloud Datastore への認証を行うには、アプリケーションのデフォルト認証情報を設定します。詳細については、ローカル開発環境の認証を設定するをご覧ください。

Entity task;
using (var transaction = _db.BeginTransaction())
{
    task = transaction.Lookup(_sampleTask.Key);
    if (task == null)
    {
        transaction.Insert(_sampleTask);
        transaction.Commit();
    }
}

Go

Cloud Datastore 用のクライアント ライブラリをインストールして使用する方法については、Cloud Datastore のクライアント ライブラリをご覧ください。 詳細については、Cloud Datastore Go API のリファレンス ドキュメントをご覧ください。

Cloud Datastore への認証を行うには、アプリケーションのデフォルト認証情報を設定します。詳細については、ローカル開発環境の認証を設定するをご覧ください。

_, 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

Cloud Datastore 用のクライアント ライブラリをインストールして使用する方法については、Cloud Datastore のクライアント ライブラリをご覧ください。 詳細については、Cloud Datastore Java API のリファレンス ドキュメントをご覧ください。

Cloud Datastore への認証を行うには、アプリケーションのデフォルト認証情報を設定します。詳細については、ローカル開発環境の認証を設定するをご覧ください。

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

Cloud Datastore 用のクライアント ライブラリをインストールして使用する方法については、Cloud Datastore のクライアント ライブラリをご覧ください。 詳細については、Cloud Datastore Node.js API のリファレンス ドキュメントをご覧ください。

Cloud Datastore への認証を行うには、アプリケーションのデフォルト認証情報を設定します。詳細については、ローカル開発環境の認証を設定するをご覧ください。

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.
      await transaction.rollback();
    } else {
      // Create the task entity.
      transaction.save(taskEntity);
      await transaction.commit();
    }
    return taskEntity;
  } catch (err) {
    await transaction.rollback();
  }
}

PHP

Cloud Datastore 用のクライアント ライブラリをインストールして使用する方法については、Cloud Datastore のクライアント ライブラリをご覧ください。 詳細については、Cloud Datastore PHP API のリファレンス ドキュメントをご覧ください。

Cloud Datastore への認証を行うには、アプリケーションのデフォルト認証情報を設定します。詳細については、ローカル開発環境の認証を設定するをご覧ください。

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

Python

Cloud Datastore 用のクライアント ライブラリをインストールして使用する方法については、Cloud Datastore のクライアント ライブラリをご覧ください。 詳細については、Cloud Datastore Python API のリファレンス ドキュメントをご覧ください。

Cloud Datastore への認証を行うには、アプリケーションのデフォルト認証情報を設定します。詳細については、ローカル開発環境の認証を設定するをご覧ください。

from google.cloud import datastore

# For help authenticating your client, visit
# https://cloud.google.com/docs/authentication/getting-started
client = datastore.Client()

import datetime

with client.transaction():
    key = client.key(
        "Task", datetime.datetime.now(tz=datetime.timezone.utc).isoformat()
    )

    task = client.get(key)

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

    return task

Ruby

Cloud Datastore 用のクライアント ライブラリをインストールして使用する方法については、Cloud Datastore のクライアント ライブラリをご覧ください。 詳細については、Cloud Datastore Ruby API のリファレンス ドキュメントをご覧ください。

Cloud Datastore への認証を行うには、アプリケーションのデフォルト認証情報を設定します。詳細については、ローカル開発環境の認証を設定するをご覧ください。

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

同じ文字列 ID を持つエンティティを別のユーザーが作成または更新しようとする状況に対処するには、前の例と同様にトランザクションが必要です。エンティティがまだ存在していない時点で 2 人のユーザーが同時にこれを作成しようとした場合、トランザクションを使用しなければ、先に作成されたエンティティは 2 人目のユーザーの操作により上書きされ、しかも 2 人ともエンティティが上書きされたことに気がづきません。

トランザクションが失敗した場合は、成功するまでアプリケーションにトランザクションを再試行させることができます。またはアプリケーションのユーザー インターフェース レベルまでエラーを伝達し、ユーザーにこのエラーを処理させることもできます。すべてのトランザクションに対し、再試行ループを作成する必要はありません。

読み取り専用トランザクション

これで、トランザクションを使用して、データベースの一貫性のあるスナップショットを読み取ることができるようになりました。こうした手法は、整合性が求められるページの表示やデータのエクスポートに、複数回の読み取りが必要となる場合に役立ちます。その場合には、読み取り専用トランザクションを作成できます。

読み取り専用トランザクションはエンティティを変更できませんが、その代わり、他のトランザクションと競合せず、再試行の必要もありません。通常の読み書きトランザクションで読み取りのみを実行すると、そのトランザクションは同一データを変更するトランザクションと競合することがあります。

C#

Cloud Datastore 用のクライアント ライブラリをインストールして使用する方法については、Cloud Datastore のクライアント ライブラリをご覧ください。 詳細については、Cloud Datastore C# API のリファレンス ドキュメントをご覧ください。

Cloud Datastore への認証を行うには、アプリケーションのデフォルト認証情報を設定します。詳細については、ローカル開発環境の認証を設定するをご覧ください。

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

Cloud Datastore 用のクライアント ライブラリをインストールして使用する方法については、Cloud Datastore のクライアント ライブラリをご覧ください。 詳細については、Cloud Datastore Go API のリファレンス ドキュメントをご覧ください。

Cloud Datastore への認証を行うには、アプリケーションのデフォルト認証情報を設定します。詳細については、ローカル開発環境の認証を設定するをご覧ください。

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

Cloud Datastore 用のクライアント ライブラリをインストールして使用する方法については、Cloud Datastore のクライアント ライブラリをご覧ください。 詳細については、Cloud Datastore Java API のリファレンス ドキュメントをご覧ください。

Cloud Datastore への認証を行うには、アプリケーションのデフォルト認証情報を設定します。詳細については、ローカル開発環境の認証を設定するをご覧ください。

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

Cloud Datastore 用のクライアント ライブラリをインストールして使用する方法については、Cloud Datastore のクライアント ライブラリをご覧ください。 詳細については、Cloud Datastore Node.js API のリファレンス ドキュメントをご覧ください。

Cloud Datastore への認証を行うには、アプリケーションのデフォルト認証情報を設定します。詳細については、ローカル開発環境の認証を設定するをご覧ください。

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

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

PHP

Cloud Datastore 用のクライアント ライブラリをインストールして使用する方法については、Cloud Datastore のクライアント ライブラリをご覧ください。 詳細については、Cloud Datastore PHP API のリファレンス ドキュメントをご覧ください。

Cloud Datastore への認証を行うには、アプリケーションのデフォルト認証情報を設定します。詳細については、ローカル開発環境の認証を設定するをご覧ください。

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

Python

Cloud Datastore 用のクライアント ライブラリをインストールして使用する方法については、Cloud Datastore のクライアント ライブラリをご覧ください。 詳細については、Cloud Datastore Python API のリファレンス ドキュメントをご覧ください。

Cloud Datastore への認証を行うには、アプリケーションのデフォルト認証情報を設定します。詳細については、ローカル開発環境の認証を設定するをご覧ください。

from google.cloud import datastore

# For help authenticating your client, visit
# https://cloud.google.com/docs/authentication/getting-started
client = datastore.Client()

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

Cloud Datastore 用のクライアント ライブラリをインストールして使用する方法については、Cloud Datastore のクライアント ライブラリをご覧ください。 詳細については、Cloud Datastore Ruby API のリファレンス ドキュメントをご覧ください。

Cloud Datastore への認証を行うには、アプリケーションのデフォルト認証情報を設定します。詳細については、ローカル開発環境の認証を設定するをご覧ください。

# task_list_name = "default"
task_list_key = datastore.key "TaskList", task_list_name
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

次のステップ