Home > Net >  Detecting an in-flight request to an API in Nodejs
Detecting an in-flight request to an API in Nodejs

Time:11-30

I am writing a service in Nodejs in which I fetch prices from an Api and the call to the API might take over a minute so one of the things that can happen is that a request for a specific item can happen and the same item can be requested before the first item is returned and I want to detect if there is an in-flight request for a specified item and, if there is one, I need to wait for this request to be finished and return the same response for both requests.

An example diagram would be:

00.000 getCost('123') #1 call
00.001 getExternalCost('123') query
01.000 getCost('123') #2 call
90.001 getExternalCost('123') response
90.002 getCost('123') #1 response
90.003 getCost('123') #2 response

This is the code I have written so far, which simply fetches the cost of the item.

let cache = new Map();
let addToCache = (key,val) => {
  if(!cache.has(key)){
    cache.set(key,val);
  }
}

const getCost = async (itemId) => {

  if(cache.has(itemId)){
    return cache.get(itemId);
  }

  const price = await getExternalCost(itemId);
  addToCache(itemId,price);
  
  return cost;
}

CodePudding user response:

This can be done if you add promises to the cache rather than awaiting them. By doing this an incoming query can be redirected to receive the value of a similar query pending in the cache.

const cache = new Map();

const addToCache = (key, val) => {
  if (!cache.has(key)) {
    cache.set(key, val);
  }
};

const retrieveFromCache = async(itemId) => {
  try{
    const start = Date.now();
    const price = await cache.get(itemId);
    const end = Date.now();
    console.log(`Query for ${itemId}. Price: ${price}. Time spent: ${(end - start)/1000} seconds.`);
    return price;
  } catch (err) {
    /*
      If required insert logic here to remove the itemId from the cache to
      allow new attempts to getExternalCost on this itemId.
    */
    return err;
  }
};

const getCost = async(itemId) => {
  if (cache.has(itemId)) {
    const start = Date.now();
    const price = await retrieveFromCache(itemId);
    return price;
  }

  const pricePromise = getExternalCost(itemId);
  addToCache(itemId, pricePromise);

  // all queries go through the cache
  const price = await retrieveFromCache(itemId);
  return price;
};


// example code
function getExternalCost(itemId) {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve(itemId * 100);
    }, 1000);
  });
}

let counter = 0;
const itemIds = [10, 10, 12, 12, 10, 1];

// mimic incoming queries
let interval = setInterval(() => {
  getCost(itemIds[counter  ]);
  if (counter === itemIds.length) {
    clearInterval(interval);
  }
}, 500);
<iframe name="sif1" sandbox="allow-forms allow-modals allow-scripts" frameborder="0"></iframe>

  • Related