LoginSignup
20
10

More than 5 years have passed since last update.

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

Last updated at Posted at 2018-12-07

はじめに

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

さいごに

エラーを見ただけでは直感的には分かりにくく、解決に時間がかかることもあったためまとめてみました。
同じエラーに遭遇した際に、本記事を見ることで少しでも助けになれば幸いです。

20
10
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
20
10