0

I am trying to write a caching function that returns cached elasticcache data or makes an api call to retrieve that data. However, the lambda function seems to be very unrealiable and timing out often.

It seems that the issue is having redis calls as well as public api calls causes the issue. I can confirm that I have setup aws correctly with a subnet with an internet gateway and a private subnet with a nat gateway. The function works, but lonly 10 % of the time.The remaining times exceution is stopped right before making the API call. I have also noticed that the api calls fail after creating the redis client. If I make the external api call prior to making the redis check it seems the function is a lot more reliable and doesn't time out.

Not sure what to do. Is it best practice to seperate these 2 tasks or am I doing something wrong?

    let data = null;

module.exports.handler = async (event) => {
  //context.callbackWaitsForEmptyEventLoop = false;
  let client;
  try {
    client = new Redis(
      6379,
      "redis://---.---.ng.0001.use1.cache.amazonaws.com"
    );

client.get(event.token, async (err, result) => {
      if (err) {
        console.error(err);
      } else {
        data = result;
        await client.quit();
      }
    });

    if (data && new Date().getTime() / 1000 - eval(data).timestamp < 30) {

      res.send(`({
        "address": "${token}",
        "price": "${eval(data).price}",
        "timestamp": "${eval(data).timestamp}"
      })`);
    } else {
      getPrice(event); //fetch api data
    }
    ```
   
Chinmay Gopal
  • 59
  • 1
  • 6

1 Answers1

0

There a lot of misunderstand in your code. I'll try to guide you to fix it and understand how to do that correctly.

  1. You are mixing asynchronous and synchronous code in your function.
  2. You should use JSON.parse instead of eval to parse the data because eval allows arbitrary code to be executed in your function
  3. You're using the res.send to return response to the client instead of callback. Remember the usage of res.send is only in express and you're using a lambda and to return the result to client you need to use callback function

To help you in this task, I completely rewrite your code solving these misundersand.

const Redis = require('ioredis');

module.exports.handler = async (event, context, callback) => {
  // prefer to use lambda env instead of put directly in the code
  const client = new Redis(
    "REDIS_PORT_ENV",
    "REDIS_HOST_ENV"  
  );

  const data = await client.get(event.token);
  client.quit();
  const parsedData = JSON.parse(data);
  if (parsedDate && new Date().getTime() / 1000 - parsedData.timestamp < 30) {
    callback(null, {
      address: event.token,
      price: parsedData.price,
      timestamp: parsedData.timestamp
    });
  } else {
    const dataFromApi = await getPrice(event);
    callback(null, dataFromApi);
  }
};

There another usage with lambdas that return an object instead of pass a object inside callback, but I think you get the idea and understood your mistakes.

Follow the docs about correctly usage of lambda: https://docs.aws.amazon.com/sdk-for-javascript/v2/developer-guide/using-lambda-functions.html

To undestand more about async and sync in javascript: https://www.freecodecamp.org/news/synchronous-vs-asynchronous-in-javascript/

JSON.parse x eval: JSON.parse vs. eval()

Julien Ambrosio
  • 311
  • 2
  • 6