Node-Schedule Module Can't Cron Job Scheduled Long Time

I used node-schedule module to cron a job executed only once at particular time. I also use callable function to trigger Cloud Function and to get client-side data. The big trouble is that all things are fine to cron a single task until 15 minutes. But, on the other hand it doesn’t do the task scheduled further time more than 15 minutes. What may occur after 15 minutes to cancel task? I used Firebase Quickstart code and I add only schedule-job module into the code. I didn’t changed anything other than getting scheduling time values from client-side. I really need help for this issue. The cloud function script is below.

Thank you.

/* eslint-disable max-len */
"use strict";

const functions = require("firebase-functions");
const sanitizer = require("./sanitizer");
const admin = require("firebase-admin");
const schedule = require("node-schedule");
admin.initializeApp();
// [START messageFunctionTrigger]
// Saves a message to the Firebase Realtime Database but sanitizes the text by removing swearwords.
exports.addMessage = functions.https.onCall((data, context) => {
  // [START_EXCLUDE]
  // [START readMessageData]
  // Message text passed from the client.
  const text = data.text;
  // [END readMessageData]
  // [START messageHttpsErrors]
  // Checking attribute.
  if (!(typeof text === "string") || text.length === 0) {
    // Throwing an HttpsError so that the client gets the error details.
    throw new functions.https.HttpsError("invalid-argument", "The function must be called with " +
        "one arguments \"text\" containing the message text to add.");
  }
  // Checking that the user is authenticated.
  if (!context.auth) {
    // Throwing an HttpsError so that the client gets the error details.
    throw new functions.https.HttpsError("failed-precondition", "The function must be called " +
        "while authenticated.");
  }
  // [END messageHttpsErrors]

  // [START authIntegration]
  // Authentication / user information is automatically added to the request.
  const uid = context.auth.uid;
  const name = context.auth.token.name || null;
  const picture = context.auth.token.picture || null;
  const email = context.auth.token.email || null;
  // [END authIntegration]

  // [START returnMessageAsync]
  // Saving the new message to the Realtime Database.

  const dateWithTimeZone = (timeZone, year, month, day, hour, minute, second) => {
    const date = new Date(Date.UTC(year, month, day, hour, minute, second));

    const utcDate = new Date(date.toLocaleString("en-US", {timeZone: "UTC"}));
    const tzDate = new Date(date.toLocaleString("en-US", {timeZone: timeZone}));
    const offset = utcDate.getTime() - tzDate.getTime();

    date.setTime( date.getTime() + offset );

    return date;
  };

  const sanitizedMessage = sanitizer.sanitizeText(text); // Sanitize the message.

  const dataFuture = [uid, name, picture, email, sanitizedMessage];

  new Promise(function() {
    schedule.scheduleJob(dateWithTimeZone("Europe/Istanbul",
        data.year, data.month, data.day, data.hour,
        data.minute, data.second), function(data) {
      const theuid = data[0];
      const thename = data[1];
      const thepicture = data[2];
      const theemail = data[3];
      return admin.database().ref("/messages").push({
        text: data[4],
        author: {theuid, thename, thepicture, theemail},
      }).then(() => {
        console.log("New Message written");
        // Returning the sanitized message to the client.
        return {text: data[4]};
      })
      // [END returnMessageAsync]
          .catch((error) => {
            // Re-throwing the error as an HttpsError so that the client gets the error details.
            throw new functions.https.HttpsError("unknown", error.message, error);
          });
    }.bind(null, dataFuture));

  // [END_EXCLUDE]
  });

  return {text: "done"};
// [END messageFunctionTrigger]
});

At a glance I think the issue is due to the fact you trying to use the node-schedule module in a firebase cloud function.

Firebase cloud functions have set time limits until they are canceled as mentioned here (540 seconds or 9 minutes).

Since node-schedule runs within the function, if the function is off/restarted it loses its state, and thus wont be able to function as a “cron-job” schedule.

To be able to arbitrarily execute “cron-jobs” using firebase functions you have to take a different approach. Firebase is built on Google’s Cloud Platform, which offers a few solutions, such as using pub-sub to schedule events to execute your functions as seen here.

1 Like

Thank you for reply. But pub-sub cron a job periodically. I want task executed once. I didn’t find anything rather than Cloud Task is hard topic to build this task. It may be good to cancel pub-sub after running the first task of period. But I didn’t find how to cancel too.

So if you want to execute a task 1 time, your actually looking more a GCP Cloud Build solution (if you wanted to stay in GCP). However what that 1 time is dictated by can still be handled by pub-sub depending on the criteria.

But again, it really depends on what that “one time” means.

1 Like

I want to learn how I trigger function at particular time. Our users publish a post by specifying lifetime of the post. When post goes to the time out, I want to trigger Cloud Function. Every post has different date to be time out. When it is time to trigger for the specific post, I just want to trigger at that particular time. After the time, there is not anything to do for the specific post. So I must cancel the cron after the trigger. I’m stuck.

So this is more like a “user scheduled task” rather than a “one time task”.

There are a few patterns I’ve seen, the most common and the simplest is to simply have a cron running every so often (say every 15 minutes) that checks a list of posts in the database to trigger for a given time period. This can scale to very very large scales and over long periods of time, but can be a waste of executions for small scale.

Another common idea behind a setup like this is to calculate when to run the trigger upon creation, rather than doing the calculations later. So if a post can “live” for 3 days, when the post is created, create a date-time entry on the post for that future 3 day marker. Once the cron runs within 15 minutes of that marker, it can remove that post.

You’d probably want to look into the pub-sub/cloud scheduler I mentioned above, as you still need that 1 function to be called to get data from the database every now and then.


Its also worth noting, you can setup the cloud scheduler when a user creates a post aswell, but this doesn’t scale due to the limitations of the cloud scheduler in the first place. As such having the “state” of when to run stuff in a database/firebase is the only option.

1 Like