firebase-functions
firebase-functions copied to clipboard
Firebase Cloud Functions V2: setGlobalOptions Does Not Apply Region Correctly
Related issues
[REQUIRED] Version info
node: v18.7.0
firebase-functions: v4.5.0
firebase-tools:
firebase-admin: v11.11.1
[REQUIRED] Test case
In Firebase Cloud Functions V2, when using setGlobalOptions to set the region to asia-northeast1, some Firestore trigger functions are still being initialized in the default region us-central1.
[REQUIRED] Steps to reproduce
- Set up a Firebase Cloud Function using
setGlobalOptionsto specify the region asasia-northeast1. - Define a Firestore trigger function in the Firebase Functions codebase.
- Deploy the project using Firebase CLI.
- Check the Firebase CLI logs to see in which region the function has been initialized.
- Notice that the function is initialized in
us-central1instead of the specifiedasia-northeast1.
[REQUIRED] Expected behavior
All functions should respect the global options set, especially regarding the region. The expected behavior is that functions are initialized in the specified region asia-northeast1.
[REQUIRED] Actual behavior
Functions such as Firestore triggers are being initialized in the us-central1 region, despite the setGlobalOptions specifying asia-northeast1.
code
[functions/index.js]
import { initializeApp, applicationDefault } from 'firebase-admin/app';
import { setGlobalOptions } from 'firebase-functions/v2';
initializeApp({
credential: applicationDefault(),
});
setGlobalOptions({
region: 'asia-northeast1',
timeoutSeconds: 540,
memory: '2GiB',
minInstances: 0,
maxInstances: 5,
concurrency: 2,
});
export * as v1 from '#root/v1/index.js';
[functions/v1/index.js]
import * as patch from '#root/v1/patch/index.js';
export {
// auth,
// firestore,
// storage,
// https,
patch,
};
[functions/v1/patch/index.js]
import { onDocumentUpdated } from 'firebase-functions/v2/firestore';
export const onCreateManagementTemplate = onDocumentUpdated(
{
document: '...',
},
async (event) => {
//
},
);
log
✔ functions: Loaded functions definitions from source: v1.patch.onCreateManagementTemplate.
✔ functions[us-central1-v1-patch-onCreateManagementTemplate]: firestore function initialized.
Were you able to successfully deploy your functions?
Yes, the functions were deployed successfully, but the regional setting was not applied as expected.
I couldn't figure out how to label this issue, so I've labeled it for a human to triage. Hang tight.
Hello @masanori-iwata. Is the issue that functions in another file don't get the region settings applied?
While not the original author, I'm experiencing the same. And yes, for me I set setGlobalOptions in the index file but the functions themselves are in multiple other files.
Hello @masanori-iwata. Is the issue that functions in another file don't get the region settings applied?
Yes. I set it on a top level file but it did not work on another files.
@masanori-iwata can you give the version of firebase-tools you are using by running the below command:
firebase --version
firebase --version
the version is 13.0.2.
I cannot reproduce this issue, unfortunately. Here's what I tried:
// src/index.ts
import { setGlobalOptions } from "firebase-functions/v2/options";
import * as admin from "firebase-admin";
import { onRequest } from "firebase-functions/v2/https";
admin.initializeApp({
credential: admin.credential.applicationDefault(),
});
setGlobalOptions({ region: "asia-south1", maxInstances: 10 });
export const helloWorld = onRequest((_, res) => {
res.send("Hello from Firebase!");
});
export * from "./more-functions";
// src/more-functions.ts
import { onRequest } from "firebase-functions/v2/https";
import { onDocumentWritten } from "firebase-functions/v2/firestore";
export const anotherHelloWorld = onRequest((_, res) => {
res.send("Hello from another Firebase function!");
});
export const firestoreTrigger = onDocumentWritten(
"users/{userId}",
(context) => {
console.log("New user:", context.data?.after.data());
}
);
I get the following in the terminal after running firebase deploy --only functions:
✔ functions[helloWorld(asia-south1)] Successful update operation.
✔ functions[anotherHelloWorld(asia-south1)] Successful update operation.
✔ functions[firestoreTrigger(asia-south1)] Successful create operation.
As you can see in the first file, I have set the global options only once, but it applies to all functions. Do note you have to select the region for V1 functions separately as setGlobalOptions is specific to V2.
Hey @exaby73, I found some interesting results:
- If you copy code structure provided by @masanori-iwata in
Javascript(notTypescript) you can reproduce the issue. - The issue is not produced in any typescript code
- If you converted the project to use
Javascriptand use the code sample provided here you can reproduce the issue. - Looks like in
Javascriptproject,setGlobalOptionsonly works for functions defined in the same file:
@OutdatedGuy thanks for this input! I can reproduce it with a JS project. I'm guessing in TS, all files are bundled into a single file by tsc which is why setGlobalOptions works there, but with different files, it doesn't work. A workaround is to have setGlobalOptions in each file. I found that having a shared function call at the top of each file works:
// src/options.js
import { setGlobalOptions } from "firebase-functions/v2/options";
export function configureOptions() {
setGlobalOptions({ region: "asia-south1", maxInstances: 10 });
}
That function can be called at the top of each file so that there is a single source of truth for global options
Experiencing the same issue, using Typescript.
index.ts:
initializeApp()
setGlobalOptions({ region: 'europe-north1' })
exports.backdrop = {
...
}
Region is not applied to the functions when deployed.
Unfortunately, using the workaround suggested by @exaby73 will result in the following errors:
{"severity":"WARNING","message":"Calling setGlobalOptions twice leads to undefined behavior"}
{"severity":"WARNING","message":"Calling setGlobalOptions twice leads to undefined behavior"}
{"severity":"WARNING","message":"Calling setGlobalOptions twice leads to undefined behavior"}
{"severity":"WARNING","message":"Calling setGlobalOptions twice leads to undefined behavior"}
{"severity":"WARNING","message":"Calling setGlobalOptions twice leads to undefined behavior"}
{"severity":"WARNING","message":"Calling setGlobalOptions twice leads to undefined behavior"}
@OskarGroth, it shouldn't be an issue as long as the options are the same (which is why I recommended having a global function called). But yes, I agree this is not ideal in the slightest.
I'm experiencing the same, despite bundling to a single file (from TypeScript). It fails when doing setGlobalOptions in the top-level index-file (which re-exports functions from discrete files), and it works then it is done from within the discrete files.
Transpiled and bundled output that works:
(top level) ...
setGlobalOptions({ region: "europe-north1" });
const myFunctionName = onRequest({ cors: !0 }, (t, e) => {
console.log("Hello world", t.body), e.send("OK");
});
export {
myFunctionName
};
Bundled output that fails:
(top level) ...
const myFunctionName = onRequest({ cors: !0 }, (t, e) => {
console.log("Hello world", t.body), e.send("OK");
});
setGlobalOptions({ region: "europe-north1" });
export {
myFunctionName
};
Seems like the order here matters, and the way (Vite) bundles is the real problem (for me).
I would think that vite wouldn't mess with the ordering of the code. Can you give an example 2 files I can pop into a new generated TS project to try and investigate what is going on?
I just stumbled across this issue when I wanted to deploy a document trigger with onDocumentUpdated and got the message A firestore trigger location must match the firestore database region. from here https://github.com/firebase/firebase-tools/blob/master/src/deploy/functions/services/firestore.ts#L40.
I didn't get this before, so I wonder why it changed now. I restored the database via terraform scripts. Maybe this changes something?
@GerroDen Could you check which region your database is from the Google Cloud Console
@exaby73 Google Cloud Console says like Firebase Console europe-west1. Which is also what I set as global options:
setGlobalOptions({
region: "europe-west1",
maxInstances: 10,
timeoutSeconds: 540,
memory: "256MiB",
});
FWIW, I could not get setGlobalOptions in my index file to apply to all my functions when using ES6, but with CommonJS it works just fine.
We are seeing the same issue after migrating the TypeScript project from CommonJS to ES Modules. This was working fine before in index.ts:
setGlobalOptions({ region: 'europe-west1' })
export * from './functions'
but now with ESM the global options are not applied. Going back to CommonJS is not a solution, unfortunately.
I ended up replacing setGlobalOptions and explicitly set the region for all functions individually.
I ended up replacing setGlobalOptions and explicitly set the region for all functions individually.
I fear we have to go the same route but setGlobalOptions not working in ES modules is something that Google should have a look hat imho 👀
I tried to use dynamic imports. Interestingly the region is applied for functions imported dynamically but then unfortunately all functions have a default- prefix, like default-function1 and default-function2.
export default {
...(await import('./function1.js')),
...(await import('./function2.js')),
}
It's possible to work around this by exporting each function separately, however it becomes quite tedious if you have many functions and/or your modules export more than just one function.
export const function1 = (await import('./function1.js')).function1
export const function2 = (await import('./function1.js')).function2
When migrating from CommonJS to ES Modules (ECMAScript) in a Firebase Functions project using TypeScript, I had the same issue where setGlobalOptions() from "firebase-functions/v2/options" was not being applied properly.
After switching to ES Modules, my Cloud Functions no longer respected the global configuration options set using:
import { setGlobalOptions } from "firebase-functions/v2/options";
setGlobalOptions({
region: "asia-south1",
timeoutSeconds: 540,
});
The issue seems to be related to the evaluation order of module imports in ES Modules. Since ES Modules execute imports before any statements in the main file, some Firebase functions were being defined before setGlobalOptions() was applied.
Solution that worked for me: Using dynamic imports (await import()) for function handlers ensures setGlobalOptions() is applied before any function is registered.
Fixed Code (TypeScript + ES Modules) Compatible with Firebase Functions v2 and modern ES Module syntax [functions/index.ts]
import { setGlobalOptions } from "firebase-functions/v2/options";
import { initializeApp } from "firebase-admin/app";
// Initialize Firebase Admin
initializeApp();
// Set global options for Cloud Functions
setGlobalOptions({
region: "us-central1",
timeoutSeconds: 540,
});
// Dynamically import functions
const { sampleFunction1 } = await import("./path/to/sampleFunction1.js");
const { sampleFunction2 } = await import("./path/to/sampleFunction2.js");
// Export functions
export { sampleFunction1, sampleFunction2 };
Solution that worked for me: Using dynamic imports (await import()) for function handlers ensures setGlobalOptions() is applied before any function is registered.
Well, that's exactly what I wrote above 😉 However this solution becomes impractical when you have many Cloud Functions.
Any update on this? I'm also having the same issue after migrating from CommonJS to ES Modules