Nodejs 10: Why does DynamoDB put function giving success multiple times - node.js

I am writing node js 10.x lambda function to put details into DynamoDB table.
Below is code
const AWS = require('aws-sdk');
var db = new AWS.DynamoDB.DocumentClient();
var tableName="xyz";
exports.handler = async (event) => {
// TODO implement
console.log("Event: "+ JSON.stringify(event));
var response = {
statusCode: 200,
"headers": {
"Access-Control-Allow-Origin" : "*",
"Access-Control-Allow-Credentials" : true
},
};
await db.put({
TableName: tableName,
Item: {
userid: event.userid,
}
}, (error, data) => {
if (error) {
console.log("error:"+ error);
}
else{
console.log("Success");
}
}).promise();
return response;
};
I am getting kind on random number of success return
Output execution 1
2019-11-07T07:03:45.388Z f451dfc1-01ea-41d0-a998-945cb0f18be1 INFO Success
2019-11-07T07:03:45.510Z f451dfc1-01ea-41d0-a998-945cb0f18be1 INFO Success
2019-11-07T07:03:45.511Z f451dfc1-01ea-41d0-a998-945cb0f18be1 INFO Success
Output execution 2
2019-11-07T07:08:19.270Z 3ce51f5d-bbbc-4dd6-b46f-2149ee9bb9cf INFO Success
Output execution 3
2019-11-07T07:08:27.410Z 2625bba5-b8e1-40e4-8704-7c0d486f6dff INFO Success
2019-11-07T07:08:27.431Z 2625bba5-b8e1-40e4-8704-7c0d486f6dff INFO Success
**
does anyone know the cause of this problem?
I am relatively new to node js 10.x. so please help me if I have missed something in code
**

you are using a callback and promise at the same time, remove the callback.
You can try something like
exports.handler = async (event, context) => {
const params = {
TableName: tableName,
Item: {
userid: event.userid,
}
};
try {
const data = await dynamoDB.put(params).promise();
console.log("Data: ", data);
} catch(error) {
console.error("Error:", error);
}
}

Related

Inconsistent DynamoDB writtings

We have the following code used as lambda Function in Serverless Framework triggered every 2min with cron. The issue we are facing is that the writing in DynamoDB is inconsistent , we want to have 3 writings but instead we receive 1 or 2 writings every 2 minutes.
DynamoDB has a HASH key the HOUR and SORT KEY the DATE and Billing mode: PROVISIONED. Has someone faced the same behavior from DynamoDB or the same issue to share how he sovled it. Thanks
"use strict";
const AWS = require("aws-sdk");
const axios = require("axios");
const dynamoDb = new AWS.DynamoDB.DocumentClient();
const lambda = new AWS.Lambda({
region: "us-east-1",
});
module.exports.getWeather = async (event, context, callback) => {
const openWeatherMapAPIURL = `http://api.openweathermap.org/data/2.5/weather?id=${event}&appid=XXXXXXXXXXXXXXXXXXXXXXX&units=metric`;
const currentWeather = await axios
.get(openWeatherMapAPIURL)
.then((records) => {
console.log(records);
const d = new Date(records.headers.date);
let hour = d.getHours();
const params = {
TableName: process.env.DYNAMODB_TABLE_NAME,
Item: {
hour: hour,
date: records.headers.date,
city: records.data.name,
temp: records.data.main.temp,
feelsLike: records.data.main.feels_like,
description: records.data.weather[0].description,
},
};
setTimeout(function () {
dynamoDb.put(params, (error) => {
// handle potential errors
console.log(`zapis na: ${records.data.name} ${records.headers.date}`);
if (error) {
console.log(error);
console.error(error);
return;
}
});
}, 3000);
})
.catch((error) => {
console.log(error);
return;
});
const response = {
statusCode: 200,
body: JSON.stringify({
message: `Weather from ${event} was requested!`,
}),
};
callback(null, response);
};
module.exports.cron_launcher = (event, context, callback) => {
const requestedID = ["786735", "792578", "785842"];
requestedID.forEach((requestedID) => {
const params = {
FunctionName: process.env.HANDLER_LOCATION + "-getWeather",
InvocationType: "RequestResponse",
Payload: JSON.stringify(requestedID),
};
return lambda.invoke(params, function (error, data) {
if (error) {
console.error(JSON.stringify(error));
return new Error(`Error printing messages: ${JSON.stringify(error)}`);
} else if (data) {
console.log(data);
}
});
});
};
You are not waiting for the dynamodb.put operation to finish. Additionally, you are wrapping the call in a setTimeout. Your lambda function is returning before the network operation can be made. Make sure the put operation succeeds before returning a result from your lambda.
I see no reason for you to use a setTimeout here.
You can call dynamodb.put(...).promise() to get a promise from the dynamodb SDK and await that promise.
2.a Or you can continue using a callback, but wrap the entire section of code in a new promise object, calling the resolve method after the dynamodb.put call finishes.

how to wait for a result in rxjs for a lambda function

I am trying to write an async lambda function which is calling a function for sign up a user in cognito.
my problem is that in my lambda function, it is not waiting for the result and finish the execution. would you mind check what is my issue? I am new to rxjs. please help me.
mylambda function
exports.handler = async (event, context) => {
//poolData and params will fetch from event
let source = await signup(poolData, params);
console.log(source);
});
my signup function
function signup(poolData, body) {
const userPool = new AmazonCognitoIdentity.CognitoUserPool(poolData);
const { username, password, attributes } = body;
const attributesList = [];
if (Array.isArray(attributes)) {
attributesList.push(
...attributes.map(item => new AmazonCognitoIdentity.CognitoUserAttribute(item))
);
}
let source = Observable.create(observer => {
let output = (err, res) => {
if (err)
{
observer.error(err);
}
else
{
const cognitoUser = res.user;
const data = {
username: cognitoUser.getUsername(),
};
observer.next(data);
}
observer.complete();
}
userPool.signUp(username, password, attributesList, null, output);
});
let respond;
let subscriber = {
next(value) {
console.log('Subscriber - next: ', value);
respond = {
'statusCode': 200,
'body': JSON.stringify({
"username": value.username,
})
}
}, error(err) {
console.log('Subscriber - err: ', err);
respond = err;
},
complete() {
console.log('Subscriber - complete');
return response;
}
};
source.subscribe(subscriber);
}
module.exports = signup;
This behavior is totally normal.
So first thing first, an observable is not a promise which means you are not able to await a response with the await keyword, also I don't see anything to be returned from the signup function, which will probably lead to undefined to be logged anyways.
So how to fix that, one way to fix this issue is to use toPromise() which will turn your observable into a promise which then can be awaited wherever needed.
The other way (which is the rxjs way) will be to return from the signup function the observable and inside your handler function to subscribe for the response.
let subscriber = {
next(value) {
console.log('Subscriber - next: ', value);
respond = {
'statusCode': 200,
'body': JSON.stringify({
"username": value.username,
})
}
}, error(err) {
console.log('Subscriber - err: ', err);
respond = err;
},
complete() {
console.log('Subscriber - complete');
return response;
}
};
exports.handler = (event, context) => {
//poolData and params will fetch from event
signup(poolData, params).subscribe(subscriber);
})

API works randomly while putting data into dynamoDB

I'm trying to put data into Dynamodb using serverless deployment. I have added the permission to write to Dynamodb.
The api is always sending {"message": "Internal server error"} but is able to put the data into db once if tried 5,6 times.
Following is the serverless.yaml config
handler: dynamoUpdate.handler
events:
- http:
path: /createdbentry
method: get
cors: true
Following is the code:
const AWS = require('aws-sdk')
AWS.config.update({ region: process.env.REGION || 'us-east-1' })
var ddb = new AWS.DynamoDB({apiVersion: '2012-08-10'});
exports.handler = async (event) => {
return await createDBEntry("i123","Working");
}
const sendRes = (status, body) => {
var response = {
statusCode: status,
headers: {
"Content-Type": "application/json"
},
body: body
};
return response;
};
const createDBEntry = async function(id,result) {
var params = {
TableName: 'emplist',
Item: {
'ID' : {S:id},
'Summary':{S: result},
}
};
ddb.putItem(params, function(err, data) {
console.log("Here comes me.")
if (err) {
console.log("Opps Error");
return sendRes(403,err);
} else {
console.log("Complete")
return sendRes(200,data);
}
});
}
How can it be resolved ?
The problem is you are not returning any promise or awaiting anything async in your function called createDBEntry. Thus your handler returns undefined which makes apigateway return 500 internal server error.
You are mixing callbacks and async/await.
Your createDBEntry function should look like this.
const createDBEntry = async function(id, result) {
var params = {
TableName: 'emplist',
Item: {
'ID' : {S:id},
'Summary':{S: result},
}
};
try {
let data = await ddb.putItem(params).promise()
return sendRes(200, JSON.stringify(data))
} catch (err) {
console.log("Oops Error");
return sendRes(403, err.toString());
}
}
When you return from an async handler in lambda the current execution environment is frozen immediately (unlike when you use a non-async handler with a callback). This is why most of the time the writes to the database does not succeed.

Deleting a row if the value is a duplicate DynamoDB

I'm trying to compare if dynambodb has duplicate phoneNumbers and if it does I want to delete the older one. There doesn't seem to be a whole lot, on doing this through lambda. Some help would be appreciated. Thanks
const dynamoDb = new AWS.DynamoDB.DocumentClient();
const params = {
TableName: process.env.DYNAMODB_TABLE,
ProjectionExpression: "phoneNumber, createdAt",
};
module.exports.list = (event, context, callback) => {
// fetch all LakeSubscriptions from the database
dynamoDb.scan(params, (error, result) => {
// handle potential errors
if (error) {
console.error(error);
callback(null, {
statusCode: error.statusCode || 501,
headers: { 'Content-Type': 'text/plain' },
body: 'Couldn\'t fetch the item.',
});
return;
}
/*
for(item in result.phoneNumber){ //not really sure how I can pull these individual values I need to use to compare.
module.exports.delete = (event, context, callback) => {
const params = {
TableName: process.env.DYNAMODB_TABLE,
Key: {
id: event.pathParameters.id,
},
};
// delete the lakeSubscription from the database
dynamoDb.delete(params, (error) => {
// handle potential errors
if (error) {
//error handling
});
return;
}
});
};
}
*/
// create a response
const response = {
statusCode: 200,
body: JSON.stringify(result.Items),
};
callback(null, response);
});
};
Not a expert in Node.JS , but some suggestions
Scan operation results are returned in pages of 1 MB each, So the above code will not return all the rows in the dynamodb . Refer to the Step 4.3: Scan of this documentation
https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/GettingStarted.NodeJs.04.html
This would give you some idea on how to paginate and get all records from dynamodb . you can add all the results
Also refer the following post
How to fetch/scan all items from `AWS dynamodb` using node.js
This should give u a better idea

Cant insert data into DynamoDB using new nodejs 8.10

I want to use new nodejs 8.10 for developing my lambdas. A simple piece of code when written in node 6.10 style works but the same(similar) code doesn't work when I use node 8.10.
Below is working code which successfully inserts data into dynamodb table(nodejs 6.10)
var AWS = require('aws-sdk');
// Set the region
AWS.config.update({region: 'us-east-1'});
var documentClient = new AWS.DynamoDB.DocumentClient({apiVersion: '2012-08-10'});
exports.handler = (event, context, callback) => {
// TODO implement
var params = {
Item: {
client: 'client_'+Math.random(),
Type: 1,
Status: true,
json: { foo: 'bar', address:{ city:'Pune', street: 'ABC Nagar', pin:'411099'} }
},
TableName: 'clients'
};
documentClient.put(params, function(err, data) {
if (err) {
console.log("Error", err);
callback(err, null);
} else {
console.log("Success", data);
// return "Hi, insert data completed";
callback(null, data);
}
});
};
And below one which is node 8.10 style which doesn't work(means doesn't insert data into dynamodb table). I keep getting null as return value.
var AWS = require('aws-sdk');
// Set the region
AWS.config.update({region: 'us-east-1'});
var documentClient = new AWS.DynamoDB.DocumentClient({apiVersion: '2012-08-10'});
exports.handler = async (event) => {
// TODO implement
var params = {
Item: {
client: 'client_'+Math.random(),
Type: 1,
Status: true,
json: { foo: 'bar', address:{ city:'Pune', street: 'ABC Nagar', pin:'411099'} }
},
TableName: 'clients'
};
documentClient.put(params, function(err, data) {
if (err) {
console.log("Error", err);
} else {
console.log("Success", data);
return "Hi, insert data completed";
}
});
};
I spent searching 2-3 hours searching.. couldn't find any article or any question similar. Can anyone tell me what am I doing wrong?
Async / Await is a syntactical sugar for promise, Your documentClient.put should be wraped with promise. Since documentClient.put is based on callback appoach, you have to wrap it with promise
var AWS = require('aws-sdk');
// Set the region
AWS.config.update({region: 'us-east-1'});
var documentClient = new AWS.DynamoDB.DocumentClient({apiVersion: '2012-08-10'});
exports.handler = async (event) => {
// TODO implement
var params = {
Item: {
client: 'client_'+Math.random(),
Type: 1,
Status: true,
json: { foo: 'bar', address:{ city:'Pune', street: 'ABC Nagar', pin:'411099'} }
},
TableName: 'clients'
};
let putItem = new Promise((res, rej) => {
documentClient.put(params, function(err, data) {
if (err) {
console.log("Error", err);
rej(err);
} else {
console.log("Success", data);
res("Hi, insert data completed");
}
});
});
const result = await putItem;
console.log(result);
return result
};
Note: Its advisable to use DB operations in separate file,rather than using in handler function itself
Did you look in your table to see if it's inserting data? I think it is.
The problem with your async-style code is that you aren't returning a value. Returning "Hi, insert data completed" from the put callback doesn't return a value from handler.
You could manually create a promise and return that from handler, but I'd try using promisify.
This code is untested but should be close:
...
const util = require('util');
...
documentClient.putPromise = util.promisify(documentClient.put);
...
try {
const data = await documentClient.putPromise(params);
console.log("Success", data);
return "Hi, insert data completed";
}
catch (err) {
console.log("Error", err);
}
Here's more on promisify: http://2ality.com/2017/05/util-promisify.html
Calling await dynamo.put(params).promise(); is how I solved this issue after some googling. Specifically, it seems like calling foo.promise(); in the aws sdk is supported now.

Resources