From 2d03226c5f18fe55d2c5432138c0d156f48d5616 Mon Sep 17 00:00:00 2001 From: Daniel Lee Date: Fri, 8 Dec 2023 07:26:09 +0900 Subject: [PATCH] Memoize firestore.getDatabase API calls when deploying Firestore functions (#6583) During deployment of a 2nd Gen Firestore functions, the CLI retrieves metadata associated with the Firestore database in order to ensure that the region of the Firestore database matches the region of the deployed function: https://github.com/firebase/firebase-tools/blob/1f4f6f494fd7a8a29887a8306930cda56aa7e13b/src/deploy/functions/services/firestore.ts#L9-L15 Unfortunately, when a large number of functions are deployed that each target a different Firestore instance (you are allowed to have multiple instances of Firestore on a single GCP project), developer will likely see a quota exceeds error: ``` Quota exceeded for quota metric 'Database operation requests' and limit 'Database Operations Per Minute' of service 'firestore.googleapis.com' for consumer ... ``` We mitigate the issue by memoizing the API calls. This should help with cases where multiple firestore functions are associated with a database, but wouldn't help if developer is setting up and deploying 60+ unique functions. Mitigates https://github.com/firebase/firebase-tools/issues/6574 --- CHANGELOG.md | 1 + src/deploy/functions/services/firestore.ts | 20 +++++++++++++++++++- src/gcp/firestore.ts | 2 +- 3 files changed, 21 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 80b2f725d68..ad44b19d754 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,4 @@ +- Fix bug where deploying Firestore function resulted in redudant API calls to the Firestore API (#6583). - Fix an issue preventing Vite applications from being emulated on Windows. (#6411) - Addressed an issue preventing Astro applications from being deployed from Windows. (#5709) - Fixed an issue preventing Angular apps using ng-deploy from being emulated or deployed. (#6584) diff --git a/src/deploy/functions/services/firestore.ts b/src/deploy/functions/services/firestore.ts index d4eb39b45d5..dade0774b8e 100644 --- a/src/deploy/functions/services/firestore.ts +++ b/src/deploy/functions/services/firestore.ts @@ -2,6 +2,24 @@ import * as backend from "../backend"; import * as firestore from "../../../gcp/firestore"; import { FirebaseError } from "../../../error"; +const dbCache = new Map(); + +/** + * A memoized version of firestore.getDatabase that avoids repeated calls to the API. + * + * @param project the project ID + * @param databaseId the database ID or "(default)" + */ +async function getDatabase(project: string, databaseId: string): Promise { + const key = `${project}/${databaseId}`; + if (dbCache.has(key)) { + return dbCache.get(key)!; + } + const db = await firestore.getDatabase(project, databaseId); + dbCache.set(key, db); + return db; +} + /** * Sets a firestore event trigger's region to the firestore database region. * @param endpoint the firestore endpoint @@ -9,7 +27,7 @@ import { FirebaseError } from "../../../error"; export async function ensureFirestoreTriggerRegion( endpoint: backend.Endpoint & backend.EventTriggered ): Promise { - const db = await firestore.getDatabase( + const db = await getDatabase( endpoint.project, endpoint.eventTrigger.eventFilters?.database || "(default)" ); diff --git a/src/gcp/firestore.ts b/src/gcp/firestore.ts index e5897c75f66..c870fa95485 100644 --- a/src/gcp/firestore.ts +++ b/src/gcp/firestore.ts @@ -8,7 +8,7 @@ const apiClient = new Client({ urlPrefix: firestoreOrigin, }); -interface Database { +export interface Database { name: string; uid: string; createTime: string;