Description
Various Firebase functions started reporting the crash: Maximum call stack size exceeded.
The issue started occurring after we deployed Firebase cloud functions to the production environment on March 9 2024 at ~1:30 PM GMT. The last deployment before the issue started appearing was Mar 1 2024 at 12:00 GMT.
Affected Firebase functions are triggered by Cloud Firestore or PubSub, not https.
Related issues
#1527 , but related to https functions
[REQUIRED] Version info
node:
20
firebase-functions:
firebase-functions@npm:4.6.0
reproducing on firebase-functions@npm:4.8.0 too
firebase-tools:
13.4.1
firebase-admin:
firebase-admin@npm:12.0.0
[REQUIRED] Test case
A simple cloud function that sets a value on the document that's changed. The issue appears on many cloud functions that perform Firestore update operations.
import { DocumentSnapshot, Timestamp } from 'firebase-admin/firestore';
import { Change, logger } from 'firebase-functions/v1';
export const processChangedDoc = (change: Change<DocumentSnapshot>) => {
const afterSnapshot: DocumentSnapshot<AnyDocData> = change.after;
const { updateTime } = afterSnapshot;
if (!afterSnapshot?.exists || !updateTime) {
// We have a delete event
logger.debug('doc has no update time');
return Promise.resolve();
}
const afterDocData = afterSnapshot.data();
const beforeSnapshot: DocumentSnapshot<AnyDocData> = change.before;
const beforeDocData = beforeSnapshot.data();
const lastDateUpdated = afterDocData ? afterDocData.xDocDateUpdated : undefined;
// We skip setting dateUpdated only if the last update was less than one hour ago
const skipUpdate = !!lastDateUpdated && updateTime.seconds < lastDateUpdated.seconds + ONE_HOUR_SECONDS;
if (skipUpdate) {
logger.debug(`Skip update: lastUpdate: ${lastDateUpdated.toDate().toISOString()}, updateTime: ${updateTime.toDate().toISOString()}`);
return Promise.resolve();
}
const docChange = {} as any;
docChange[DocDateUpdatedKey] = updateTime;
logger.debug(`doc update: before ${beforeDocData?.xDocDateUpdated?.toDate().toISOString()}, after ${updateTime.toDate().toISOString()},`);
return afterSnapshot.ref.update(docChange).catch((err) => console.error(err));
};
export default region('europe-west1')
.firestore.document('{collection}/{docId}')
.onWrite((change) => {
return processChangedDoc(change);
});
[REQUIRED] Steps to reproduce
Deploy FCF. After a few hours, errors start appearing in Google Error Reporting. This only happens in the production environment where there's more activity (e.g. 600k invocations per 24hrs).
[REQUIRED] Expected behavior
FCF should not crash.
[REQUIRED] Actual behavior
Error: RangeError: Maximum call stack size exceeded
at write (/workspace/node_modules/firebase-functions/lib/logger/index.js:66:74)
at Object.debug (/workspace/node_modules/firebase-functions/lib/logger/index.js:76:5)
at processChangedDoc (/workspace/lib/services/processChangedDocument.js:38:17)
at /workspace/lib/api/db/all/levelOne.js:8:59
at /workspace/node_modules/firebase-functions/lib/common/onInit.js:33:16
at /workspace/node_modules/firebase-functions/lib/common/onInit.js:33:16
at /workspace/node_modules/firebase-functions/lib/common/onInit.js:33:16
at /workspace/node_modules/firebase-functions/lib/common/onInit.js:33:16
Error: RangeError: Maximum call stack size exceeded
at get parent [as parent] (/workspace/node_modules/@google-cloud/firestore/build/src/reference.js:216:15)
at processChangedDoc (/workspace/lib/services/processChangedDocument.js:17:73)
at /workspace/lib/api/db/all/levelOne.js:8:59
at /workspace/node_modules/firebase-functions/lib/common/onInit.js:33:16
at /workspace/node_modules/firebase-functions/lib/common/onInit.js:33:16
at /workspace/node_modules/firebase-functions/lib/common/onInit.js:33:16
at /workspace/node_modules/firebase-functions/lib/common/onInit.js:33:16
at /workspace/node_modules/firebase-functions/lib/common/onInit.js:33:16
at /workspace/node_modules/firebase-functions/lib/common/onInit.js:33:16
at /workspace/node_modules/firebase-functions/lib/common/onInit.js:33:16
at entryFromArgs (/workspace/node_modules/firebase-functions/lib/logger/index.js:130:19)
at Object.error (/workspace/node_modules/firebase-functions/lib/logger/index.js:116:11)
at console.error (/workspace/lib/services/logging.js:15:28)
at sendCrashResponse (/layers/google.nodejs.functions-framework/functions-framework/node_modules/@google-cloud/functions-framework/build/src/logger.js:27:17)
at sendResponse (/layers/google.nodejs.functions-framework/functions-framework/node_modules/@google-cloud/functions-framework/build/src/invoker.js:37:40)
at /layers/google.nodejs.functions-framework/functions-framework/node_modules/@google-cloud/functions-framework/build/src/function_wrappers.js:34:40
at bound (node:domain:432:15)
at runBound (node:domain:443:12)
at /layers/google.nodejs.functions-framework/functions-framework/node_modules/@google-cloud/functions-framework/build/src/function_wrappers.js:142:60
at process.processTicksAndRejections (node:internal/process/task_queues:95:5)
Were you able to successfully deploy your functions?
Yes
No