Bug: Precomputation Cache has multiple entries when switching environment

Hey folks,

I’m writing a plugin function for JQL. I have been using the subtaskOf example that is around.

I got it working in my dev environment, so then I installed it as staging on the proper work jira instance.
However, then I noticed something odd, and tried to connect to it with forge tunnel - which notified me it can only tunnel to dev.
So I removed the staging install and then installed the development version on the same Jira cloud instance.

However, now, there is a lingering precompute which is causing issues.
So there’s a bug or anomaly with the precompute system when switching an app’s environment.
See the screenshot attached for the exact details:

My precomputationApi.js is very similar to the provided example:

import api, { route } from "@forge/api";

// https://developer.atlassian.com/cloud/jira/platform/rest/v3/api-group-jql-functions--apps-/

export const fetchPrecomputations = async (orderBy) => {
  return await api
    .asApp()
    .requestJira(route`/rest/api/3/jql/function/computation?orderBy=${orderBy || "UPDATED"}`, {
      headers: {
        Accept: "application/json",
      },
    })
    .then((response) => response.json());
};

// `precomputations` is a list that looks like this: [{ id: "id", "value": "jql" }]
export const updatePrecomputations = async (precomputations, component, operator) => {
  const response = await api
    .asApp()
    .requestJira(route`/rest/api/3/jql/function/computation`, {
      method: "POST",
      headers: {
        Accept: "application/json",
        "Content-Type": "application/json",
      },
      body: JSON.stringify({
        "values": precomputations
      }),
    });

  console.log(`Update precomputation ${precomputations.map(p => p.id)} (For (${operator}) Component: '${component}') result: ${response.statusText}`);

  if (!response.ok)
    throw Error(`${response.status}:${response.statusText}`)
};

The bug appears to be that the GET and the POST have a difference of opinion. I suspect the GET should NOT return the first id seen in the screenshot above.

Updating the function to this is a workaround:

async function refreshMostStalePrecomputation() {

  var precomputations = await fetchPrecomputations();
  
  const oldestPrecomp = precomputations.values[0]
  const operator = oldestPrecomp.operator;
  const component = oldestPrecomp.arguments[0];

  // There's a bug for multiple ids mapped to the same operator/component
  // This is a workaround
  var idsToUpdate = []
  for (const precomp of precomputations.values) {
    if (precomp.operator == operator && precomp.arguments[0] == component) {
      idsToUpdate.push(precomp.id);
    }
  }
  console.log(`Refreshing precomputation(s) ${idsToUpdate.toString()} (For (${operator}) Component: '${component}')`)

  const functionResult = await epicHasComponentImpl(component, operator);

  if (functionResult.jql) {
    var errors = []
    for (const id of idsToUpdate) {
      const updatedPrecomputation = {
        id: id,
        value: functionResult.jql
      };

      try {
        await updatePrecomputations([updatedPrecomputation], component, operator);
      }
      catch (ex) {
        errors.push(ex.message);
      }
    }
    if (errors.length == idsToUpdate.length)
    {
      console.error(`Failed to update precomputation, POST(s) failed with ${errors.toString()}`)
    }
  } else {
    console.error(`Failed to update precomputation(s) ${idsToUpdate.toString()} for ${component}: ${functionResult.error}`);
  }
}

Hi @vsaran, thank you for reaching out!

We will take a look and come back to you. Sorry for the inconvenience.

Hi @vsaran, thank you for your patience.
We have rolled out the fix, so you no longer should experience this problem.

1 Like