SIMMORSAL
SIMMORSAL

Reputation: 1674

How to have Cloud Tasks run only once?

I've written a cloud task and it works perfectly and triggers the link I gave without any problems, but it won't stop retrying running the link.

How can I make it run it only once?

What I'm trying to do is run a Firestore Function once in the future, on a document write in a collection. I found this tutorial for it.

So far my task creation code works perfectly, and delivers correct payload to the function it's going to call. And the called function works correctly too the first time it runs and exits with status 200. But on the retries I have to exit with error 500 since there's no data to access anymore.

I can see the 200 and 500 logs in firestore function's logs, but Cloud Tasks' logs is empty, even if a method has been run 50 times!


This is the full code

import * as functions from 'firebase-functions'
import * as admin from 'firebase-admin'

const { CloudTasksClient } = require('@google-cloud/tasks')

exports.moveActivityFromPlanToRecord = () =>
    functions
    .region('europe-west1')
    .firestore.document('Users/{userId}/Activities/{activityId}')
        .onCreate(async snapshot => {

            const moveTime = snapshot.data()! as MoveTime

            if (!moveTime || !moveTime.dueTime) {
                console.log("DueTime is empty or null: \n" + moveTime)
                return
            }


            // Get the project ID from the FIREBASE_CONFIG env var
            const project = JSON.parse(process.env.FIREBASE_CONFIG!).projectId
            const location = 'europe-west1'
            const queue = 'activityDateEventChecker'

            //queuePath is going to be a string that uniquely identifes the task
            const tasksClient = new CloudTasksClient()
            const queuePath: string =
                tasksClient.queuePath(project, location, queue)

            // URL to my callback function and the contents of the payload to deliver
            const url = `https://${location}-${project}.cloudfunctions.net/activityDateEventCheckerCallback`
            const docPath = snapshot.ref.path
            const dueTime = moveTime.dueTime
            const payload: MoveTaskPayload = { docPath, dueTime }

            console.log(payload)

            // build up the configuration for the Cloud Task
            const task = {
                httpRequest: {
                    httpMethod: 'POST',
                    url: url,
                    body: Buffer.from(JSON.stringify(payload)).toString('base64'),
                    headers: {
                        'Content-Type': 'application/json',
                    },
                },
                scheduleTime: {
                    seconds: moveTime.dueTime / 1000
                }
            }

            // enqueue the task in the queue
            return tasksClient.createTask({ parent: queuePath, task: task })
        })


interface MoveTime extends admin.firestore.DocumentData {
    dueTime?: number
}
interface MoveTaskPayload {
    docPath: string,
    dueTime: number
}

exports.activityDateEventCheckerCallback = () =>
    functions
    .region('europe-west1')
    .https.onRequest(async (req, res) => {
        const payload = req.body as MoveTaskPayload
        try {
            // getting the item
            const activity = await admin.firestore().doc(payload.docPath).get()

            // if time is up for it
            if (Date.now() >= payload.dueTime && activity.data() != undefined) {
                // getting path to activity to be in record
                const pathUser = activity.ref.parent.parent?.path
                const pathDocRecord = admin.firestore().doc(`${pathUser}/Record/${activity.id}`)

                
                console.log("RECORD--  ", (await (await pathDocRecord.get()).data())?.subject)

                // moving activity into record
                await pathDocRecord.set(activity.data()!)
                await activity.ref.delete()


                // sending notif to user
                const fcmPayload = {
                    notification: {
                        title: `${activity.data()?.subject}`,
                        body: " Time for activity. Record how it goes!"
                    },
                    data: {
                        activityId: activity.id
                    }
                }

                const user = await admin.firestore().doc(pathUser!).get()
                const fcmToken: string = user.data()?.fcmToken

                return admin.messaging().sendToDevice(fcmToken, fcmPayload)
            }

            return null

        } catch (error) {
            console.error(error)
            res.status(500).send(error)
            return null
        }
    })

Upvotes: 1

Views: 857

Answers (1)

Vikram Shinde
Vikram Shinde

Reputation: 1028

Tasks in Cloud Task retries when it does not get response code 2XX.

You can config the retry in Cloud Task Queue using maxAttempt paramtere.

Details are mentioned in the doc

Upvotes: 2

Related Questions