Firebase で無料枠を超えたら Firestore を自動的に無効にする
個人開発ではあまり課金したくないですよね
ただし、Firebase の場合は、Functions を使えば Blaze プラン(従量課金)への移行が必須で、Firestore を使えば割とすぐに無料枠を超過します
もし、アプリケーションを公開してると F5/Command+R 連打される危険性もあります
寝てる時にされたら終了です 😇
実際私は以前、無料枠を超過したことがあって、26 円払いました
今は円安なので、26 円じゃあ済まされないですね
ちなみにこの時はローカルで開発してて、Firestore 叩きまくってたら読み取り 5 万件/日
を超えてました
料金プランは以下を参考
本題ですが、今回作った「Firestore を無効にするプログラム」は以下の流れで動きます
- GCP の予算アラートが発動
- Pub/Sub トピックに接続
- 予算アラート内の設定で指定可能
- Pub/Sub トリガーから実行される Cloud Functions 内で Firestore を無効化する
以下注意点です
- この方法では課金を 0 円にすることはできません
- 予算アラート発動から Firestore を無効化するまで、使われた分は課金されます
- 無効化されると Firestore へのリクエストはすべてエラーになるので、アプリケーションを公開している場合は注意してください
また、Firestore の無効化に関して実際に行う作業は、Google App Engine を無効化することです
GAE アプリを無効化することで、それに紐づく Firestore へのアクセスが無効になるという流れです
手動で GAE アプリを無効化する場合は以下から行えます
こちらに書かれている通り、無効化しても Firestore へのリクエストは停止されますが、データはなくなりません(保存済みのデータの課金も無料枠を超えていれば発生します)
予算アラートの作成
GCP コンソールのお支払い -> 予算とアラート -> 予算を作成
から作成できます
とりあえず私は 1 円でも使われたらアラート飛ばす設定にしています
最後に通知の種類を選べるので、ここで Pub/Sub トピックと紐付けてください
もし、トピックがない場合でもこの画面から作成できます
Cloud Functions for Firebase の作成
必要なパッケージをインストールします
npm i firebase-functions googleapis
環境変数をセットします
firebase functions:config:set pubsub.topic="[先程作成したPub/Subトピック名]"
以下が関数の実装(TypeScript)です
実装例は Python ですが以下にあります
import * as functions from 'firebase-functions'
import { google } from 'googleapis'
export const disableApp = functions.pubsub
.topic((functions.config() as { pubsub: { topic: string } }).pubsub.topic)
.onPublish(async m => {
const data = JSON.parse(Buffer.from(m.data, 'base64').toString()) as { costAmount: number; budgetAmount: number }
if (data.costAmount <= data.budgetAmount) {
console.info(`No action necessary. (Current cost: ${data.costAmount})`)
return null
}
const auth = new google.auth.GoogleAuth({
scopes: ['https://www.googleapis.com/auth/cloud-platform']
})
const authClient = await auth.getClient()
const projectId = await auth.getProjectId()
await google.appengine('v1').apps.patch({
auth: authClient,
appsId: projectId,
updateMask: 'serving_status',
requestBody: { servingStatus: 'USER_DISABLED' }
})
console.info(`App ${projectId} disabled`)
return null
})
デプロイします
firebase deploy --only functions
動作確認
gcloud を使って、作成した Pub/Sub トピックに publish します
costAmount と budgetAmount 以外はたぶん適当な値でオッケーです
gcloud pubsub topics publish [先程作成したPub/Subトピック名] --message '{
"budgetDisplayName": "aaaaa",
"alertThresholdExceeded": 1.0,
"costAmount": 100.01,
"costIntervalStart": "2019-01-01T00:00:00Z",
"budgetAmount": 100.00,
"budgetAmountType": "SPECIFIED_AMOUNT",
"currencyCode": "USD"
}'
再度、GAE の設定画面を確認し、以下の画像のように無効化されていれば成功です
再開する場合は、「アプリケーションを有効にする」ボタンを押せばオッケーです
私の環境の場合は有効化後に数秒で使えるようになっていました
また、無効化後に Firebase JS SDK 経由で Firestore を参照すると以下のエラーになります
Could not reach Cloud Firestore backend. Connection failed 1 times. Most recent error: FirebaseError: [code=permission-denied]: The project was disabled or deleted.
This typically indicates that your device does not have a healthy Internet connection at the moment. The client will operate in offline mode until it is able to successfully connect to the backend.
参考
Discussion