DEV Community 👩‍💻👨‍💻

Cover image for Schedule Lambda Functions Using EventBridge | Serverless
awedis for AWS Community Builders

Posted on

Schedule Lambda Functions Using EventBridge | Serverless

Have you ever wondered 🤔 how you can schedule Lambda functions in an easy way? well in many situations you need lambda functions running some background jobs in scheduled intervals or in specific days

In this blog, will see how to tackle this task using Amazon EventBridge, from which we can easily create rules for our functions and schedule them whenever we want 🏝

The main parts of this article:

  1. About AWS Lambda Functions
  2. Brief Introduction about Amazon EventBridge
  3. About Cron Jobs
  4. Examples

1. AWS Lambda Functions

AWS Lambda is an event-driven, serverless computing platform. It is a computing service that runs code in response to events and automatically manages the computing resources required by that code

Lambda allows you to focus on your code rather than having to take care of provisioning and maintaining (virtual) machines. It removes the need for such traditional compute services and therefore also reducing the complexity and operation cost. Lambda functions have their limitations but are great for small pieces of isolated tasks

2. Amazon EventBridge

EventBridge allows you to build event-driven architectures, which are loosely coupled and distributed. In this article we are going to focus on the scheduling part, however if you want to know more about EventBidge, you can visit this link

3. About Cron Jobs (In General)

A cron job is a Linux command used for scheduling tasks to be executed sometime in the future. It is normally used to schedule a job that is executed periodically – for example, to send out a notice every morning

* * * * *  command to execute
│ │ │ │ │
│ │ │ │ │
│ │ │ │ └───── day of week (0 - 6) (0 to 6 are Sunday to Saturday, or use names; 7 is Sunday, the same as 0)
│ │ │ └────────── month (1 - 12)
│ │ └─────────────── day of month (1 - 31)
│ └──────────────────── hour (0 - 23)
└───────────────────────── min (0 - 59)
Enter fullscreen mode Exit fullscreen mode

For example:

  • 0 7 * * 1 command to execute (Set up a cronjob every once Monday at 7am) What does Asterisk (*) mean: The asterisk indicates that the cron expression matches for all values of the field. E.g., using an asterisk in the 4th field (month) indicates every month
  • 0 0 1 * * to create a cron job that runs on the first day of month

Side notes:

  • Slash ( / )
    Slashes describe increments of ranges. For example 3-59/15 in the 1st field (minutes) indicate the third minute of the hour and every 15 minutes thereafter.
    The form "*/..." is equivalent to the form "first-last/...", that is, an increment over the largest possible range of the field.

  • Comma ( , )
    Commas are used to separate items of a list. For example, using "MON,WED,FRI" in the 5th field (day of week) means Mondays, Wednesdays and Fridays.

  • Hyphen ( - )
    Hyphens define ranges. For example, 2000-2010 indicates every year between 2000 and 2010 AD, inclusive.

  • Percent ( % )
    Percent-signs (%) in the command, unless escaped with backslash (), are changed into newline characters,
    and all data after the first % are sent to the command as standard input.

📋 Note: For more information and help about Cron Jobs you can visit these site:

To know more about how AWS EventBridge cron job works you can visit this link

4. Example

In this part we are going to build 3 simple examples
1- API to schedule Lambda for specified date
2- Directly schedule Lambda for every day job
3- API to list all my Rules

First of all let us create and deploy our Lambda function which are going to execute it later on from the Amazon EventBridge

"use strict";

module.exports.backgroundFunction = async (event) => {
  try {
    console.log(event);
    console.log('This is the lambda that will be executed in the background');
  } catch (error) {
    console.log(error);
    return Response(500, {
      message: 'Something went wrong'
    });
  }
};
Enter fullscreen mode Exit fullscreen mode

Copy and save the Arn, it will be used inside the function that we will add the Targets

I- Example

In our first example we have two Lambda functions, first one creates the schedule for the EventBridge, and in second one will be executed based on the schedule of the Event. And at the background we will create a Lambda function which will be executed based on the scheduled date (to keep things simple, our Lambda function will just log the data)

  • Route:
scheduleDate:
  handler: src/modules/Schedule/controller/lambda.scheduleDate
  events:
    - http:
        method: get
        path: job/schedule-date
        cors: true
Enter fullscreen mode Exit fullscreen mode
  • Create a Lambda Function
  • Setup a rule in Amazon EventBridge First you need to create a rule (1), define a pattern and select an event bus (2) assign it a target (our Lambda function) (3)
"use strict";
const { listRules, putRule, putTargets, putEvents } = require('../service/eventBridge.service');

module.exports.scheduleDate = async (event) => {
  try {
    console.log(event);
    const body = JSON.parse(event.body);
    console.log('body =>', body);
    const {
      id,
      cron,
    } = body;

    const ruleResult = await putRule(cron);
    console.log('ruleResult =>', ruleResult);
    const targetResult = await putTargets(body);
    console.log('targetResult =>', targetResult);
    const eventResult = await putEvents(body);
    console.log('eventResult =>', eventResult);

    return Response(200, 'Scheduled');
  } catch (error) {
    console.log(error);
    return Response(500, {
      message: 'Something went wrong'
    });
  }
};
Enter fullscreen mode Exit fullscreen mode
  • EventBridge Helper functions:
const AWS = require('aws-sdk');
const eventBridge = new AWS.EventBridge();
const { utcToZonedTime } = require('date-fns-tz');
const { format } = require('date-fns');

const ruleName = 'my_test_rule';

module.exports.putRule = async (scheduledDate) => {
  const utcDate = utcToZonedTime(scheduledDate, 'GMT');
  const ScheduleExpression = `cron(${format(utcDate, 'm')} ${format(utcDate, 'h')} ${format(utcDate, 'd')} ${format(utcDate, 'M')} ? ${format(utcDate, 'yyyy')})`;
  console.log('ScheduleExpression =>', ScheduleExpression);

  const payload = {
    Name: ruleName,
    ScheduleExpression,
  };
  return await eventBridge.putRule(payload).promise();
}

module.exports.putTargets = async (data) => {
  const payload = {
    Rule: ruleName,
    Targets: [
      {
        Arn: process.env.SCHEDULED_LAMBDA_ARN,
        Id: String(data.id),
        Input: JSON.stringify(data),
      }
    ]
  };
  return await eventBridge.putTargets(payload).promise();
}

module.exports.putEvents = async (data) => {
  const payload = {
    Entries: [
      {
        Source: 'custom.lambda',
        DetailType: 'trigger',
        Detail: JSON.stringify(data),
      },
    ],
  };
  return await eventBridge.putEvents(payload).promise();
}
Enter fullscreen mode Exit fullscreen mode

📋 Note: Here I am using date-fns-tz & date-fns to help me convert my timestamp to cron value, however you are free to use any library that may suit you 🙂

  • API body sent:
{
  "id": 12,
  "scheduledDate": 1665225612000
}
Enter fullscreen mode Exit fullscreen mode

We can see from the body above that I'm sending scheduledDate value 1665225612000, which will be converted to cron(40 10 8 10 ? 2022)

  • Remember to add the permission inside the resources section in serverless.yml file (In order to be able to invoke the Lambda from EventBridge)
resources:
  BackgroundFunctionInvokePermission:
    Type: 'AWS::Lambda::Permission'
    Properties:
      FunctionName: arn:aws:lambda:${env:region}:${env:accountId}:function:${self:service}-${env:stage}-backgroundFunction
      Action: 'lambda:InvokeFunction'
      Principal: 'events.amazonaws.com'
      SourceArn: arn:aws:events:${env:region}:${env:accountId}:rule/my_test_rule
Enter fullscreen mode Exit fullscreen mode

📋 Note: When creating an EventBridge rule with a Lambda function as the target, keep the following in mind: (1- When using the EventBridge console to create the rule, the appropriate permissions are added to the function's resource policy automatically) (2- When using the AWS CLI, SDK, or AWS CloudFormation to create the same rule, you must manually apply the permissions in the resource policy)

  • eventBridge.putRule - Creates or updates the specified rule. Rules are enabled by default, or based on value of the state. A single rule watches for events from a single event bus
  • eventBridge.putTargets - Adds the specified targets to the specified rule, or updates the targets if they are already associated with the rule. Targets are the resources that are invoked when a rule is triggered
  • eventBridge.putEvents - Sends custom events to Amazon EventBridge so that they can be matched to rules

II- Example

In our second example our Lambda function will be automatically scheduled. For this one we are going to trigger the function every 5 Minutes

  • Routes:
everyFiveMinutesLambda:
  handler: src/modules/Schedule/controller/lambda.everyFiveMinutesLambda
  events:
    - eventBridge:
        schedule: cron(0/5 * * * ? *)
Enter fullscreen mode Exit fullscreen mode
  • everyFiveMinutesLambda Function
module.exports.everyFiveMinutesLambda = async (event) => {
  try {
    console.log(event);
    console.log('This Lambda λ function will be executed every Five minutes');

  } catch (error) {
    console.log(error);
    return Response(500, {
      message: 'Something went wrong'
    });
  }
};
Enter fullscreen mode Exit fullscreen mode

And that's it now our Lambda function will be executed every five minutes 😎 (To keep it simple I'm just logging out some messages which can be viewed from CloudWatch)

III- Example

Now let us build a simple API which returns all my Rules created inside EventBridge, if we have successfully created out an scheduled Rule, it should be returned here

  • API Route:
addMessage:
  handler: src/modules/Schedule/controller/lambda.listEventBridgeRules
  events:
    - http:
        method: post
        path: job/list
        cors: true
Enter fullscreen mode Exit fullscreen mode
  • Lambda Code:
"use strict";
const { listRules } = require('../service/eventBridge.service');

module.exports.listEventBridgeRules = async (event) => {
  try {
    console.log(event);
    const body = JSON.parse(event.body);

    const result = await listRules();
    console.log('result =>', result);
    let finalResult = result.Rules;

    finalResult.forEach(function(item) {
      delete item.Arn;
      delete item.EventBusName;
    });

    return Response(200, finalResult);
  } catch (error) {
    console.log(error);
    return Response(500, {
      message: 'Something went wrong'
    });
  }
};
Enter fullscreen mode Exit fullscreen mode
  • Service helper function:
const AWS = require('aws-sdk');
const eventBridge = new AWS.EventBridge();

module.exports.listRules = async () => {
  return await eventBridge.listRules().promise();
}
Enter fullscreen mode Exit fullscreen mode
  • Response: (Here we should be able to see our previously created rules)
[
    {
        "Name": "my_test_rule",
        "State": "ENABLED",
        "ScheduleExpression": "cron(55 10 8 10 ? 2022)"
    },
    {
        "Name": "sls-try-v1-everyFiveMinutesLambda-rule-1",
        "State": "ENABLED",
        "ScheduleExpression": "cron(0/5 * * * ? *)"
    }
]
Enter fullscreen mode Exit fullscreen mode

Voilà 😊 now we have successfully created our scheduled functions, the first one will be executed only one time, while the other every five minutes

Conclusion

In This article we saw how to schedule a Lambda function using EventBridge. In many scenarios you need this feature, which helps you to tackle real life problems which need cron jobs or scheduled tasks

This article is part of "Messaging with Serverless" series that I've been writing for a while, if you are interested to read other Serverless offerings from AWS, feel free to visit the links below

Top comments (1)

Collapse
 
jsproject profile image
jsbeginner

Great 😃

Timeless DEV post...

How to write a kickass README

Arguably the single most important piece of documentation for any open source project is the README. A good README not only informs people what the project does and who it is for but also how they use and contribute to it.

If you write a README without sufficient explanation of what your project does or how people can use it then it pretty much defeats the purpose of being open source as other developers are less likely to engage with or contribute towards it.