By default, async event consumers time out after 55 seconds. Use cases that required longer computation have previously had to rely on breaking the task into multiple steps or batches, and queuing multiple events to do the work.
You can now configure a timeout of up to 900 seconds (15 minutes) which will allow many such use cases to be performed in a single invocation.
Imagine you are developing an application that generates detailed reports from Jira issues. This process can be time-consuming, especially if you are aggregating data from multiple projects and applying complex calculations. The Long-Running Compute feature allows you to handle these tasks efficiently without running into timeout issues.
This tutorial assumes you're already familiar with developing on Forge and the Async Events API.
Before you start, ensure you have the following:
Create a new Forge app:
1 2forge create
Follow the prompts to set up your application. Name your app and choose the template appropriate to the type of app you are creating. For the purposes of this tutorial, we will use the blank
template. Select the following options:
context: Show All
category: Show All
blank
If you intend to extend this example to use UI to trigger a long-running function, or display the results of your long-running function to the front end, you can select a template from the UI Kit or Custom UI category.
Navigate to your app directory:
1 2cd your-app-name
Add necessary permissions:
Open manifest.yml
and add the required permissions to access Jira data:
1 2permissions: scopes: - read:jira-work
If you chose the blank
template, the existing function
module in the manifest can be removed, along with the sample src/index.js
file.
All long-running functions must be invoked by an async event consumer. Update the manifest.yml
file to include the required event consumer module and corresponding function module:
1 2modules: consumer: - key: queue-consumer-key queue: queue-consumer-name resolver: function: generate-report method: generate-report-event-listener function: - key: generate-report handler: generateReport.handler timeoutSeconds: 900
generateReport.handler
will be invoked by the queue each time an event is pushed to it.generate-report
and method value generate-report-event-listener
. The function value must match the key under the function
module. The method value must be defined on the resolver object in generateReport.js
for the consumer to invoke it. This will be visible in the following section.Create a new long-running function:
In the src
directory, create a new file called generateReport.js
. This is where the resolver and the long-running function is defined. The below long-running function will take 5 seconds to execute, however it can take up to 900 seconds until it gets timed out:
1 2import Resolver from '@forge/resolver'; const resolver = new Resolver(); resolver.define('generate-report-event-listener', async ({ payload, context }) => { // This resolver function can take up to 900 seconds to complete console.log("The resolver has been invoked"); const ret = await processGenerate(payload); console.log(`The resolver returned with: ${ret.body}`); return ret; }); export const processGenerate = async (event) => { const { projectKey } = event; // Simulate a long-running task const reportData = await generateReport(projectKey); return { statusCode: 200, body: JSON.stringify(reportData), }; }; const generateReport = async (projectKey) => { const issues = await fetchIssuesFromJira(projectKey); // Perform complex calculations and aggregations const report = performCalculations(issues); return report; }; const fetchIssuesFromJira = async (projectKey) => { // Simulate a delay for fetching data await new Promise(resolve => setTimeout(resolve, 5000)); // Return mock data return [ { id: 1, status: 'Done', points: 5 }, { id: 2, status: 'In Progress', points: 3 }, ]; }; const performCalculations = (issues) => { // Aggregate data const totalPoints = issues.reduce((sum, issue) => sum + issue.points, 0); return { totalPoints }; }; // This variable is referenced to in the manifest export const handler = resolver.getDefinitions();
There are many ways to invoke a function which will push events to the consumer queue, however a simple one we will use is a trigger.
Create a new file src/pushToQueue.js
with the following code:
1 2import { Queue } from "@forge/events"; export const handler = async (_req) => { const queue = new Queue({key: "queue-consumer-name"}); console.log("Pushing an event to the queue"); const jobId = await queue.push({ hello: 'world' }); console.log(`Queued job ${jobId}`); return { statusCode: 200, statusText: "Success" }; }
Update manifest.yml
to include a new trigger module, and extend the current function module. The event which will invoke the trigger is avi:jira:updated:issue
. This means every time an issue is updated, the trigger is invoked and pushToQueue.handler
is called. Your manifest.yml
file should now look like this:
1 2modules: consumer: - key: queue-consumer-key queue: queue-consumer-name resolver: function: generate-report method: generate-report-event-listener trigger: - key: invoke-lrf-when-jira-issue-updated function: push-to-queue events: - avi:jira:updated:issue function: - key: generate-report handler: generateReport.handler timeoutSeconds: 900 - key: push-to-queue handler: pushToQueue.handler
blank
template was chosen, there are no pre-installed dependencies.
1 2npm install @forge/resolver @forge/events
1 2forge deploy
1 2forge install
Update any Jira issue to invoke the long-running function! The console.log
statements executed can be seen in the terminal if running forge tunnel
or in the developer console if not. You can also see them by running forge logs
. To see the output of the long-running function on your frontend, build a UI Kit or Custom UI app.
If you have an active forge tunnel
running, the expected output is as follows:
1 2invocation: ... pushToQueue.handler INFO 14:30:01.146 ... Pushing an event to the queue INFO 14:30:01.589 ... Queued job queue-consumer-name#... invocation: ... generateReport.handler INFO 14:30:02.506 ... The resolver has been invoked INFO 14:30:07.508 ... The resolver returned with: {"totalPoints":8}
The ...
replaces anywhere an id is used in the log output. Your output will have real values.
Long-running functions allow Forge developers to handle complex and time-consuming tasks efficiently. By following this tutorial, you have learned how to set up a realistic use case for generating reports from Jira data.
Rate this page: