みなさん、こんにちは!
今回は「Firestoreのトランザクションで排他制御っぽいこと(同時更新対策)をする方法」について、お伝えしていきたいと思います。
公式ドキュメントと実務での経験を基にお伝えしていきます。
Firestoreでデータ不整合が起こるケースについてはこちらの記事をご参照ください。
【Firestore】arrayUnion、arrayRemoveを使って、Firestoreで配列への要素追加(削除)
Contents
Firestoreの主なデータ取得、更新処理
get : ドキュメントを取得する
set : 新しくドキュメントを追加する or 既存のドキュメントを丸ごと上書きする
update : フィールドの一部を追加・変更する
※クエリ等は除く
配列への要素の追加・削除、数値の増加の場合
これらの場合には、各メソッドが用意されています。
arrayUnion
:配列に要素を追加
arrayRemove
:配列から要素を削除
increment
:数値の増加
具体的にはこんなケースです。
userID一覧の配列フィールドにIDを追加したい
いいね数のようなカウントフィールドに+1(-1)したい
ただし、乗算・除算が必要なケースは以降のトランザクションを使う必要があります。
JavaScriptでの書き方の例です。
// 配列に要素を追加
const docRef = db.collection('class').doc('class10');
docRef.update({
students: firebase.firestore.FieldValue.arrayUnion(127),
}).then(() => {
console.log('更新完了');
});
// 数値の場合
const docRef = db.collection('class').doc('class20');
docRef.update({
good: firebase.firestore.FieldValue.increment(-1),
}).then(() => {
console.log('更新完了');
});
その他取得したデータを基に加工した後、更新が必要な場合
トランザクションを用いることで、解決できます。
例えば、こんなケースです。
フィールドの値を2倍にしたい
取得したデータと別のデータを突合して再度セットしたいなど
トランザクションについて
Firestoreトランザクションの特徴(公式サイトのほぼ引用ですが...)は以下です。
参考
・アトミックである(アトミックコミットが完了するまでに障害があった場合、"コミット"は中止され、実行された全ての変更は戻される(ロールバックされる))
・一度のトランザクションでは500ドキュメント まで
・クライアントがオフラインの場合、トランザクションは失敗する
・読み取りオペレーションは書き込みオペレーションの前に実行する必要がある
・同時編集が発生した場合はトランザクションの関数が複数回実行される
JavaScriptでの書き方の例です。
// トランザクションを使って、フィールドの値を2倍にする
const docRef = db.collection('class').doc('class10');
return db.runTransaction((transaction) => {
const latestgetData = await transaction.get(docRef);
const latestData = latestgetData.data();
const newNumber = latestData.number * 2;
transaction.update(
docRef,
{number: newNumber},
);
}).then(() => {
console.log('successfully committed!');
}).catch((error) => {
console.log('Transaction failed: ', error);
});