How to include only one class from aws-sdk in Lambda - node.js

In an effort to improve cold start latency in AWS Lambda, I am attempting to include only the necessary classes for each Lambda function. Rather than include the entire SDK, How can I include only the DynamoDB portion of the SDK?
// Current method:
var AWS = require('aws-sdk');
var dynamodb = new AWS.DynamoDB();
// Desired method:
var AWSdynamodb = require('aws-dynamodb-sdk');

The short answer is: you do not need to do this.
The AWS SDK for JavaScript uses dynamic requires to load services. In other words, the classes are defined, but the API data is only loaded when you instantiate a service object, so there is no CPU overhead in having the entire package around.
The only possible cost would be from disk space usage (and download time), but note that Lambda already bundles the aws-sdk package on its end, so there is no download time, and you're actually using less disk space by using the SDK package available from Lambda than using something customized.

I don't think this is possible.
The npm registry only has aws-sdk. https://www.npmjs.com/package/aws-sdk
There may be other npm packages available for dynamodb, but I would advice only using the sdk provided by aws team.

Be sure to instantiate the SDK outside of the handler.
This example is good, and will result in a cold start time of about 1s
const AWS = require("aws-sdk");
const SNS = new AWS.SNS({apiVersion: '2010-03-31'});
exports.handler = function(event, context) {
//do stuff
};
This example is bad and will result in a cold start time of about 5s
exports.handler = function(event, context) {
const AWS = require("aws-sdk");
const SNS = new AWS.SNS({apiVersion: '2010-03-31'});
//do stuff
};
https://docs.aws.amazon.com/lambda/latest/dg/best-practices.html
Take advantage of execution context reuse to improve the performance
of your function. Initialize SDK clients and database connections
outside of the function handler, and cache static assets locally in
the /tmp directory. Subsequent invocations processed by the same
instance of your function can reuse these resources. This saves
execution time and cost.

Related

How to import functions node.js

This is a question about how to manage a project with functions in separate files.
main file
const AWS = require('aws-sdk');
const monkeyDynamodb = require("../includes/dynamodb/monkey-dynamodb-functions.js")
...
exports.handler = async function(event, context) {
let billingDevices = monkeyDynamodb.scan("SensorNodeBilling");
....
monkey-dynamodb-functions.js
async function scan(dbName) {
var docClient = new AWS.DynamoDB.DocumentClient();
...
If you compile this, you have 2 separate objects called AWS & AWS2, and a warning since one of them is not declared. And if you run it, it fails.
On the other hand, if you add in const AWS = require('aws-sdk'); into monkey-dynamodb-functions.js, it now works, but the compiled code still creates multiple AWS objects. What is the right way to code this?
You should have this:
const AWS = require('aws-sdk');
in any module that wants to use this AWS SDK. The module system uses a cache so the module itself will only be loaded once (the first time it is required) and from then after will just return the same exports object that was originally loaded.
The rest of your question is not entirely clear as there's no AWS2 object in any of your code that you show so it's unclear what you think you have two of. If you're just talking about the monkey-dynamodb-functions.js file, then yes, it must also require in the AWS module so it can use it.
Unlike in the browser, each of your nodejs files runs in its own module. So, when you do something like this:
const AWS = require('aws-sdk');
in the main file, that AWS symbol is local to only this module. It cannot be accessed directly by any other module. So, in any other module that you also want to use the AWS sdk, just require it into that module also.
You can share objects or functions from one module to others by exporting them from one module and then requiring them into another module from that module and every module that wishes to have access to that shared object would require it in separately. That's what the aws-sdk module is itself doing.
but the compiled code still creates multiple AWS objects
It's not entirely clear what you mean by this. If you mean that when do require in the aws-sdk in both modules, you will then have two separate variables for the AWS sdk, that is true. That is the correct way to program it. Each module contains its own set of local variables. Those two variables will each contain the exact same aws-sdk object reference. This is the correct way to code with modules in nodejs.
Main file
const AWS = require('aws-sdk');
const monkeyDynamodb = require("../includes/dynamodb/monkey-dynamodb-functions.js")
...
exports.handler = async function(event, context) {
let billingDevices = monkeyDynamodb.scan("SensorNodeBilling");
....
monkey-dynamodb-functions.js
const AWS = require('aws-sdk');
async function scan(dbName) {
var docClient = new AWS.DynamoDB.DocumentClient();
...
}
exports.scan = scan;

Failed attempts to write to DynamoDB Local

I recently discovered DynamoDB Local and started building it into my project for local development. I decided to go the docker image route (as opposed to the downloadable .jar file.
That being said I've gotten image up and running and have created a table and can successfully interact with the docker container via the aws cli. aws dynamodb list-tables --endpoint-url http://localhost:8042 successfully returns the table I created previously.
However, when I run my lambda function and set my aws config like so.
const axios = require('axios')
const cheerio = require('cheerio')
const randstring = require('randomstring')
const aws = require('aws-sdk')
const dynamodb = new aws.DynamoDB.DocumentClient()
exports.lambdaHandler = async (event, context) => {
let isLocal = process.env.AWS_SAM_LOCAL
if (isLocal) {
aws.config.update({
endpoint: new aws.Endpoint("http://localhost:8042")
})
}
(which I have confirmed is getting set) it actually writes to the table (with the same name of the local dynamodb instance) in the live AWS Webservice as opposed to the local container and table.
It's also worth mentioning I'm unable to connect to the local instance of DynamoDB with the AWS NoSQL Workbench tool even though it's configured to point to http://localhost:8042 as well...
Am I missing something? Any help would be greatly appreciated. I can provide any more information if I haven't already done so as well :D
Thanks.
SDK configuration changes, such as region or endpoint, do not retroactively apply to existing clients (regular DynamoDB client or a document client).
So, change the configuration first and then create your client object. Or simply pass the configuration options into the client constructor.

Reuse of AWS.SNS service from Node.js AWS SDK

When using the SNS client found on AWS-SDK:
const sns = new AWS.SNS({});
Should I reuse this object across calls to save a handshake with the server?
This kind of object is usually stateless and benefits from pooling/cache; However the docs aren't really clear about that.
I believe you should initiate the class outside of your lambda.
AWS will reuse the instance when possible.
E.g.
const AWS = require('aws-sdk')
const sns = new AWS.SNS()
module.exports.handler = async input => {
// use sns class here
return input
}
EDIT:
Link to the official documentation that explains how the lambda execution context works: https://docs.aws.amazon.com/lambda/latest/dg/running-lambda-code.html
Any declarations in your Lambda function code (outside the handler
code, see Programming Model) remains initialized, providing additional
optimization when the function is invoked again. For example, if your
Lambda function establishes a database connection, instead of
reestablishing the connection, the original connection is used in
subsequent invocations. We suggest adding logic in your code to check
if a connection exists before creating one.

AWS Lambda function times out when I require aws-sdk module

I'm trying to query a DynamoDB table from a Lambda function (for an Alexa skill), but when I send the intent that calls require('aws-sdk'), the skill seems to hang and timeout. The Alexa test page just says "There was a problem with the requested skill's response", and I don't see any errors in the CloudWatch logs. I have the skill set up to catch any errors and return them as a spoken response, so I'm sure it's not an uncaught exception. I've also tried wrapping the require in a try/catch block, but that didn't work either.
This is the module that gets loaded with require if the test database intent request is received:
const AWS = require('aws-sdk');
module.exports = () => {
return 'Success!';
};
If I comment out require('aws-sdk'), the function works properly and Alexa responds with "Success".
Why does my skill break when all I'm doing is requiring the aws-sdk module?
I'm very new to AWS and this is my first experience trying to access a DynamoDB table in a Lambda function.
The Lambda function is uploaded as a zip that contains my source code, package.json (that includes aws-sdk as a dependency), and the node_modules folder.
After hours of debugging, I've found that changing import * as AWS from 'aws-sdk'; to import {DynamoDB} from 'aws-sdk'; (or {CloudFront} or whatever you actually use) made the timeout issue disappear. Mind you, the time to actually connect to DynamoDB was never an issue for me, it was always the import line where the timeout happened.
This can be fixed by either increasing the timeout or the memory allotted to the lambda function.
This is probably because the SDK is too big to be imported by the default timeout value of 3 seconds and the default memory value of 128 MB.
This is why it will probably work if you try importing smaller components like only DynamoDB.
Lambda, when using NodeJS, uses a callback continuation model. Your module should export a function that takes three parameters: event, context, and callback.
Event provides input parameters.
The other two are used for returning control from your handler function, depending on the NodeJS version you are using.
Try adding the three parameters that I mentioned and the, from within your exported handler function, call:
module.export = function(event, context, callback) {
callback(‘success’);
}
Bear in mind, I wrote this on mobile off the top of my mind, so you may need to make small afjustments to the code but the idea is the same. Don’t return directly from the function, but call the callback to supply the response as a continuation. And note that in earlier versions of NodeJS, prior to version 4, you would have to use the context to set success or failure, rather than calling the callback.
For more details, see the Lambda with NodeJS tech docs on AWS.
The other thing to keep in mind is that for Alexa specifically, the response should be in the correct format. That is a JSON response that contains all the necessary elements as explained in the Alexa Skills Kit tech docs.
The Alexa ASK sdk that you’ve included generates those responses but I thought I should point you to the actual docs in case you were going to try building the response by hand to understand how it works.

Is using PostgreSQL on stateless FaaS like AWS lambda a good idea?

I'd like to use Postgresql as a database on my AWS lambda functions but I'm worried about performance.
I'm worried that Lambdas are stateless and only exist in the time they're executing so I imagine every time the Lambda is triggered it'll try to initiate a brand new PG connection.
I'm not sure if this decreases performance or causes issues with stale connections somehow. Anyone know more about this?
I know DynamoDB is more in line with Lambda but I really need a relational database but at the same time Lambda's scalability.
You can make use of the container execution model of AWS lambda. When a lambda is invoked, AWS spins up a container to run the code inside the handler function. So if you define the PG connection outside the handler function it will be shared among the invocations of Lambda functions. You can find that in the above link.
Any declarations in your Lambda function code (outside the handler code, see Programming Model) remains initialized, providing additional optimization when the function is invoked again. For example, if your Lambda function establishes a database connection, instead of reestablishing the connection, the original connection is used in subsequent invocations. You can add logic in your code to check if a connection already exists before creating one.
const pg = require('pg');
const client = new pg.Client(<connection_string>);
exports.handler = (event, context, cb) => {
client.query('SELECT * FROM users WHERE ', (err, users) => {
// Do stuff with users
cb(null); // Finish the function cleanly
});
};
Refer this blog post.
But there is a caveat.
When you write your Lambda function code, do not assume that AWS Lambda always reuses the container because AWS Lambda may choose not to reuse the container. Depending on various other factors, AWS Lambda may simply create a new container instead of reusing an existing container.
Additionally you can create a scheduled job to warm up lambda function. (runs in every 5mins)

Resources