For some reason I am getting the Process exited before completing request error.
Here is my code:
var http = require('http');
var aws = require('aws-sdk');
var ddb = new aws.DynamoDB();
function getUser(userid) {
var q = ddb.getItem({
TableName: "clients",
Key: {
ClientID: { S: userid } }
}, function(err, data) {
if (err) {
console.log(err);
return err;
}
else {
console.log(data);
}
});
//console.log(q);
}
exports.handler = function(event, context) {
getUser('user23');
console.log("called DynamoDB");
};
After googling a few people suggested changing the time out to a higher amount. Which I did to one minute.
However the function only took:
Duration : 2542.23 ms
I have also checked and double checked the table name and the key name etc...
The console log has this :
2016-03-21T04:09:46.390Z - Received event
2016-03-21T04:09:46.751Z - called DynamoDB
2016-03-21T04:09:47.012Z - {}
END RequestId: id123
Can anyone see why this is not working?
Edit
As per the answer below I tried:
console.log('Loading event');
var AWS = require('aws-sdk');
var dynamodb = new AWS.DynamoDB({apiVersion: '2012-08-10'});
exports.handler = function(event, context) {
console.log(JSON.stringify(event, null, ' '));
dynamodb.listTables(function(err, data) {
console.log(JSON.stringify(data, null, ' '));
});
var tableName = "clients";
var datetime = new Date().getTime().toString();
dynamodb.getItem({
TableName: tableName,
Key: {
ClientID: { S: "gr5f4sgnca25hki" } }
}, function(err, data) {
if (err) {
context.done('error','putting item into dynamodb failed: '+err);
}
else {
context.done(data);
}
});
};
but now my response is:
"errorMessage": "[object Object]"
What I am trying to do is this: Check if Item exists in database. Get the parameters from the entry if exists, then do something with the parameters
Can anyone help me?
First of all, context.done expects an Error object as the first argument, not a string containing the word "error".
Second, if the Error object is null or undefined, then the termination will be taken as a succeed.
Now, consider your callback function:
function (err, data)
{
if (err) {
context.done('error', 'putting item into dynamodb failed: ' + err);
}
else {
context.done(data);
}
}
If you have an error, then your lambda will terminate in a failure, which is expected, but the errorMessage you'll get would simply be "error", which isn't much informative.
If you don't have an error, then your lambda will also terminate in a failure, because you are passing in data as the first argument to context.done, and remember that the first argument is always the Error object.
To fix this, you can simply do:
function (err, data)
{
if (err) {
context.done(err);
} else {
context.done(null, data);
}
}
Or even better:
function (err, data)
{
context.done(err, data);
}
If you don't want to handle the item and just return it immediately, you can use context.done as your callback function to the DynamoDB operation:
dynamodb.getItem({
TableName: tableName,
Key: {
ClientID: { S: "gr5f4sgnca25hki" }
}
}, context.done);
You need to signal Lambda end of function.
Important
To properly terminate your Lambda function execution, you must call context.succeed(), context.fail(), or context.done() method. If you don't, either your Lambda function will continue to run until the Node.js event queue is empty, or your Lambda function times out.
Here is an example:
https://gist.github.com/markusklems/1e7218d76d7583f1f7b3
"errorMessage": "[object Object]"
can be solved by a small change as follows
function(err, data) {
if (err) {
context.done(err);
}
else {
context.succeed(data);
}
});
Note where context.succeed differs() from context.done()
Related
I have the following API to get the user's data based on a [pid]:
import prisma from "../../../../lib/prisma";
// Master read function - API route includes profile, subnodes and contents
async function getProfile(req, res) {
const profilePID = await prisma.profileNode.findUnique({
where: {
userName: req.query.pid
},
include: {
subnode: {
include: {
content: true,
}
},
},
})
// Integer for how many accounts the current user is following
const followingCount = await prisma.follower.count({
where: {
followerId: profilePID.userId
},
select: {
profileId: true
}
})
// integer for how many accounts the current user is being followed
const followerCount = await prisma.follower.count({
where: {
profileId: profilePID.userId
},
select: {
profileId: true
}
})
// detailed profile info of the people you are following
const following = await prisma.follower.findMany({
where: {
followerId: profilePID.userId,
NOT: {
profileId: null,
}
},
include: {
followees: true
}
})
// aggregate all data queries into one
const aggregatedData = {
profilesYouAreFollowing: followingCount.profileId,
yourProfileFollowers: followerCount.profileId,
followingData: following,
profileData: profilePID
}
if (aggregatedData) {
res.status(200).json(aggregatedData)
} else {
return res.status(500).json({ error: 'Something went wrong' })
}
}
export default async function handler(req, res) {
// commit to the database
if (req.method === 'GET') {
return getProfile(req, res)
}
}
As you would observe, the first request is to find the profileNode using a [pid] - which is a string like localhost:3000/user/ABC. Then I would get the userId (an integer) within the profileNode. The userId is then used in the rest of the prisma query to the database for followers and followers' details since all the ids are stored as integer.
I used SWR for client-side fetch, which is all fine but I noticed that while fetching, it will cause an error 500 before the data is fully fetched.
Now, while this does not hinder data fetching for presenting data to the client since SWR takes care of error handling and continue fetching until all the data is acquired, however, it does throw an error on other code like JSON.parse, as the error 500 has passed an undefined value to it - thus throwing an error.
Any tips or tricks as to how to get rid of the error 500?
Added client side code below:
const { data, error } = useSWR(`/api/profiles/read/${slug}`, fetcher)
const [subnodes, setSubnodes] = useState();
// authentication using next-auth session and fetched client-side userId
// compare equality - if equal, set Auth to true and show edit components
useEffect(() => {
async function fetchingData() {
setLoading(true);
// session
const session = await getSession();
let sessionUserId;
if (!session) {
sessionUserId = null;
} else {
sessionUserId = session.user.id;
}
// client
const clientId = await data?.profileData.userId;
// authentication check
if (sessionUserId !== clientId) {
setAuth(false);
} else {
setAuth(true);
}
async function asyncStringify(str) {
return JSON.parse(JSON.stringify(str));
}
const awaitJson = await asyncStringify(data?.profileData.subnode)
setSubnodes(awaitJson);
setLoading(false)
}
fetchingData();
}, []);
I need to save to db inside an async waterfall series.
I've tried to integrate these two function after the clean function
function connectDb(next) {
pool.getConnection(function(err, connection) {
if (err) console.log(err);
conn = connection;
}, next);
},
function saveDb(next) {
let sql = "UPDATE media SET media_url = ? WHERE media_url = ?";
conn.query(sql, [dstKey, srcKey], function (error, results, fields) {
if (error) {
conn.release();
console.log(error);
}else{
console.log("media db updated");
}
}, next)
}
The problem is that these two functions block the code execution. How can I integrate it in the function below? I've tried to wrap the function in promise but it is also not working.
async.waterfall([
function download(next) {
s3.getObject({
//param
},
next);
},
function transform(response, next) {
resizeMedia(response.Body ).then( ( file ) => { next();} ).catch( (err) => { reject(err) } ); }
},
function upload(next) {
var fileData = fs.createReadStream('/tmp/'+dstKey);
if (isVideo ) { var ContentType = 'video/mp4' }
if (isAudio ) { var ContentType = 'audio/mp3' }
s3.putObject({
//param
},
next);
},
function clean(next) {
// Stream the transformed image to a different S3 bucket.
fs.unlinkSync('/tmp/'+dstKey);
s3.deleteObject({
//param
},
next);
}
], function (err) {
if (err) {
console.error('Error');
callback(null, "Error");
return;
} else {
console.log('Success');
callback(null, "Done");
return;
}
callback(null, "Done");
return;
}
);
The purpose of async waterflow is to block the waterfall until the callback is called.
P.S. Usually you should not create a new db connection each time. The connection should be done once when the application start and get used whenever you need.
I highly recommend you to use knex.js instead, it return promises by default and if you like to use it inside async waterfall (and wait for resolve) you can call .asCallback.
I've found the problem, if someone fall into the same issue here my solution:
If a waterfall function has a response, this response is automatically added as first argument in the next function. In my code the mistake was simple (after night's sleep), the s3.deleteObject and s3.putObject has response, this response need to be setted as first argument and the callback as last, as you say I've used only the callback as argument (next) and this broke my code.
[...]
function upload(next) {
s3.putObject({
//param
},
next);
},
function clean(response, next) { // response in arguments
s3.deleteObject({
//param
},
next);
}
[...]
I've found several examples on S/O and otherwise, but they don't seem to be helping me.
I'm creating a private module in node that takes in a csv and converts it into a json object. It is properly outputting the correct value onto the command line, but the object itself is undefined.
exports.csvToJSON = function(file, callback) {
converter.fromFile(file, function(err, result) {
if (err) {
console.log('error: ', error)
} else {
return result;
callback(err, result);
}
});
console.log(callback)
}
I'm currently using the csvtojson module and have tried other similar packages. Articles I've referenced:
Function Returns undefined object
Why does this callback return undefined?
Why is function return value undefined when returned in a loop? (although not entirely relevant as this is not a loop function)
Callback returning undefined
I'm unsure if I'm just not understanding the callback correctly, but even if I console.log(result.type), it returns back undefined with or without the callback. I've also tried defining the callback like so:
exports.csvToJSON = function(file, callback) {
csvtojson().fromFile(file, function(err, result) {
if (err) {
console.log('error: ', error)
}
return result;
callback(result);
});
}
Here's an example of the console output:
Mirandas-MacBook-Pro:zendesktool mirandashort$ node ./controllers/update.js
[ { ticket:
{ ids: '3280805',
requester_email: 'miranda#barkbox.com',
status: 'pending',
comment: [Object],
subject: 'sup dog',
custom_fields: [Object],
add_tags: 'update_via_csv, dogs_are_cool, sup' } } ] undefined
Right now, since my other functions are dependent on this working, I'm only calling it in the file with exports.csvToJSON('update2.csv') where update2.csv is an actual file. Eventually this will be called inside another function in which I will be utilizing async, but I need this to work first. Additionally, that output seems to be linked to console.log(err) when called by the second code block example, which I'm not to sure why.
Or if there's a way to do this altogether without csvtojson, that's fine too. The only requirement be that a file in csv format can be returned as an object array.
Got it. I just used waterfall to put the two individual modeles together:
exports.csvToJSON = function(file, callback) {
csvtojson().fromFile(file, function(err, result) {
if (err) {
console.log(err);
} else {
callback(null, result[0]);
}
});
}
exports.zdUpdateMany = function(data, callback) {
credentials.config.tickets.updateMany(3280805, data, function(err, result) {
if (err) {
console.log(err);
} else {
callback(false, result);
}
});
}
// function to update tickets
exports.processUpdate = function(file, callback) {
async.waterfall([
async.apply(exports.csvToJSON, file),
exports.zdUpdateMany
], function(err, result) {
if (err) {
console.log(err);
} else {
console.log(result);
}
callback(err, result);
});
}
I'm quite new to NodeJS and JS globally and I'm in trouble while setting and Object Property through a MySQL query.
I'm using Promise to avoid bad asynchronous effect but apparently I'm doing it wrong, the property of my Agent Obejct is never updated.
Here is the code :
class Agent {
constructor(agentId, agentName, agentCountry) {
this.agentId = agentId;
this.agentName = agentName;
this.agentCountry = agentCountry;
}
setAgentCountry () {
var promise = function(agentID) {
return new Promise(function(resolve, reject) {
var query = "SELECT c.CountryID, c.CountryName FROM AgentCountry ac, Country c WHERE ac.AgentID = '" + agentID + "' AND ac.CountryID = c.CountryID";
connection.query(query, function(err, results) {
if (!err) {
resolve(results);
} else {
console.log('Error while performing Query.');
}
});
});
}
promise(this.agentID).then(function(data) {
var string = JSON.stringify(data);
var json = JSON.parse(string);
//the agent property is never updated !!
this.agentCountry = json;
}.bind(this), function(err) {
console.log(err);
});
}
}
I call the method this way :
var agent = new Agent(1,"John Doe", "France");
console.log(agent.agentCountry); //Displays "France"
agent.setAgentCountry();
console.log(agent.agentCountry); //Did not display the table of countries it should
Could you help me with this ?
Thanks
The main problem is that console.log is being executed before the promise being resolved. Writing a console.log inside the "then" clause will show you the timing.
The promise will be resolved or rejected eventually but nobody is waiting for setAgentCountry.
There are several points of order here:
A promise must always be either (1) resolved or (2) rejected. Your error case logs it to the console without calling reject(), so it's stuck in promise limbo for forever when it errors.
Why do you name a variable, promise, the same as the library, Promise?
I think you will find it more modular to just wrap the mysql_conn.query() callback into a promise():
const mysql_conn = mysql.createConnection({
host: mysql_conf.host,
user: mysql_conf.user,
password: mysql_conf.password
});
mysql_conn.queryPromiser = function(sql, args) {
return new Promise(function(resolve, reject) {
mysql_conn.query(
sql,
args,
function(err, results, fields) {
if (err) {
reject(err);
} else {
resolve( {"results": results, "fields": fields} );
}
}
);
});
};
then you can use it like so:
class Agent {
constructor(agentId, agentName) {
this.agentId = agentId;
this.agentName = agentName;
this.agentCountry = null;
}
configureCountryPromiser() {
var sql = "SELECT country FROM agent_countries WHERE agent_id = ?";
var args = [ this.agentId ];
var that = this;
return mysql_conn.queryPromiser(sql, args)
.then(function(data) {
if (data.results.length) {
that.agentCountry = data.results[0].country;
} else {
// handle case where agent_id is not found in agent_countries
}
});
}
};
agent_instance = new Agent(1, "Benoit Duprat");
agent_instance.configureCountryPromiser()
.then(function() {
console.log("agent country configured to ", agent_instance.agentCountry);
}).catch(console.error);
Please note that I have not tested the class code, but the general idea should be enough to answer your question.
I created 4 Lambda functions to process information that will be written into a MySQL table. the first three function just select, insert and update a MYSQL table record respectively.
I then created a 4th function to accept the record detail as part of the event parameter. This function will first try to select the record by invoking the first lambda function and if it finds it, will update the record on the table using the update lambda function. If it does not find it, it will invoke the insert function to add the record. I am using pool.query on the 3 functions that manipulates the MySQL table. I am also using lambda.invoke to call those three functions from the 4th function.
I was able to successfully test the 4th function locally by passing the record details as parameter and it was able to successfully call the three Lambda function and update the mySQL table record. The problem that I am having is that when I upload the function in AWS Lambda, it does not invoke any of the three functions. I am not seeing any errors in the log so I don't know how to check where the problem is. Here's ,y code that invokes the other functions:
exports.handler = (event, context, callback) => {
var err = null;
var payload = {
qryString : event.qryString,
record: event.updaterecord,
dbConfigPool : event.dbConfigPool
}
var params = {
FunctionName: 'getInventory',
Payload: JSON.stringify(payload)
}
console.log(' before invoke ' + JSON.stringify(params) )
lambda.invoke(params, function(err, data) {
console.log(' aftr invoke ' + JSON.stringify(params) )
if (err) {
console.log('err ' + err, err.stack); // an error occurred
event.message = err + ' query error';
}
else {
console.log('success' + JSON.stringify(data));
console.log(' status code ' + JSON.stringify(data.StatusCode));
console.log(' Payload ' + JSON.stringify(JSON.parse(data.Payload)));
var rowsTemp = JSON.parse(data.Payload);
var rows = rowsTemp.data;
if (!rowsTemp.recordExist) {
console.log('insert')
// Update inventory record only if quantity is not negative
var newQuantity = 0
newQuantity = parseFloat(event.updaterecord.quantity);
if (Math.sign(newQuantity) === 1) {
var payload = {
record: event.updaterecord,
dbConfigPool : event.dbConfigPool
}
console.log('insert' + JSON.stringify(payload));
var params = {
FunctionName: 'insertInventory',
Payload: JSON.stringify(payload)
}
lambda.invoke(params, function(err, data) {
if (err) console.log(err, err.stack); // an error occurred
else console.log(data); // successful response
});
}
}
else {
newQuantity = 0
newQuantity = parseFloat(event.updaterecord.quantity) + parseFloat(rows[0].quantity);
if (Math.sign(newQuantity) === 1) {
event.updaterecord.quantity = newQuantity;
} else {
// Set to zero if the result is negative
event.updaterecord.quantity = 0;
}
console.log('value ' + JSON.stringify(newQuantity) + ' updaterecord' + JSON.stringify(event.updaterecord.quantity) );
var payload = {
qryString : event.qryString,
record: event.updaterecord,
dbConfigPool : event.dbConfigPool
}
console.log('update' + JSON.stringify(payload));
var params = {
FunctionName: 'updateInventory',
Payload: JSON.stringify(payload)
}
console.log(' before invoke ' + JSON.stringify(params) )
lambda.invoke(params, function(err, data) {
console.log(' after invoke ' + JSON.stringify(params) )
if (err) {
console.log('err ' + err, err.stack); // an error occurred
event.message = err + ' query error';
} else {
console.log(data);
} // else
}); // lambda invoke
}
} // successful response
});
console.log(' end of function');
var completed = true;
context.callbackWaitsForEmptyEventLoop = false;
callback(null, completed);
}
Apologies if the code is quite long. But I wanted to show that I did put a number of console.logs to monitor where is goes through. The cloudwatch logs only shows the first message before the first lambda.invoke and then it shows the last message for the end of the function.
I am also not seeing any log entries in cloudwatch for the three functions that has been invoked.
06/17
ok, since I am still unable to make this work, I simplified the code to just the following:
exports.handler = (event, context, callback) => {
var err = null;
var updatedRecord = false;
var responseDetail = {};
var payload = {
qryString : event.qryString,
record: event.updaterecord,
dbConfigPool : event.dbConfigPool
}
var params = {
FunctionName: 'getInventory',
Payload: JSON.stringify(payload)
}
console.log(' before invoke ' + JSON.stringify(params));
lambda.invoke(params, function(err, data) {
if (err) {
event.message = err + ' query error';
callback(err,event.message);
}
else {
console.log('success' + JSON.stringify(data));
console.log(' status code ' + JSON.stringify(data.StatusCode));
console.log(' Payload ' + JSON.stringify(JSON.parse(data.Payload)));
callback(null, data);
} // successful response
});
console.log(' end of function');
// var completed = true;
// context.callbackWaitsForEmptyEventLoop = false;
// callback(null, completed);
}
However, the function is timing out when I do my test. I have also given the role attached to the functions full Lambda and RDS access.
First of all - welcome to callback hell! I will return to this later.
This is a simple code that invokes a lambda function.
var params = {
FunctionName: 'LAMBDA_FUNCTION_NAME', /* required */
};
lambda.invoke(params, function(err, data) {
if (err) {
console.log(err, err.stack); // an error occurred
}
else {
console.log(data); // successful response
}
});
lambda.invoke function has two parameters (params, function(err,data){..}). The first one is a simple JSON object. The second one is a function - a callback function. This function will be "called back" when the execution of lambda.invoke (you could think LAMBDA_FUNCTION_NAME) ends. If an error occurs it would be "stored" in err variable otherwise returned data will be stored in data variable (This is not the right explanation but I am trying to keep it simple here).
What happens when you want to invoke two lambda functions one after another?
var params1 = {
FunctionName: 'LAMBDA_FUNCTION_1_NAME', /* required */
};
lambda.invoke(params1, function(err, data) {
if (err) {
console.log(err, err.stack); // an error occurred
}
else {
console.log('Lambda function 1 invoked!');
console.log(data); // successful response
}
});
var params2 = {
FunctionName: 'LAMBDA_FUNCTION_2_NAME', /* required */
};
lambda.invoke(params2, function(err, data) {
if (err) {
console.log(err, err.stack); // an error occurred
}
else {
console.log('Lambda function 2 invoked!');
console.log(data); // successful response
}
});
console.log('I am done!');
Depending on execution time of LAMBDA_FUNCTION_1_NAME and LAMBDA_FUNCTION_2_NAME you could see different outputs like:
Lambda function 1 invoked!
I am done!
or
Lambda function 1 invoked!
Lambda function 2 invoked!
I am done!
or even
Lambda function 1 invoked!
I am done!
Lambda function 2 invoked!
This is because you are calling lambda.invoke and after that (without waiting) you are calling lambda.invoke again. After that (of course without waiting) the previous functions to end you are calling console.log('I am done!');
You could solve this by putting each function in previous function's callback. Something like this:
var params1 = {
FunctionName: 'LAMBDA_FUNCTION_1_NAME', /* required */
};
lambda.invoke(params1, function(err, data) {
if (err) {
console.log(err, err.stack); // an error occurred
}
else {
console.log('Lambda function 1 invoked!');
console.log(data); // successful response
var params2 = {
FunctionName: 'LAMBDA_FUNCTION_2_NAME', /* required */
};
lambda.invoke(params2, function(err, data) {
if (err) {
console.log(err, err.stack); // an error occurred
}
else {
console.log('Lambda function 2 invoked!');
console.log(data); // successful response
console.log('I am done!');
}
});
}
});
That way your output would be:
Lambda function 1 invoked!
Lambda function 2 invoked!
I am done!
But if you want to invoke 3 or more functions one after another you will end up with nested code. This is the callback hell. You could re-write you code in that way. But in my opinion it is a good idea to check waterfall async library
async.waterfall([
function(callback) {
callback(null, 'one', 'two');
},
function(arg1, arg2, callback) {
// arg1 now equals 'one' and arg2 now equals 'two'
callback(null, 'three');
},
function(arg1, callback) {
// arg1 now equals 'three'
callback(null, 'done');
}
], function (err, result) {
// result now equals 'done'
})
Pseudo code should look like this:
async.waterfall([
function(callback1) {
var params1 = {
FunctionName: 'LAMBDA_FUNCTION_1_NAME', /* required */
};
lambda.invoke(params1, function(err, data) {
if (err) {
console.log(err, err.stack); // an error occurred
}
else {
console.log('LAMBDA_FUNCTION_1_NAME finished!');
callback1(null,data);
}
});
},
function(result_from_function_1, callback2) {
console.log(result_from_function_1); // outputs result from LAMBDA_FUNCTION_1_NAME
var params2 = {
FunctionName: 'LAMBDA_FUNCTION_2_NAME', /* required */
};
lambda.invoke(params2, function(err, data) {
if (err) {
console.log(err, err.stack); // an error occurred
}
else {
console.log('LAMBDA_FUNCTION_2_NAME finished!');
callback2(null,data);
}
});
},
function(result_from_function_2, callback3) {
console.log(result_from_function_2); // outputs result from LAMBDA_FUNCTION_2_NAME
var params3 = {
FunctionName: 'LAMBDA_FUNCTION_3_NAME', /* required */
};
lambda.invoke(params3, function(err, data) {
if (err) {
console.log(err, err.stack); // an error occurred
}
else {
console.log('LAMBDA_FUNCTION_3_NAME finished!');
callback3(null,data);
}
});
}
], function (err, result) {
// result now equals LAMBDA_FUNCTION_3_NAME result
})
Please note that all callbacks (callback1, callback2 and callback3) could be with name "callback" only. I changed their names for better understanding.
Think about what this does:
lambda.invoke(params, function(err, data) { ...
It starts "doing something" (the fact that happens to be invoking another lambda function is actually unimportant) and when "something" is done, it calls function(), right?
But it also returns immediately, and the next statement executes.
Meanwhile "something" is being handled by the asynchronous event loop.
The "next statement" is
console.log(' end of function');
Then, you're telling lambda "hey, I may have some async stuff going on, but don't worry about waiting for it to finish":
context.callbackWaitsForEmptyEventLoop = false;
So the good news is your code is doing what is was written to do... but that turns out to be wrong.
Everywhere you have one of these two things...
// an error occurred
// successful response
...those are the places you should be calling callback(), not at the end of the handler function, which your code reaches very quickly, as it is supposed to.
You shouldn't need to use context.callbackWaitsForEmptyEventLoop = false; if you properly disconnect your database connections and all modules you include are well-behaved. While that has its purposes, there seem to be a lot of people using it to cover up for subtle unfinished business.
Or, for a tidier solution, where you only have one mention of the callback at the end and your function { function { function { nesting doesn't get so out of control, use async-waterfall.