Cloud Datastoreで出会った制限系エラー(再現コード有り)


はじめに

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でのソートや、絞り込みとソートを行った




  • 対策




  • ヒトコト


    • Datastoreエミュレータを利用している場合には WEB-INF/index.yaml に複合インデックス用の定義ファイルが出力される



  • 再現サンプルコード


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)
}
}



さいごに

エラーを見ただけでは直感的には分かりにくく、解決に時間がかかることもあったためまとめてみました。

同じエラーに遭遇した際に、本記事を見ることで少しでも助けになれば幸いです。