16

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.

Zonxwedop
  • 335
  • 3
  • 12
  • 3
    I haven't looked into this in any detail, but can you simply add some code to your Lambda function to edit the CloudWatch event schedule that's actually triggering the Lambda? Related: http://stackoverflow.com/questions/27382009/aws-lambda-scheduled-tasks?rq=1 – jarmod Jan 09 '17 at 22:46

4 Answers4

21

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

Aaron Medacco
  • 1,843
  • 2
  • 11
  • 12
  • This is essentially what we have done. Just modify the CloudWatch timer to run at a rate of the next time I need it to kick off. The only issue is that the precision is no less than a minute. Unfortunately, scheduled events do not allow for cron jobs with seconds. But this will suffice for now. – Zonxwedop Jan 26 '17 at 15:00
  • Do we not need to specify a target in this case? – Yadynesh Desai Jan 30 '20 at 05:31
  • the blog post link in the answer doesn't exist anymore – Thomas Apr 08 '21 at 13:09
10

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();
c1moore
  • 1,631
  • 17
  • 25
0

You could create a CloudWatch rule to run at a particular time. CloudWatch rules can be run periodically or with a cron like syntax that lets you specify a single run time. You'll likely need to clean these up later though.

stdunbar
  • 10,999
  • 9
  • 26
  • 38
0

You can keep executing the lambda on a schedule but persist a value in a data store such as DynamoDB or S3 that tells the lambda when it should run next.

The lambda will keep executing periodically but you can control when it actually does what its intended for. There are billing considerations here (the lambda will continue consuming a minimal amount of resources in the background) but hopefully things shouldnt get too out of hand. Probably simpler than trying to manage the triggers from within the same lambda.

Steven de Salas
  • 18,983
  • 6
  • 63
  • 77
  • 1
    The issue is that I need to control when the lambda runs, not when the specified code on the lambda runs. I do persist data that lets me know when to run next, but the necessity is for the lambda run time to be configurable. – Zonxwedop Jan 10 '17 at 21:35