質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.46%
Firebase

Firebaseは、Googleが提供するBasSサービスの一つ。リアルタイム通知可能、並びにアクセス制御ができるオブジェクトデータベース機能を備えます。さらに認証機能、アプリケーションのログ解析機能などの利用も可能です。

JavaScript

JavaScriptは、プログラミング言語のひとつです。ネットスケープコミュニケーションズで開発されました。 開発当初はLiveScriptと呼ばれていましたが、業務提携していたサン・マイクロシステムズが開発したJavaが脚光を浴びていたことから、JavaScriptと改名されました。 動きのあるWebページを作ることを目的に開発されたもので、主要なWebブラウザのほとんどに搭載されています。

Q&A

解決済

1回答

995閲覧

Firebaseのトランザクションで数字のカウントがずれる

abc1222

総合スコア24

Firebase

Firebaseは、Googleが提供するBasSサービスの一つ。リアルタイム通知可能、並びにアクセス制御ができるオブジェクトデータベース機能を備えます。さらに認証機能、アプリケーションのログ解析機能などの利用も可能です。

JavaScript

JavaScriptは、プログラミング言語のひとつです。ネットスケープコミュニケーションズで開発されました。 開発当初はLiveScriptと呼ばれていましたが、業務提携していたサン・マイクロシステムズが開発したJavaが脚光を浴びていたことから、JavaScriptと改名されました。 動きのあるWebページを作ることを目的に開発されたもので、主要なWebブラウザのほとんどに搭載されています。

0グッド

0クリップ

投稿2021/09/12 12:02

編集2021/09/14 15:22

前提・実現したいこと

申請に対して、現在の申請数をチェックし、制限人数以内なら許可、オーバーする場合は不許可を返す関数を作りたいです。

firebaseを使っているため、トランザクションを活用し、上記の関数を作ろうとしているのですが、上手くいきません。

正確には、殆どの場合上手くいくのですが、時々カウントと実際の申請許可数がずれてしまいます。

なにかトランザクションの仕様を見落としているのでしょうか。
コードを見て頂き、見落としている点や原因を絞り込む方法があれば教えて頂きたいです。

データベース構造と起きている現象

コレクション:Number
ドキュメント:id
フィールド:
・max:Int(制限人数)
・number: Int(現在の申請許可数)

申請が4件しか来ていないのに、numberが6になっていることがあります。
Maxを6に設定してた場合、4件しか申請を許可していなのに、申請の許可が止まるため困っています。
毎回ではないのですが、たまに数字がズレ、再現しようとしても条件が分かりません。

該当のソースコード

Javascript

1exports.numberSet = functions.region(REGION).https 2 .onCall(async(data, context) => { 3 4 const numberRef = db.collection("Number").doc(//id); 5 6 try { 7 const withinCapacity = await db.runTransaction(async(transaction) => { 8 const numDoc = await transaction.get(numberRef) 9 if (!numDoc.exists) { 10 throw "Document does not exist!" 11 } 12 const newNumber = numDoc.data().number + 1 13 const max = numDoc.data().max 14 15 if (!Number.isFinite(max) || newNumber <= max) { 16 transaction.update(numberRef, { number: newNumber }) 17 return true 18 } else { 19 return false 20 } 21 }) 22 if (withinCapacity) { 23 return true 24 } else { 25 return false 26 } 27 28 } catch (error) { 29 return error 30 } 31 })

試したこと

いろんな条件で関数を動かしてみても、再現ができません。(正しくカウントしてくれます)
しかし、複数人の友達に触って貰ってみると、たまに数字がずれてエラーを取得できないでいます。

補足情報(FW/ツールのバージョンなど)

関数はcloud Functionで動かしています。
関数を呼び出す申請フォームを友達に触ってもらってみたところ、記載の現象が起きています。

修正依頼を受けての追記

申請と言っても、定員数以内かどうかだけをチェックして、定員数以内ならその場で自動的に許可&クライアント側に許可されましたとその場で表示する仕様です。
定員オーバーなら、falseを返し、その場で定員オーバーと表示されます。
申請フォームを作った人が、手動で許可するといったものではありません

そのため、質問に記載したコードの下記の部分(定員以内の場合の挙動)以外でnumberを+1することはありません。
transaction.update(numberRef, { number: newNumber })

numberを-1することはあるので、質問のコードに追加しておきます
numberを-1するのは、キャンセルが生じた場合です

Javascript

1exports.cancelSet = functions.region(REGION).https 2 .onCall(async(data, context) => { 3 4 const numberRef = db.collection("Number").doc(//id); 5 6 try { 7 return db.runTransaction(async(transaction) => { 8 const numDoc = await transaction.get(numberRef) 9 if (!numDoc.exists) { 10 throw "Document does not exist!" 11 } 12 var newNumber = numDoc.data().number - 1 13 transaction.update(numberRef, { number: newNumber }) 14 }).then(() => { 15 return true 16 }).catch((error) => { 17 return false 18 }); 19 } catch (error) { 20 return error 21 } 22 })

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

gekijin

2021/09/14 06:46

ロジック自体はあっていると思いますが、設計に疑問があります。 「申請が4件しか来ていないのに、numberが6になっていることがあります」とのことですが、「numberを1増やすことと申請が許可状態になること」は対応できているんでしょうか? 1つの申請を許可するのに、numberが2つ増えたりしていませんか? トランザクションを組むのであれば、 【トランザクションスタート】 ・申請の許可フラグが立っていないことを確認する(立っていたら処理を抜ける) ・申請の許可フラグを立てる ・numberを1増やす 【トランザクションコミット】 という作りになっていなければ意味がない気がします。
abc1222

2021/09/14 15:05

ご確認いただきありがとうございます! >「numberを1増やすことと申請が許可状態になること」は対応できているんでしょうか? 申請という言葉がややこしいですね。申し訳ございません 申請と言っても、定員数以内かどうかだけをチェックして、定員数以内ならその場で自動的に許可&クライアント側に許可されましたとその場で表示する仕様です。 申請フォームを作った人が、手動で許可するといったものではありません そのため、質問に記載したコードの下記の部分以外でnumberを+1することはありません。 transaction.update(numberRef, { number: newNumber }) ※numberを-1することはあるので、質問のコードに追加しておきます ※numberを-1するのは、キャンセルが生じた場合です 質問の意図を読み違えていたら申し訳ありません。 プログラミング初心者のため「申請の許可フラグを立てる」という意味がよく分からず、「申請フォームを作った人が手動で許可する」という仕様をイメージされていると思い、上記のような回答をさせていただきました。
gekijin

2021/09/14 15:21

> 申請と言っても、定員数以内かどうかだけをチェックして、定員数以内ならその場で自動的に許可&クライアント側に許可されましたとその場で表示する仕様です あ、想定通りでございます。 私が思ったのは、 ユーザーは1回だけ申請したつもり。だけどリクエストが(=https.onCallが)ボタン連打やらネットワーク不良やらで複数回呼ばれているのではないか?そうすると、カウントは呼ばれた回数だけ増えるけど、ユーザー的には1回しか申請していない。結果、「4回しか申請していないのにカウントが6になってる」が発生しているのではないか? ということです。Webは非同期処理なので、申請をしてそのレスポンスがあるまでの間に、もう1回申請をしてしまうことができます。 これを防ぐには、ボタンを複数回押せなくするUI側の工夫も必要ですが、さらにシステム側で「申請1つに対して絶対に1つだけカウントアップする」という制御が必要になります。 この制御を行うためには、 ・申請をDBに保持して許可フラグをつける。許可フラグを立てる処理とカウントアップする処理をトランザクションにする とか ・申請ごとにユニークなIDをつける。許可した申請はDBなどに保存する。許可するときには、許可した申請一覧に許可する申請のIDがないことを確認して、許可した申請一覧にIDを追加する処理とカウントアップをトランザクションにする などの仕組みが必要になると思います。 私が申請の許可フラグの話を出したのは、こういう理由です。
abc1222

2021/09/14 15:32

なるほど! 私の分かりにくい日本語を読み解いて頂きありがとうございます! そして、記載いただいている「申請1つに対して絶対に1つだけカウントアップする」という制御は未実装でした! 独学のためプログラミングの定石というものが分かっておらず、とても勉強になりました。 記載いただいた具体的な方法については、知識が追いついていないのでこの場で完全理解とまでは行ってないのですが、自分自身で調べながら実装する所までは行けそうです! まずは上記に対応して、カウントのズレが無くなるか様子を見てみます!
abc1222

2021/09/21 02:05

アドバイスいただいた下記の申請フラグを実装した所、数字のズレが無くなりました! ・申請ごとにユニークなIDをつける。許可した申請はDBなどに保存する。許可するときには、許可した申請一覧に許可する申請のIDがないことを確認して、許可した申請一覧にIDを追加する処理とカウントアップをトランザクションにする ベストアンサーに選びたいのですが、修正依頼の方に回答いただいているので、選ぶことが出来ません。 良ければ、ベストアンサーの方にも同じ内容を記載いただけると幸いです。 別に選ばなくて良いよという場合は無視頂ければ、1週間後くらいに自己解決という形で解決済みにします
guest

回答1

0

ベストアンサー

ユーザーは1回だけ申請したつもり。だけどリクエストが(=https.onCallが)ボタン連打やらネットワーク不良やらで複数回呼ばれているのではないか?そうすると、カウントは呼ばれた回数だけ増えるけど、ユーザー的には1回しか申請していない。結果、「4回しか申請していないのにカウントが6になってる」が発生しているのではないでしょうか。

Webは非同期処理なので、申請をしてそのレスポンスがあるまでの間に、もう1回申請をしてしまうことができます。
これを防ぐには、ボタンを複数回押せなくするUI側の工夫も必要ですが、さらにシステム側で「申請1つに対して絶対に1つだけカウントアップする」という制御が必要になります。
この制御を行うためには、
・申請をDBに保持して許可フラグをつける。許可フラグを立てる処理とカウントアップする処理をトランザクションにする
とか
・申請ごとにユニークなIDをつける。許可した申請はDBなどに保存する。許可するときには、許可した申請一覧に許可する申請のIDがないことを確認して、許可した申請一覧にIDを追加する処理とカウントアップをトランザクションにする
などの仕組みが必要になると思います。

投稿2021/09/21 02:20

gekijin

総合スコア187

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.46%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問