Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Can you trigger an AWS Lambda on a dynamic timer?

Is there a way to trigger an AWS Lambda on a dynamic timer? Currently, I am utilizing scheduled-events to trigger the lambda, but this is a set timer. Is there a way to dynamically set a time for the Lambda to be triggered from within the Lambda?

The idea here is that this Lambda does specific checks and executes code to know when it should run next (because I only want this lambda to run when it needs to). I want to 1) determine the next time it needs to run and 2) set the time from within the Lambda code.

I see there are a lot of resources that are used for triggering Lambda functions (SNS, Kinesis, etc.), but I cant seem to find a good way to dynamically kick one off.

like image 211
Zonxwedop Avatar asked Jan 09 '17 22:01

Zonxwedop


People also ask

Can Lambda be triggered by time?

From now on, your AWS Lambda function will be triggered every Thursdays at 7 AM in GMT time zone. You can see your new scheduled rule on Amazon CloudWatch Events console as well.

What can trigger an AWS Lambda function?

Lambda Functions can be triggered in different ways: an HTTP request, a new document upload to S3, a scheduled Job, an AWS Kinesis data stream, or a notification from AWS Simple Notification Service (SNS).


2 Answers

This can be accomplished by setting a CloudWatch event rule to trigger your Lambda function. On each invocation of your Lambda function, the function will need to determine its next run time and modify the event rule appropriately.

var AWS = require("aws-sdk");

exports.handler = function(event, context) {
    var cloudwatchevents = new AWS.CloudWatchEvents();
    var intervals = Array(3, 5, 7);
    var nextInterval = intervals[Math.floor(Math.random()*intervals.length)];
    var currentTime = new Date().getTime(); // UTC Time
    var nextTime = dateAdd(currentTime, "minute", nextInterval);
    var nextMinutes = nextTime.getMinutes();
    var nextHours = nextTime.getHours();

    //  =================================
    //  DO YOUR WORK HERE
    //  =================================

    var scheduleExpression = "cron(" + nextMinutes + " " + nextHours + " * * ? *)";
    var params = {
        Name: "YOUR CLOUDWATCH EVENT RULE NAME",
        ScheduleExpression: scheduleExpression
    };
    cloudwatchevents.putRule(params, function(err, data) {
        if (err) {
            console.log(err, err.stack);  
        }
        else {
            console.log(data);
        }
    })
};

var dateAdd = function(date, interval, units) {
    var ret = new Date(date); // don't change original date
    switch(interval.toLowerCase()) {
        case 'year'   :  ret.setFullYear(ret.getFullYear() + units);  break;
        case 'quarter':  ret.setMonth(ret.getMonth() + 3*units);  break;
        case 'month'  :  ret.setMonth(ret.getMonth() + units);  break;
        case 'week'   :  ret.setDate(ret.getDate() + 7*units);  break;
        case 'day'    :  ret.setDate(ret.getDate() + units);  break;
        case 'hour'   :  ret.setTime(ret.getTime() + units*3600000);  break;
        case 'minute' :  ret.setTime(ret.getTime() + units*60000);  break;
        case 'second' :  ret.setTime(ret.getTime() + units*1000);  break;
        default       :  ret = undefined;  break;
    }
    return ret;
}

You should be able to swap my random determination with your own scheduling logic and insert whatever work you need in place of my comment.

You will need to substitute your event rule's name for "YOUR CLOUDWATCH EVENT RULE NAME" in my snippet.

Great question for a blog: AWS Lambda Functions That Dynamically Schedule Their Next Runtime

like image 199
Aaron Medacco Avatar answered Sep 22 '22 08:09

Aaron Medacco


This can now be accomplished without polling using a step function. You can find more information on AWS, but basically you would define a state machine for your step function that uses the Wait state and the TimestampPath field. Your state machine might end up looking something like

{
  "SartAt": "WaitState",
  "States": {
    "WaitState":     {
      "Type":          "Wait",
      "TimestampPath": "$.timestamp",
      "Next":          "ExecuteLambda"
    },
    "ExecuteLambda": {
      "Type":          "Task",
      "Resource":      "lambda-arn",
      "End":           true,
      "Retry":         [
        { ... },
        ...
      ]
  }
}

Assuming you're using Node, you could then invoke the step function with the following code:

const AWS = require('aws-sdk');

const stepFunctions = new AWS.StepFunctions();

await stepFunctions.startExecution({
  stateMachineArn: process.env.STATE_MACHINE_ARN,
  name:            "unique-name",
  input:           JSON.stringify({
    timestamp: (new Date(/*Date you want to start execution*/)).toISOString(),
    // Any extra context you want to pass to the step function.
  }),
}).promise();
like image 39
c1moore Avatar answered Sep 23 '22 08:09

c1moore