はじめに
GCPのCloud Datastore利用時にハマったエラーについてまとめてみました。
エラーから逆引きできるようにエラーを節名にしています。
今回GAEは使用していないのでご注意ください。
- 今回利用したdatastore用struct
type Entity struct {
Value string `datastore:"-"`
NoIndexValue string `datastore:",noindex"`
Sort string `datastore:"-"`
}
cannot write more than 500 entities in a single call
-
原因
- エラーにも書いてあるように500件以上の書き込み
- 500件以上の読み込みの際にも似たようなエラーが発生
-
対策
- 1リクエスト500件以内に収める
-
ヒトコト
- 1リクエスト制限には件数だけでなく、リクエストサイズの制限もあるため注意
-
再現サンプルコード
func main() {
ctx := context.Background()
dsClient, err := datastore.NewClient(ctx, "")
if err != nil {
panic(err)
}
//num := 500 // OK 500件以内の場合はエラーが発生せず
num := 501 // NG
keys := make([]*datastore.Key, num)
putEntities := make([]*Entity, num)
for i := range keys {
str := strconv.Itoa(i)
keys[i] = datastore.NameKey("Entity", str, nil)
putEntities[i] = &Entity{Value:str}
}
if _, err := dsClient.PutMulti(ctx, keys, putEntities); err != nil {
// ここでエラー
// rpc error: code = InvalidArgument desc = cannot write more than 500 entities in a single call
panic(err)
}
}
operating on too many entity groups in a single transaction.
-
原因
- 1トランザクション内で、26以上のエンティティを変更した
-
対策
- 1トランザクション、25以内に抑える
-
ヒトコト
- エラー内容に具体的な数値が分かりにくいため注意
-
再現サンプルコード
func main() {
ctx := context.Background()
dsClient, err := datastore.NewClient(ctx, "")
if err != nil {
panic(err)
}
//num := 25 // OK 25件以内の場合はエラーが発生せず
num := 26 // NG
// データ更新
tx, err := dsClient.NewTransaction(ctx)
if err != nil {
panic(err)
}
for i:=0; i<num; i++ {
str := strconv.Itoa(i)
key := datastore.NameKey("Entity", str, nil)
e := new(Entity)
if err := tx.Get(key, e); err != nil {
// この箇所でエラー
// rpc error: code = InvalidArgument desc = operating on too many entity groups in a single transaction.
panic(err)
}
e.Value = e.Value + "update"
if _, err := tx.Put(key, e); err != nil {
panic(err)
}
}
if _, err := tx.Commit(); err!=nil {
panic(err)
}
}
datastore: string property too long to index for a Property with Name "XXX"
-
原因
- インデックス有りの項目に対し、1500 Byteを超えて書き込もうとした
-
対策
- 1500Byte以内に抑える
- インデックス無しの項目に変更する
Value string `datastore:",noindex"`
-
ヒトコト
- これもエラー内容に具体的な数値が分かりにくいため注意
-
再現サンプルコード
func main() {
ctx := context.Background()
dsClient, err := datastore.NewClient(ctx, "")
if err != nil {
panic(err)
}
strLength := 1501 // NG
//strLength := 1500 // OK
k := datastore.NameKey("Entity", "stringID", nil)
e := &Entity{
Value: strings.Repeat(" ", strLength),
}
if _, err := dsClient.Put(ctx, k, e); err != nil {
// datastore: string property too long to index for a Property with Name "Value"
panic(err)
}
}
The value of property "Value" is longer than 1048487 bytes.
-
原因
- 1つの項目に1MBを超えて書き込もうとした
-
対策
- 1MB以内に抑える
-
ヒトコト
- datastoreのエミュレータではこのエラーなく登録できるので注意
-
再現サンプルコード
func main() {
ctx := context.Background()
dsClient, err := datastore.NewClient(ctx, "")
if err != nil {
panic(err)
}
//strLength := 1048488 // NG
strLength := 1048400 // OK
k := datastore.NameKey("Entity", "stringID", nil)
e := &Entity{
NoIndexValue: strings.Repeat(" ", strLength),
}
if _, err := dsClient.Put(ctx, k, e); err != nil {
// rpc error: code = InvalidArgument desc = The value of property "Value" is longer than 1048487 bytes.
panic(err)
}
}
no matching index found.
-
原因
- 複数のkeyでのソートや、絞り込みとソートを行った
-
対策
- 複合インデックスを追加(
gcloud datastore create-indexes
コマンドを実行)
- 複合インデックスを追加(
-
ヒトコト
- Datastoreエミュレータを利用している場合には
WEB-INF/index.yaml
に複合インデックス用の定義ファイルが出力される
- Datastoreエミュレータを利用している場合には
-
再現サンプルコード
func main() {
ctx := context.Background()
dsClient, err := datastore.NewClient(ctx, "")
if err != nil {
panic(err)
}
q := datastore.NewQuery("Entity").
Filter("Value =", "1").
Order("Sort")
var resultEntities []*Entity
if _, err := dsClient.GetAll(ctx, q, &resultEntities); err != nil {
// rpc error: code = FailedPrecondition desc = no matching index found. recommended index is:
// - kind: Entity
// properties:
// - name: Value
// - name: Sort
panic(err)
}
for _, entity := range resultEntities {
fmt.Printf("%#v\n", entity)
}
}
func main() {
ctx := context.Background()
dsClient, err := datastore.NewClient(ctx, "")
if err != nil {
panic(err)
}
q := datastore.NewQuery("Entity").
Order("Sort").
Order("Value")
var resultEntities []*Entity
if _, err := dsClient.GetAll(ctx, q, &resultEntities); err != nil {
// rpc error: code = FailedPrecondition desc = no matching index found. recommended index is:
// - kind: Entity
// properties:
// - name: Sort
// - name: Value
panic(err)
}
for _, entity := range resultEntities {
fmt.Printf("%#v\n", entity)
}
}
さいごに
エラーを見ただけでは直感的には分かりにくく、解決に時間がかかることもあったためまとめてみました。
同じエラーに遭遇した際に、本記事を見ることで少しでも助けになれば幸いです。