Firebase Emulator: using PubSub in Functions - firebase

I am trying to run my Firebase functions locally through the emulator. I have built a architecture where chains of processes are invoked by PubSub events. When trying to invoke these events, the emulator logs give me this:
Sorry, we cannot connect to Cloud Services without a project ID. You may specify one with an environment variable named "GOOGLE_CLOUD_PROJECT".
I am running the emulators with --project {projectID}. Also I am constructing my PubSub events like this:
const pubsub = new PubSub({ projectId: getRealtimeDatabase().app.options.projectId })
I am using package "#google-cloud/pubsub": "^0.22.2", and I am importing PubSub like import { PubSub } from '#google-cloud/pubsub';
What do I have to do to make PubSub invoke new events? Thanks!

Turned out getRealtimeDatabase().app.options.projectId was undefined. I retrieved my projectId elsewhere. With the correct projectId, the code above works.

Related

repeated firebase deploy functions pub/sub creates duplicate scheduler jobs

I use the following command to deploy my project, one of the functions being a pub/sub:
firebase deploy --only functions
...and everything happens just fine, but the scheduler job is created a 2nd time, an exact copy of the previous one every time I deploy again.
I don't change the pub/sub function, so I would not necessarily need to include it during deploy, but why is it creating a new scheduler job every time? Neither the function, nor the topic in Pub/Sub is duplicating, only the scheduler job - why?
Here is how the pubsub function looks like, if relevant:
export const scheduledStuff = functions.pubsub
.schedule('0 0 * * *')
.timeZone('America/New_York')
.onRun(() => {
// do stuff
})
Update Jun 17 2022: I didn't specifically mention this, but the code I gave above, I hoped, should have given it away: I'm using the ES6 annotation for the function, ie. use import instead of require for packages, and the function is exported as export const scheduledStuff... instead of exports.scheduledStuff....
My functions are then separated into 2 categories, functions:functions, which are all the functions that are NOT pubsub, and this one pubsub. This is because I wanted to prevent the deployment of the pubsub function to my staging environment, so I'm using firebase deploy --only functions:functions for that one.
For this purpose I have an index file in my main functions directory that looks something like this:
import { default as firstFunction } from './callables/firstFunction.js'
import { default as secondFunction } from './callables/secondFunction.js'
//...
export const functions = {
firstFunction,
secondFunction,
//...
}
import { scheduledStuff } from './pubsub/scheduledStuff.js'
export const pubSubStuff = { scheduledStuff }
I tried to reproduce your case by deploying the below scheduler function which will run every minute and create a pubsub topic and a scheduler job.
In my case, my first deploy firebase deploy –only functions created a pubsub topic firebase-schedule-scheduledFunctionCrontab-us-central1 and a scheduler job firebase-schedule-scheduledFunctionCrontab-us-central.
With the second deployment firebase deploy –only functions, neither a duplicate scheduler job was created nor a duplicate pubsub topic.
My terminal shows the below lines:
functions: updating Node.js 16 function scheduledFunctionCrontab(us-central1)...
✔ functions[scheduledFunctionCrontab(us-central1)] Successful update operation.
The last deployment run got updated but a duplicate is not created. Also the scheduler function keeps updating the last run time and next run time field which confirms that the function is running every minute.
If what you have shared here, is the entire code of your scheduler function, then I think this is a Customer issue/bug.
Still to confirm I would like to have a look at your logs, but if you are confident that this is not an intended behavior (which seems to be according to me) please raise a support ticket with Google Cloud Support or raise a Customer issue in a public issue tracker

Some Cloud Functions formats are not deployed

I guess it must be a silly reason but I don't understand why from these 2 Cloud Functions, only the first one is recognized and deployed when I run the following command:
firebase deploy --only "functions:test1,functions:test2"
The first function that is a simple CRON is well deployed:
exports.test1 = functions
.pubsub.schedule('2,7,12,17,22,27,32,37,42,47,52,57 * * * *')
.timeZone('Europe/Paris')
.onRun((context) => {
console.log('test1');
});
The second one that receives data from a PubSub is never deployed:
exports.test2 = (message, context) => {
const name = message.data
? Buffer.from(message.data, 'base64').toString()
: 'World';
console.log('test2');
};
Do I have to use the gcloud commands to deploy the second one?
The second function test2 which receives data from Pub/Sub is triggered by a Pub/Sub topic. Cloud Functions for Firebase with Pub/Sub trigger which you can find here. As per the linked documentation you should mention the Cloud Functions for Firebase to be triggered by the Pub/Sub topic inside the functions code. The firebase deploy command does not have an option to specify the trigger topic while running the command. So if you want to deploy both the functions at a time then you should change your code to include the trigger topic as following -
exports.test2 = functions.pubsub.topic('topic-name').onPublish((message) => {
const name = message.data
? Buffer.from(message.data, 'base64').toString()
: 'World';
console.log(name);
});
topic-name is your Pub/Sub topic name.
If you don’t want to deploy both the functions at a time, then you can deploy the test2 function using gcloud with the code you have written and specify the trigger topic name while running the command, as mentioned here.

How to trigger Pub/Sub Topic for Firebase Emulator

Using Firebase Functions, I have code that runs every hour via a Google Cloud Scheduler Job.
It looks like this:
exports.hourly_tick =
functions.pubsub.topic("hourly-tick").onPublish((message, context) => {
return getData()
.then((data) => {
sendEmail(data["message"]);
})
.catch((error) => {
return console.log("🚩 Caught error: ", error);
});
});
I need to be able to test this locally, and am able to start my Firebase Emulator via firebase emulators:start from my terminal. However I do not know how to trigger this function in my local test environment to see logs in the local emulator.
How can I test this scheduled job / firebase function with the local emulator?
This is an ongoing feature request in Firebase tools (see GitHub issue).
As mentioned in the thread:
I think we maybe misled with how we represented #2011. It lets those functions be loaded into the emulator but doesn't actually trigger them on a schedule. Instead you'd have to manually trigger them using a Pub/Sub message.
You can check a workaround on this answer where you'd have to manually trigger a scheduled function using a Pub/Sub message.

firebase scheduled cloud function not being called [duplicate]

I am working on cloud functions especially schedule functions. I need to trigger a function periodically each 5 minutes, but in only test step. I need to run it on pubsub emulator without deploying it.
How to do it?
I tried to use firebase shell, but it triggered only once
exports.scheduledFunctionPlainEnglish =functions.pubsub.schedule('every 2 minutes')
.onRun((context) => {
functions.logger.log("this runs every 2 minutes")
return null;
})
Scheduled functions are loaded to the Cloud Functions emulator runtime and are bound to the PubSub emulator topic.
But as #samstern said (https://github.com/firebase/firebase-tools/issues/2034):
you'd have to manually trigger them using a Pub/Sub message.
You can do it like this:
import * as functions from 'firebase-functions';
import * as admin from 'firebase-admin';
import { PubSub } from '#google-cloud/pubsub';
if (!admin.apps.length) {
admin.initializeApp();
}
const pubsub = new PubSub({
apiEndpoint: 'localhost:8085' // Change it to your PubSub emulator address and port
});
setInterval(() => {
const SCHEDULED_FUNCTION_TOPIC = 'firebase-schedule-yourFunctionName';
console.log(`Trigger sheduled function via PubSub topic: ${SCHEDULED_FUNCTION_TOPIC}`);
const msg = await pubsub.topic(SCHEDULED_FUNCTION_TOPIC).publishJSON({
foo: 'bar',
}, { attr1: 'value1' });
}, 5 * 60 * 1000); // every 5 minutes
Additional info about this concept (thanks to #kthaas):
https://github.com/firebase/firebase-tools/pull/2011/files#diff-6b2a373d8dc24c4074ee623d433662831cadc7c178373fb957c06bc12c44ba7b
https://github.com/firebase/firebase-tools/pull/2011/files#diff-73f0f0ab73ffbf988f109e0a4c8b3b8a793f30ef33929928a892d605f0f0cc1f
As you said, you can use firebase shell to run your function once.
And in firebase shell, you can use NodeJS commands.
Use setInterval
Inside firebase functions:shell, use setInterval to run your function every 2 minutes.
user#laptop:~$ firebase functions:shell
✔ functions: functions emulator started at http://localhost:5000
i functions: Loaded functions: myScheduledFunction
firebase > setInterval(() => myScheduledFunction(), 120000)
> this runs every 2 minutes
Single line script
Since version 8.4.3 of firebase-tools, and especially this PR, the pipe solution does not work anymore.
In Bash, you can even pipe the setInterval command to firebase shell
user#laptop:~$ echo "setInterval(() => myScheduledFunction(), 120000)" | firebase functions:shell
For those of you seeing this in 2023, it's still not supported.
My solution was to abstract the code that does the "work" out of functions.pubsub.schedule and into their own functions. Then create a separate file (i added it at the top of the functions folder) with a setInterval inside it that fires the aforementioned abstracted function.
For example, somewhere in your code:
exports.myScheduledFunctionCode = () => {
console.log('why, hello there interval');
}
And in the timers.js (for example) file at the top of the /functions directory:
setInterval(() => {
myScheduledFunctionCode();
}, 60000);
Then, you can fire up your Firebase Emulator suite. In another Terminal session, just run a vanilla $ node functions/timers.js. Now your scheduled function code is running, and your whole emulator suite too.
Hope this helps someone!
This is currently not supported for scheduled functions. The documentation states:
Using the shell, you mock data and perform function calls to simulate interaction with products that the Emulator Suite does not currently support: Storage, PubSub, Analytics, Remote Config, Storage, Auth, and Crashlytics.
Scheduled functions are an unsupported extension of pubsub triggers.
Feel free to file a feature request with Firebase support.

Google Cloud Functions Cron Job Not Working

I am trying to set up a scheduled function in Firebase Cloud Functions. As a simple test, I have tried to recreate the sample shown on the documentation page:
const functions = require('firebase-functions')
exports.scheduledFunction = functions.pubsub
.schedule('every 5 minutes')
.onRun(context => {
console.log('This will be run every 5 minutes!')
return null
})
However, when I run firebase serve --only functions, I get the following error:
function ignored because the pubsub emulator does not exist or is not running.
Any idea why I get this message and how I can fix it?
From the documentation on Firebase's local emulator:
The Firebase CLI includes a Cloud Functions emulator which can emulate the following function types:
HTTPS functions
Callable functions
Cloud Firestore functions
So the local Firebase emulators don't currently support pubsub, and the error message seems to confirm that. So for the moment, you can't run pubsub triggered Cloud Functions locally.
A feature request for adding PubSub support to the emulator was filed. You might want to read up (and possibly comment) there, as the direction taken may or may not match with your needs.
The local shell does support invoking pubsub functions. That is of course quite different, but might be useful as a workaround for the moment.
For what it is worth, you need to enable the pubsub emulator in firebase. Add this to your emulators block:
{
"emulators": {
"pubsub": {
"port": 8085
},
}
}
Even then, it only creates the definition. The emulator doesn't support running the function on a schedule.
To simulate that behavior, I define a HTTP trigger, in which I manually send a message to the topic. For a schedule topic, it is firebase-schedule-<functionName>. In your case it will be firebase-schedule-scheduledFunction.
Sample code looks like:
const pubsub = new PubSub()
export const triggerWork = functions.https.onRequest(async (request, response) => {
await pubsub.topic('firebase-schedule-scheduledFunction').publishJSON({})
response.send('Ok')
})
Then on the command line, I trigger the HTTP function on a schedule.
while [ 1 ];
do wget -o /dev/null -O /dev/null http://localhost:5001/path/to/function/triggerWork;
sleep 300;
done

Resources