firebase / firebase-functions

Firebase SDK for Cloud Functions
https://firebase.google.com/docs/functions/
MIT License
1.01k stars 201 forks source link

Firebase Cloud Functions V2: setGlobalOptions Does Not Apply Region Correctly #1502

Open masanori-iwata opened 6 months ago

masanori-iwata commented 6 months ago

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

  1. Set up a Firebase Cloud Function using setGlobalOptions to specify the region as asia-northeast1.
  2. Define a Firestore trigger function in the Firebase Functions codebase.
  3. Deploy the project using Firebase CLI.
  4. Check the Firebase CLI logs to see in which region the function has been initialized.
  5. Notice that the function is initialized in us-central1 instead of the specified asia-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.

google-oss-bot commented 6 months ago

I couldn't figure out how to label this issue, so I've labeled it for a human to triage. Hang tight.

exaby73 commented 5 months ago

Hello @masanori-iwata. Is the issue that functions in another file don't get the region settings applied?

fwal commented 5 months ago

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.

masanori-iwata commented 5 months ago

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.

OutdatedGuy commented 5 months ago

@masanori-iwata can you give the version of firebase-tools you are using by running the below command:

firebase --version
masanori-iwata commented 5 months ago
firebase --version

the version is 13.0.2.

exaby73 commented 4 months ago

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.

OutdatedGuy commented 4 months ago

Hey @exaby73, I found some interesting results:

  1. If you copy code structure provided by @masanori-iwata in Javascript (not Typescript) you can reproduce the issue.
  2. The issue is not produced in any typescript code
  3. If you converted the project to use Javascript and use the code sample provided here you can reproduce the issue.
  4. Looks like in Javascript project, setGlobalOptions only works for functions defined in the same file: Screenshot 2024-02-07 at 7 33 05 PM
exaby73 commented 4 months ago

@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

OskarGroth commented 2 months ago

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"}
exaby73 commented 2 months ago

@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.