Cloud Datastore 트랜잭션

트랜잭션은 최대 25개의 항목 그룹에 있는 하나 이상의 항목에 대한 Datastore 작업의 집합입니다. 각 트랜잭션은 원자적 성격을 가집니다. 즉 트랜잭션이 일부만 적용되는 경우는 없습니다. 트랜잭션의 모든 작업이 적용되거나 아니면 모두 취소되는 방식입니다.

트랜잭션 사용

트랜잭션의 최대 지속 시간은 270초이며, 30초 후에 10초의 유휴 만료 시간이 있습니다.

다음과 같은 경우에는 작업이 실패할 수 있습니다.

  • 동일한 항목 그룹에 동시 실행되는 수정 시도가 너무 많은 경우
  • 트랜잭션이 리소스 한도를 초과하는 경우
  • Datastore에 내부 오류가 발생하는 경우

이러한 경우에는 항상 Datastore API가 오류를 반환합니다.

트랜잭션은 Datastore의 선택적 기능이므로 Datastore 작업을 수행하기 위해 반드시 트랜잭션을 사용할 필요는 없습니다.

애플리케이션은 단일 트랜잭션으로 일련의 문과 Datastore 작업을 실행할 수 있습니다. 따라서 일부 문 또는 작업에서 예외가 발생하면 어떤 Datastore 작업도 적용되지 않습니다. 애플리케이션은 트랜잭션에서 수행할 작업을 정의합니다.

다음 스니펫은 Datastore API를 사용하여 트랜잭션을 수행하는 방법을 보여줍니다. 계좌 간에 자금을 이체하는 경우입니다.

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을 생략하기도 합니다. 프로덕션 코드에서는 모든 트랜잭션이 명시적으로 커밋되거나 롤백되도록 만드는 것이 중요합니다.

트랜잭션에서 가능한 작업

트랜잭션의 모든 Datastore 작업은 최대 25개의 항목 그룹에서 운영될 수 있습니다. 여기에는 상위 그룹별 항목 쿼리, 키별 항목 검색, 항목 업데이트, 항목 삭제가 포함됩니다.

2개 이상의 트랜잭션이 하나 이상의 공통된 항목 그룹에 있는 항목을 수정하려고 동시에 시도하면 변경 사항을 커밋한 첫 번째 트랜잭션만 성공하고, 나머지 모든 항목은 커밋에 실패합니다. 이러한 설계로 인해 항목 그룹을 사용하면 임의의 그룹 항목에 동시에 실행할 수 있는 쓰기 수가 제한됩니다. 트랜잭션이 시작되면 Datastore에서는 트랜잭션에 사용되는 항목 그룹의 최종 업데이트 시간을 확인하여 최적의 동시 실행 제어를 수행합니다. 항목 그룹에 트랜잭션을 커밋하면 Datastore가 트랜잭션에 사용된 항목 그룹의 최종 업데이트 시간을 다시 확인합니다. 최초 확인 이후로 변경된 내역이 있으면 오류가 반환됩니다. 항목 그룹에 대한 설명은 상위 경로를 참조하세요.

격리 및 일관성

트랜잭션 외에 Datastore의 격리 수준은 커밋된 읽기에 가장 근접합니다. 트랜잭션 내에서 직렬 가능한 격리가 적용됩니다. 즉, 다른 트랜잭션은 이 트랜잭션에서 읽거나 수정한 데이터를 동시에 수정할 수 없습니다. 격리 수준에 대한 자세한 내용은 트랜잭션 격리 문서와 위키의 직렬화 가능한 격리를 참조하세요.

트랜잭션의 모든 읽기에는 트랜잭션이 시작된 시점에 Datastore의 현재 일관된 상태가 반영됩니다. 트랜잭션 내 쿼리 및 조회를 통해 트랜잭션 시작 시점을 기준으로 Datastore의 일관된 단일 스냅샷을 확인할 수 있습니다. 트랜잭션 항목 그룹 내의 항목 및 색인 행은 완전히 업데이트되므로 트랜잭션 외부의 쿼리에서 발생할 수 있는 거짓 양성 또는 거짓 음성 없이 완전하고 정확한 결과 항목 집합이 반환됩니다(트랜잭션 격리 설명 참조).

이 일관된 스냅샷 보기는 트랜잭션 내 쓰기 후 읽기까지도 확장됩니다. 대부분의 데이터베이스와 달리 Datastore 트랜잭션 내 쿼리 및 가져오기는 트랜잭션 내에서 이전에 발생한 쓰기 결과를 보여주지 않습니다. 특히 트랜잭션 내에서 항목이 수정되거나 삭제된 경우 쿼리 또는 조회를 수행하면 트랜잭션 시작 시점의 원래 버전 항목이 반환되며, 시작 시점에 해당 항목이 없었으면 아무것도 반환되지 않습니다.

트랜잭션 용도

트랜잭션 용도 중 하나는 항목을 현재 값과 관련된 새로운 속성 값으로 업데이트하는 것입니다. 위의 transferFunds 예시에서는 한 계좌에 있는 돈을 인출해 다른 계좌로 이체하여 두 항목에 이 작업을 수행합니다. 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 값을 업데이트할 수 있으므로 트랜잭션이 필요합니다. 트랜잭션이 없으면 사용자의 요청은 다른 사용자의 업데이트 전에 balance 값을 사용하고 새 값을 덮어씁니다. 트랜잭션이 있으면 애플리케이션에 다른 사용자의 업데이트가 전달됩니다.

트랜잭션의 다른 일반적인 용도는 명명된 키로 항목을 가져오거나 없으면 만드는 것입니다(이 예시에서는 항목 만들기의 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명의 사용자가 생성을 시도하면 두 번째 생성된 내용이 첫 번째 생성 사실을 알지 못한 채 첫 번째 내용을 덮어쓰게 됩니다.

트랜잭션이 실패하는 경우 성공할 때까지 재시도하도록 앱을 설정하거나 앱의 사용자 인터페이스 수준에 전달하여 사용자가 오류를 처리하도록 할 수 있습니다. 매 트랜잭션마다 재시도 루프를 만들 필요는 없습니다.

마지막으로 트랜잭션을 사용하여 Datastore의 일관된 스냅샷을 읽을 수 있습니다. 페이지를 렌더링하거나 일관성이 필요한 데이터를 내보내는 데 여러 읽기 작업이 필요한 경우 유용할 수 있습니다. 이러한 종류의 트랜잭션은 쓰기를 수행하지 않으므로 보통 읽기 전용 트랜잭션이라고 부릅니다. 읽기 전용 단일 그룹 트랜잭션이 동시 실행 수정으로 인해 실패하는 경우는 없으므로 실패 시 재시도를 구현할 필요는 없습니다. 하지만 다중 항목 그룹 트랜잭션은 동시 실행 수정으로 인해 실패할 수 있으므로 재시도를 포함해야 합니다.

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

트랜잭션 및 항목 그룹

항목 그룹은 공통된 루트 요소에 상위 관계로 연결된 항목 집합입니다. 데이터를 항목 그룹으로 구성하면 트랜잭션이 수행할 수 있는 작업이 제한될 수 있습니다.

  • 트랜잭션에서 액세스하는 모든 데이터는 최대 25개의 항목 그룹에만 포함될 수 있습니다.
  • 트랜잭션 내에서 쿼리를 사용하려면 올바른 데이터에 일치하는 상위 필터를 지정할 수 있는 방식으로 데이터가 항목 그룹으로 구성되어 있어야 합니다.
  • 단일 항목 그룹 내에서는 쓰기 처리량 한도가 초당 약 1개의 트랜잭션으로 제한되어 있습니다. 이 한도가 존재하는 이유는 Datastore가 높은 안정성과 내결함성을 제공하기 위해 광범위한 지리적 영역에 걸쳐 각 항목 그룹의 마스터 없는 동기 복제 작업을 수행하기 때문입니다.

많은 애플리케이션에서 관련성이 없는 데이터를 포괄적으로 확인할 때는 eventual consistency(즉, 여러 항목 그룹에 걸친 비상위 쿼리로 약간 오래된 데이터를 반환할 수 있음)를 사용하고 관련성이 높은 단일 데이터세트를 보거나 수정할 때는 strong consistency(상위 쿼리 또는 단일 항목의 lookup)를 사용하는 것이 좋을 수 있습니다. 이러한 애플리케이션에서는 일반적으로 관련성이 높은 각 데이터세트마다 별도의 항목 그룹을 사용하는 방식이 바람직합니다. 자세한 내용은 데이터 일관성을 참조하세요.

다음 단계