Nicolai Lissau
Nicolai Lissau

Reputation: 8312

Passing previous result to next Lambda invocation in CloudWatch scheduled events

Description

I have AWS CloudWatch Events set up to invoke a Lambda function every 5 minutes.

What I would like to achieve is to monitor whether the response of an API has changed. Therefore I would like to pass the result of the previous invocation on to the next invocation, for comparison.

The result is an array of < 200 elements and therefore quite small.

My considerations are:

Both options seems like a lot of gunpowder to use for shooting a few pigeons, so my question is:

Is there a simpler way to pass results between invocations of Lambda functions?

Upvotes: 0

Views: 617

Answers (2)

Nicolai Lissau
Nicolai Lissau

Reputation: 8312

I allow myself to also add an answer, based on @Marcin's comment on utilising the AWS System Manager parameter store, as it solves my question and tackles the request of a lightweight solution.

Here's an example of a counter, incremented each time a Lambda function is invoked:

// handler.js, Lambda entry point
const AWS = require('aws-sdk')
const ssm = new AWS.SSM();

const SSM_KEY = 'SSMData';

const getSSMData = async () => {

  var getParams = {
    Name: SSM_KEY,
  };

  try {
    const data = await new Promise((resolve, reject) => {
        ssm.getParameter(getParams, (error, data) => {
            if (error) reject(error)
            if (data) resolve(data)
        });
    });

    if (data.Parameter && data.Parameter.Value) {
        return JSON.parse(data.Parameter.Value)
    } else {
        return null
    }

  } catch (error) {

    return false
  }

}

const putSSMData = async (data) => {

  const jsonData = JSON.stringify(data)

  var putParams = {
    DataType: "text",
    Name: SSM_KEY, /* required */
    Type: 'String',
    Value: jsonData, /* Max 4kb */
    Overwrite: true
  };

  try {
    await new Promise((resolve, reject) => {
        ssm.putParameter(putParams, (error, data) => {
            if (error) reject(error)
            if (data) resolve(data)
        });
    });

    return true

  } catch (error) {

    console.log(error, error.stack);
    return false;
  }

};

module.exports.hello = async (event, context) => {

  let previousResult = await getSSMData()

  if (!previousResult) {
    previousResult = {
        count: 0
    }
  }

  console.log({ previousResult })

  const nextResult = { ...previousResult, count: previousResult.count + 1 }

  await putSSMData(nextResult)
};

Result

Lambda invokation 1: { previousResult: { count: 0 } }

Lambda invokation 2: { previousResult: { count: 1 } }

Lambda invokation 3: { previousResult: { count: 2 } }

...

Permissions

It is necessary to add an IAM role for the Lambda function to use SSM. Using the serverless framework:

iamRoleStatements:
  - Effect: "Allow"
    Action:
      - "ssm:GetParameter"
    Resource:
      - "arn:aws:ssm:<REGION>:<ACCOUNT_ID>:parameter/SSMData"
  - Effect: "Allow"
    Action:
      - "ssm:PutParameter"
    Resource:
      - "arn:aws:ssm:<REGION>:<ACCOUNT_ID>:parameter/SSMData"

Upvotes: 1

Dennis Traub
Dennis Traub

Reputation: 51634

You’re right, EFS would probably be too much of an overhead.

However, Amazon S3 and DynamoDB both are perfectly suitable to persist data between individual invocations of a Lambda function.

The Lambda service itself doesn’t provide long-term persistence out of the box. It is better to architect functions in a stateless way and use serverless persistence services, like S3 or DynamoDB, to store state.

Upvotes: 1

Related Questions