Go Datastore API

このドキュメントでは、Cloud Datastore に格納されるオブジェクトのデータモデル、API を使用したクエリの作成方法、トランザクションの処理方法について説明します。datastore パッケージの内容を確認するには、datastore パッケージ リファレンスをご覧ください。

エンティティ

Cloud Datastore のオブジェクトをエンティティと呼びます。エンティティは 1 つ以上の名前付きプロパティを持ち、各プロパティが 1 つ以上の値を持ちます。プロパティ値には、整数、浮動小数点数、文字列、日付、バイナリデータなど、さまざまなデータ型があります。複数の値を持つプロパティに対してクエリを実行すると、いずれかの値がクエリの条件を満たしているかどうかが評価されます。このため、メンバーかどうかを評価をするときに、そのようなプロパティが役立ちます。

種類、キー、識別子

Cloud Datastore の各エンティティは特定の種類に属しています。種類とは、クエリ用にエンティティを分類するものです。たとえば、人事アプリケーションでは、会社の各従業員を Employee という種類のエンティティで表します。また、各エンティティに独自のキーがあり、これによりエンティティが一意に識別されます。キーは次のコンポーネントで構成されています。

  • エンティティの種類
  • 識別子。次のいずれかになります。
    • キー名の文字列
    • 整数の ID
  • オプションの祖先パス。Cloud Datastore 階層内でのエンティティの位置を指定します。

識別子は、エンティティの作成時に割り当てられます。識別子はエンティティのキーの一部であるため、エンティティに永続的に関連付けられており、変更できません。識別子を割り当てる方法は 2 通りあります。

  • アプリケーションでエンティティの固有のキー名文字列を指定する。
  • エンティティに整数の数値 ID が自動的に割り当てられるように Cloud Datastore を設定する。

識別子の割り当て

ランタイム サーバーは、次の 2 種類の自動 ID ポリシーを使用して ID を自動生成するように構成できます。

  • default ポリシーは、おおむね均一に分散された一連の ID を生成します。各 ID の長さは最大で 16 桁です。
  • legacy ポリシーは、連続していない小さい整数の ID を生成します。

エンティティの ID をユーザーに表示する場合や、ID の順序に従ってなんらかの処理を行う場合は、手動で割り当てることをおすすめします。

祖先パス

Cloud Datastore 内の各エンティティは、ファイル システムのディレクトリ構造と同様の階層的に構造化された空間を形成します。エンティティを作成するときに、別のエンティティを親として設定することもできます。新しいエンティティは、この親エンティティの子になります(ファイル システムとは異なり、親エンティティが実際に存在している必要はありません)。親を持たないエンティティは、ルート エンティティとなります。 エンティティと親との割り当ては永続的であり、エンティティの作成後は変更できません。同じ親を持つ 2 つのエンティティ、または 2 つのルート エンティティ(親を持たないエンティティ)に同じ数値 ID が割り当てられることはありません。

エンティティの親、親の親、さらにその親などは順に祖先として位置付けられ、エンティティの子、子の子、さらにその子などは順に子孫として位置付けられます。 ルート エンティティとその子孫のエンティティはすべて同じエンティティ グループに属します。 ルート エンティティから始まり、親から子を経由して対象のエンティティに至るまでの連なりをエンティティの祖先パスといいます。 エンティティを識別する完全なキーは、エンティティの祖先パスから始まってそのエンティティ自身で終わる一連の「種類と識別子のペア」で構成されます。

[Person:GreatGrandpa, Person:Grandpa, Person:Dad, Person:Me]

ルート エンティティの場合は祖先パスが空で、エンティティ自身の種類と識別子だけでキーが構成されています。

[Person:GreatGrandpa]

この概念を次の図に示します。

エンティティ グループ

クエリとインデックス

アプリケーションは、キーにより Cloud Datastore から直接エンティティを取得できるだけでなく、クエリを実行して、エンティティのプロパティ値を基準にエンティティを取得することもできます。クエリは、指定された種類のエンティティに対して動作します。クエリでは、エンティティのプロパティ値、キー、祖先に対してフィルタを指定し、結果として 0 個以上のエンティティを返すことができます。 並べ替え順を指定し、プロパティ値を基準に結果を並べ替えることもできます。結果には、フィルタと並べ替え順で指定されているすべてのプロパティに対応する 1 つ以上の値を持つエンティティの中で、指定されているすべてのフィルタ条件をプロパティ値が満たしているものがすべて含まれます。クエリは、エンティティ全体や射影が作成されたエンティティのほか、エンティティのキーだけを返すこともできます。

一般的に、クエリは次のような要素で構成されます。

クエリを実行すると、指定したすべてのフィルタ条件を満たす、指定した種類のすべてのエンティティが取得され、指定した順序で並べ替えられます。クエリは読み取り専用として実行されます。

注: メモリを節約し、パフォーマンスを向上させるため、クエリでは可能な限り返される結果の数に対する制限を指定してください。

クエリに祖先フィルタを設定すると、指定した祖先の下位にあるエンティティ グループだけが返されるように結果を制限できます。このようなクエリを祖先クエリといいます。デフォルトでは、祖先クエリは強整合性が確保された結果を返します。つまり、データに対する直近の変更内容が反映された最新の結果です。これに対して、祖先クエリ以外のクエリは、1 つのエンティティ グループだけでなく Cloud Datastore 全体を対象にすることができますが、結果整合性のみを確保するため、最新でない結果が返される可能性があります。強整合性が必要なアプリケーションでデータ構造を定義する場合は、祖先を指定してクエリで取得できるように、関連するエンティティを同じエンティティ グループに配置することも検討してください。

App Engine は、エンティティの各プロパティに単純なインデックスを事前に定義します。さらに App Engine アプリケーションでは、index.yaml という名前のインデックス構成ファイルでカスタム インデックスを定義することもできます。開発用サーバーは、既存のインデックスでは実行できないクエリが出現したときに、このファイルに自動的に候補を追加します。アプリケーションをアップロードする前にこのファイルを編集して、インデックスを手動で調整できます。

注: インデックス ベースのクエリ メカニズムはさまざまなクエリをサポートしているため、ほとんどのアプリケーションに適しています。ただし、他のデータベース テクノロジーでは一般にサポートされているいくつかの種類のクエリがサポートされていません。特に Cloud Datastore のクエリエンジンでは、結合と集計クエリがサポートされていない点に注意してください。Cloud Datastore クエリの制限については、データストアのクエリのページをご覧ください。

トランザクション

エンティティの挿入、更新、削除はすべてトランザクションのコンテキスト内で試行されます。1 つのトランザクションに、このようなオペレーションをいくつでも含めることができます。すべてのオペレーションはトランザクションごとに 1 つの単位として Cloud Datastore に適用されるので、データの整合性が維持されます。いずれかのオペレーションが失敗した場合、どのオペレーションも適用されません。

1 つのエンティティに対する複数のオペレーションを 1 つのトランザクションで実行できます。たとえば、オブジェクトのカウンタ フィールドの値を増やす場合、カウンタの値を読み取り、新しい値を計算してフィールドに保存する必要があります。トランザクションを使用しないでこのオペレーションを行うと、値の読み取り時点から更新時点までの間にカウンタの値が別のプロセスによって更新されてしまうことがあり、アプリケーションがその更新された値を上書きすることになる可能性があります。1 つのトランザクションで読み取り、計算、書き込みを行うことで、他のプロセスによる影響を防ぐことができます。

トランザクションとエンティティ グループ

トランザクションで使用できるクエリは祖先クエリだけです。つまり、トランザクションで実行するクエリの対象は単一のエンティティ グループに限定されます。トランザクション自体は、単一のエンティティ グループに属するエンティティだけでなく、最大 25 の異なるエンティティ グループ(クロスグループ トランザクションの場合)に属する複数のエンティティに適用できます。

Cloud Datastore は、楽観的同時実行を使用してトランザクションを管理します。2 つ以上のトランザクションが、既存エンティティの更新や新しいエンティティの作成といった変更を同じエンティティ グループに対して同時に実行しようとすると、最初に commit したトランザクションが成功し、残りのトランザクションは commit に失敗します。とはいえ、失敗した残りのトランザクションを使って、更新されたデータに対してオペレーションを再試行できます。このような特長があるため、特定のエンティティ グループのエンティティに対して同時に可能な書き込み数は制限されます。

クロスグループ トランザクション

異なるエンティティ グループに属する複数のエンティティに対するトランザクションをクロスグループ(XG)トランザクションといいます。1 つのトランザクションは、最大 25 のエンティティ グループに適用できます。あるトランザクションの適用先となるいずれかのエンティティ グループが他の同時実行トランザクションによって変更される場合を除き、そのトランザクションの処理は成功します。アトミックな書き込みを行うためだけの理由で同じ祖先の下位に異なる種類のデータを無理に配置する必要がないため、データを柔軟に編成することができます。

単一グループ トランザクションと同様に、XG トランザクションでは祖先クエリ以外のクエリを実行することはできません。とはいえ、複数の祖先クエリがある場合、各クエリを別個のエンティティ グループに対して実行できます。非トランザクション クエリ(祖先クエリ以外のクエリ)では、その前に commit されたトランザクションの結果のすべてまたは一部を取得できる場合もありますし、できない場合もあります(この問題の背景については、Cloud Datastore への書き込みとデータの可視性をご覧ください)。ただし、そのような非トランザクション クエリでは、部分的に commit された単一グループ トランザクションの結果より、部分的に commit された XG トランザクションの結果を返す可能性が高くなります。

1 つのエンティティ グループしか処理しない XG トランザクションのパフォーマンスとコストは、XG 以外の単一グループ トランザクションとまったく同じになります。複数のエンティティ グループを処理する XG トランザクションの場合、オペレーションのコストは XG 以外のトランザクションで実行した場合と同じになりますが、レイテンシが長くなる可能性があります。

Cloud Datastore の書き込みとデータの可視性

データは、以下の 2 つのフェーズで Cloud Datastore に書き込まれます。

  1. commit フェーズ。エンティティ データは大半のレプリカのトランザクション ログに記録されます。エンティティ データが記録されなかったレプリカは、最新のログがないことを示すマークが付きます。
  2. 適用フェーズ。レプリカごとに個別に発生します。このフェーズでは、次の 2 つのアクションが並列処理されます。
    • エンティティ データがそのレプリカに書き込まれます。
    • エンティティのインデックス行がそのレプリカに書き込まれます(この処理は、データ自体の書き込みより時間がかかる場合があります)。

書き込みオペレーションは commit フェーズの直後に戻り、適用フェーズが非同期で実行されます。このフェーズは、レプリカごとに異なる時間に実行される可能性が高いため、commit フェーズの完了から数百ミリ秒ほどの遅延が発生する場合があります。commit フェーズでエラーが発生すると、自動的に再試行されますが、エラーが繰り返し発生すると、Cloud Datastore がアプリケーション例外を通知するエラー メッセージを返します。commit フェーズが完了した後、特定のレプリカで適用フェーズが失敗した場合、以下のいずれかが発生すると、そのレプリカで適用が完了までロールフォワードされします。

  • Cloud Datastore の定期的なスイープで未完了の commit ジョブの有無が検査され、それらが適用される。
  • 影響を受けるエンティティ グループを使用する特定のオペレーション(getputdelete、祖先クエリ)が行われたために、次のオペレーションに進む前に、commit 後に未適用の変更が、オペレーションを実行中のレプリカで完了する。

この書き込み動作は、commit フェーズと適用フェーズのそれぞれで、アプリケーションでデータの取得がどのように可能になるかとそのタイミングに影響を及ぼす可能性があります。

  • 書き込みオペレーションでタイムアウト エラーが発生した場合、データの読み取りを行わないと、オペレーションが成功したのか失敗したのか判断できません。
  • Cloud Datastore の get と祖先クエリを実行すると、実行対象となるレプリカに対して未処理のすべての変更が適用されるため、それまでに成功したすべてのトランザクションは一貫した状態で取得できるようになります。つまり、get オペレーション(更新されたエンティティをキーで検索)を実行すると、そのエンティティの最新バージョンを取得できます。
  • 祖先クエリ以外のクエリは、最新のトランザクションが適用されていないレプリカで実行される可能性があるため、古い結果が返される場合があります。たとえ未処理のトランザクションを必ず適用するようなオペレーションを実行したとしても、そのようになる可能性があります。クエリが前のオペレーションとは異なるレプリカで実行される場合があるためです。
  • 同時変更のタイミングが祖先クエリ以外のクエリの結果に影響を及ぼす可能性があります。クエリ条件を満たしていたエンティティが変更され、条件を満たさなくなった場合、クエリを実行したレプリカのインデックスに変更がまだ適用されていないと、エンティティがクエリの結果セットに残ってしまう可能性があります。=

Cloud Datastore の統計情報

Cloud Datastore は、指定の種類のエンティティの数や所定のタイプのプロパティ値に使用されている領域の容量など、アプリケーションのために保存されているデータの統計情報を維持します。このような統計情報は、GCP Console の Cloud Datastore ダッシュボード ページで確認できます。このような値には、Cloud Datastore API を使用してアプリケーション内でプログラミングによってアクセスすることもできます。この場合、特別な名前のエンティティに対してクエリを実行します。詳しくは、Go の Datastore 統計情報をご覧ください。

Go Datastore の例

Go では、Cloud Datastore のエンティティは struct 値から作成されます。この構造体のフィールドが、エンティティのプロパティになります。新しいエンティティを作成するには、保存する値を設定し、キーを作成して、その両方を datastore.Put() に渡します。既存のエンティティを更新する場合は、同じキーを使用して新たに Put() を実行します。Cloud Datastore からエンティティを取得するには、まずエンティティのマーシャリング解除先となる値を設定し、次に、キーとその値へのポインタを datastore.Get() に渡します。

Cloud Datastore でデータの保存と取得を行う例は、次に示すとおりです。

import (
	"fmt"
	"net/http"
	"time"

	"google.golang.org/appengine"
	"google.golang.org/appengine/datastore"
	"google.golang.org/appengine/user"
)

type Employee struct {
	Name     string
	Role     string
	HireDate time.Time
	Account  string
}

func handle(w http.ResponseWriter, r *http.Request) {
	ctx := appengine.NewContext(r)

	e1 := Employee{
		Name:     "Joe Citizen",
		Role:     "Manager",
		HireDate: time.Now(),
		Account:  user.Current(ctx).String(),
	}

	key, err := datastore.Put(ctx, datastore.NewIncompleteKey(ctx, "employee", nil), &e1)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	var e2 Employee
	if err = datastore.Get(ctx, key, &e2); err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	fmt.Fprintf(w, "Stored and retrieved the Employee named %q", e2.Name)
}

詳細については、Datastore リファレンスをご覧ください。

このページは役立ちましたか?評価をお願いいたします。

フィードバックを送信...

Go の App Engine スタンダード環境