Firestoreのbatch制限に関するトラップ対策
こんにちは。virapture株式会社のもぐめっとです。
最近人生のイベントがあって友人がたくさん写真をとってくれたので自分の写真のネタには困らないです。
今回zenn.dev初公開です!!
今回のお話はfirestoreのbatchは500個までしか書き込み出来ないというのは有名な話なのですが、なぜか255個しか書き込んでないのに下記エラーが出たのでその原因と対策の軽いメモです。
INVALID_ARGUMENT: maximum 500 writes allowed per request
原因
原因はちゃんとマニュアルに書いてありました。。。
一括書き込みでは最大 500 件のオペレーションを実行できます。バッチ内の各オペレーションは別々に Firestore の使用量として計上されます。書き込みオペレーションでは、serverTimestamp、arrayUnion、increment などのフィールド変換がそれぞれ追加オペレーションとして計上されます。
ドキュメント1個に対して1オペレーションだけではなく、その中にserverTimestamp、arrayUnion、incrementが入ってたらそれも追加オペレーションとして計上されるというものでした。
これ結構知らない人いるんじゃないかと思ったのですが、、、もぐめっとだけですかね?
対策
今回はiosでの実装でしたが、書き込みするEntityに対してFieldValueのプロパティ分の数だけカウントを返すような定数をとりあえず返すことで、batch書く時はそれを考慮しながらコミットする形に修正しました。
struct Vote: FirestoreCodable {
let createdAt: FieldValue
var updatedAt: FieldValue
let voteName: String
static let writeOperationCount: Int = 2
}
func createVotes(votes: [Vote], completion: @escaping CompletionCallback) {
let batchMaxCount = 500
let batch = Firestore.firestore().batch()
var tmpVotes = votes
let maxWriteCount = batchMaxCount / (Vote.writeOperationCount + 1) // Voteのoeperation数を定義したのを用いる
for _ in 0 ..< maxWriteCount {
guard let vote = tmpVotes.popLast(),
let voteData = try? Firestore.Encoder().encode(vote)
else { break }
let reference = Firestore.firestore().collection("votes").document()
batch.setData(voteData, forDocument: reference)
}
batch.commit { [weak self] error in
if let error = error {
completion(.failure(error))
return
}
// まだ未コミットがあれば再帰的に処理する
if !tmpVotes.isEmpty {
self?.createVotes(votes: tmpVotes, completion: completion)
return
}
completion(.success(nil))
}
}
ただ、Vote.swiftがこのままではFieldValueのプロパティが追加される度に定数を更新しないといけないみたいな感じになってるので、リフレクションとか使って、型をチェックしてその数を数えるみたいな感じにするとより良い実装になるかなぁと思いました。
まとめ
serverTimestamp、arrayUnion、incrementも書き込みの数として計上する!!
[最後に宣伝]
こんなサービス作ってるのでよかったら使ってみてください!
ワンナイト人狼 for mobile
ワンナイト人狼オンライン(ガッツリfirebase使ってます。)
気軽に投稿できるフォトコンサイト - Camecon
連絡先を交換しなくてもグルチャができる - Offcha
Discussion