10

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;
}) 
benomatis
  • 5,536
  • 7
  • 36
  • 59
Extinct Light
  • 103
  • 1
  • 4

4 Answers4

14

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):

  1. https://github.com/firebase/firebase-tools/pull/2011/files#diff-6b2a373d8dc24c4074ee623d433662831cadc7c178373fb957c06bc12c44ba7b
  2. https://github.com/firebase/firebase-tools/pull/2011/files#diff-73f0f0ab73ffbf988f109e0a4c8b3b8a793f30ef33929928a892d605f0f0cc1f
Viacheslav Dobromyslov
  • 3,168
  • 1
  • 33
  • 45
  • Just for the sake of completeness, there are two more things in order for this code snippet to work. First, the `PUBSUB_EMULATOR_HOST` env variable has to be set to something like `localhost:8432` (depending on your local emulator setup). Second, the project id has to be given to the PubSub constructor (e.g. `new PubSub({ projectId: '...' }) `) – Stephan Jan 26 '21 at 03:20
  • @Stephan I added `apiEndpoint` to the PubSub initialization. Instead of specifying `projectId` I use `env-cmd` with `.env` file containing a link to the default credentials `GOOGLE_APPLICATION_CREDENTIALS=./default-credentials.json` – Viacheslav Dobromyslov Jan 26 '21 at 19:40
  • 2
    How did this not have any votes before mine? This works perfectly! I created a js file (instead of a ts file to not have to worry about compiling it) with code borrowed from your answer and am triggering the task manually instead of in an interval like this: `node functions/src/myTriggerfile.js` – Johnny Oshika Apr 23 '21 at 23:33
  • @ViacheslavDobromyslov can you explain this ans more detaily? Because I tried to put setInterval in global scope but not working – flutroid Oct 20 '22 at 21:24
  • I achive this by enable pubsub emulator in emulator suite and create a small node.js instance separately that only publish to topic like above example. Best approach I can find so far. – flutroid Oct 21 '22 at 03:41
  • Hi, is pubsub still not working on localhost:4000? – kd12345 Nov 17 '22 at 07:13
9

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
Community
  • 1
  • 1
foobar443
  • 2,339
  • 3
  • 23
  • 31
  • 3
    Since version 8.4.3 of firebase-tools, and especially [this PR](https://github.com/firebase/firebase-tools/pull/2350), this solution does not work anymore. – foobar443 Sep 14 '20 at 13:02
  • for clarification, you mean the single line pipe to shell doesn't work? (Then that could be edited from the answer?) I'm using fireabse-tools `9.16.0` and interactively running `setInterval(` in the shell works for me. – Carl Walsh Jul 09 '21 at 23:55
  • I realy like this solution, thanks. Didn't wanna create another file and run it. This makes it much easier – Isonlaxman Aug 15 '22 at 20:36
1

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.

Doug Stevenson
  • 297,357
  • 32
  • 422
  • 441
1

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!