FirestoreのCountクエリでコストとパフォーマンスは改善できるのか
この記事は シェア買いアプリ「カウシェ」 Advent Calendar 2022 の7日目の記事です。
こんにちは。株式会社カウシェでバックエンドエンジニアをしている@uoです。
Google Cloud Next ‘22でFirestoreのCount機能が発表されました!
以前書いた記事のように今まではFirestoreでCountするには考慮することが多かったため、嬉しい機能になります!
カウシェではGo言語を採用しているのですが、Go言語ではcloud.google.com/go/firestore v1.8.0からCount機能を使えるようになっています。
これまでのカウント方法
これまではFirestoreのドキュメント数をカウントする場合、対象のドキュメントを全て取得してクライアント側でカウントを行う必要がありました。
この方法には以下のような問題があります。
- データ件数が多い場合にデータの取得に時間がかかる
- データの読み取りに料金がかかる
カウシェではこれらの問題を回避するために事前にカウントをしてFirestoreに保存する、という方法を使っています。
ただ、事前にカウントする機能の実装が必要だったり、実際の件数とカウントした値のズレをどう防ぐか考慮する必要がある、といった手間がありました。
Countクエリはこれらの問題の解決策になり得るため、ずっと期待していた機能でした!
Countクエリを使ってみる
実際にCountクエリを使ってデータ件数を取得してみます。言語はGoを使っています。
Usersコレクションからアクティブなユーザーを取得する場合のコードは以下のようになります。
func count(ctx context.Context, client *firestore.Client) error {
q := client.Collection("Users").Where("Status", "==", "active")
// firestore.Queryからfirestore.AggregationQueryを生成
aq := q.NewAggregationQuery()
// AggregationQueryのOperatorにCountを設定し、Aliasをつける
aq := aq.WithCount("activeUserCount")
// AggregationQueryを実行
res, err := aq.Get(ctx)
if err != nil {
return err
}
// 結果を取得する
count, ok := res["activeUserCount"]
if !ok {
return fmt.Errorf("aggregation query key not found")
}
cv := count.(*pb.Value)
fmt.Printf("Active User Count: %d", cv.GetIntegerValue())
return nil
}
- QueryからAggregationQueryを生成する
- AggregationQueryのOperatorにCountを設定する
- Getする
これだけでデータ件数を取得することができます!
FirestoreのAggregationについて
Firestoreのprotoの定義を確認すると、1つのクエリに対してAggregationは最大5件設定できるようになっています。
// Optional. Series of aggregations to apply over the results of the `structured_query`.
//
// Requires:
//
// * A minimum of one and maximum of five aggregations per query.
repeated Aggregation aggregations = 3 [(google.api.field_behavior) = OPTIONAL];
今はCountしか使えないですが、今後は1つのクエリに対してMaxやSumといった集約クエリを同時に実行することを想定していると考えられます。
期待が高まりますね!
Countの料金
Countの料金は、「一致したインデックス エントリの数」に対して課金されます。
インデックスエントリの一致が1000件で、1回のドキュメント読み込み相当の課金が行われます。
これまでは1000回のドキュメント読み込みが発生していたのが1回分の読み込みで済むので、コスト面でのCountの効果は大きいです。
Countのパフォーマンス
既存のカウント方法とCountクエリを利用した場合のデータの取得時間を調べてみました。
以下は10回カウントを取得した時の平均の取得時間です。
既存のカウント方法と比べるとパフォーマンスが大きく改善されています!
ただし、データ件数が増えると処理時間も増えるので、アプリから呼ばれるような同期的なAPIでCountクエリを使う場合には注意が必要になりそうです。
最後に
Firestoreに追加されたCountクエリについて調べてみました。
今までと比べると、簡単でコスト効率も良くカウントができるようになっています!
データ量が多く、パフォーマンスを考慮する必要がある場合はDistributed countersを選択する必要があると思いますが、それ以外ならCountクエリが使えそうだと感じました。
まだPreview版の機能なので、GA版を楽しみに待ちたいと思います!
カウシェでは、共に「世界一楽しいショッピング体験をつくる」メンバーを募集中
少しでも気になった方は、ぜひ下記よりエントリーいただければと思います。
参考
- https://cloud.google.com/blog/products/databases/aggregating-data-with-firestore
- https://firebase.google.com/docs/firestore/query-data/aggregation-queries