Overview
I'm developing an MVC application with NodeJS. When the application loads for the first time, the database object (using a pool) is created.
var pool = mysql.createPool({connectionLimit: 150, host: __host,
user: __user, password: __password,
database: __database})
module.exports = pool
When a request is received, a Controller object is created, which creates a Model to perform actions. The model gets a connection from the pool, performs the action, and releases the connection back to the pool.
//router snippet
router.get('/post_data', function(req, res){
router.setRequestAndResponse(req, res)
var post_data = new Post_Data()
post_data.processDataFromGet(router)
})
//controller code snippet
Post_Data_Controller.prototype.processDataFromGet = function(router){
var controller_obj = this
var data_array = {}
var req = router.req, res = router.res
//retrieving data from request and passing to the data_array
controller_obj.model.create(data_array, function(result){
var xml = xmlbuilder.create("response")
if (result.code == "error"){
xml.e("code", "error")
xml.e("message", result.error_message)
}else if (result.code == "success"){
xml.e("code", "success")
}
controller_obj.sendResponse(router.res, xml, "xml")
})
}
Post_Data_Controller.prototype.sendResponse = function(res, response, type){
if (type == "json"){
res.set({"Content-Type": "application/json", "Content-Length": JSON.stringify(response).length})
res.send(response)
}else{ /* Default type is XML */
res.set({"Content-Type": "application/xml", "Content-Length": response.end({pretty: true}).length})
res.send(response.end({pretty: true}))
}
}
//Model snippet
Post_Data.prototype.create = function(data_array, callback){
/* data validation */
var fail = false, error_data = {}
if (fail) {callback({code: "fail", cause: error_data}); return;}
//the next 2 lines do not throw an error when uncommented
//callback({code: "fail", cause: "how's it going"});
//return;
__db_pool.getConnection(function(err, db_conn){
// the next two lines throw an error for two or more requests coming in at the same time
callback({code: "fail", cause: "how's it going"});
return;
if (err) { callback({code: "error", error_message: err}); return;}
callback({code: "fail", cause: "how's it going"});
return;
db_conn.query("sql command", [data_array],
function(err, result){
if (err){ callback({code: "error", error_message: err}); return;}
if (result && result.length > 0){ //affiliate and listing exist
data_array.listing_id = result[0].listings_id
var data = [data_to_insert]
db_conn.query("sql command here", data,
function(err, result){
db_conn.release()
if (err){ callback({code: "error", error_message: err}); return;}
if (result && result.affectedRows > 0) {
callback({code: "success", data: {data_to_be_returned}})
}else {callback({code: "error", error_message:"Error inserting data"}); return}
})
}else{
callback({code: "fail", cause: "error to send back"})}
})
})
}
Problem
These requests are web service requests. If I send one GET request, no error happens; however, when I send two or more concurrent requests, I receive this error:
/project_path/node_modules/mysql/lib/protocol/Parser.js:82
throw err;
^
Error: Can't set headers after they are sent.
at ServerResponse.OutgoingMessage.setHeader (http.js:689:11)
at ServerResponse.res.set.res.header (/project_path/node_modules/express/lib/response.js:549:10)
I traced the culprit to the specific line in the Model code pasted above. It seems that for some reason, once the model obtains a connection from the pool for the second request, it somehow interferes with the first request. Both requests still insert the proper data into the database; however, the second and subsequent requests can't send a response without throwing an error anymore.
I have performed the requests with GET, POST, and PUT content-types; only GET throws the error. All the other content-types don't throw any error, even with over one thousand concurrent requests.
Here's the web service code for the GET requests; it's the same for the other content-types except for the content-type changes and the data being put in the body.
for(var i=0; i less than 5; i++){
sendAsGet()
i++
}
function sendAsGet(){
try{
var data = "?data_to_be_sent"
var uri =url.parse("http://localhost:4000/post_data")
var options = {hostname: uri.hostname, port: uri.port, method: "GET",
path: uri.path + data, agent: false}
request = (uri.protocol == "https")? https : http
var req = request.request(options, function(res){
var result = ""
console.log("STATUS: " + res.statusCode)
console.log("HEADERS: " + JSON.stringify(res.headers))
res.setEncoding("utf8")
res.setTimeout(50, null)
res.on("data", function(chunk){
result += chunk
})
res.on("end", function(){
console.log(result)
})
})
req.end()
}catch(err){
console.log(err.message)
}
}
I would like to know 2 things:
Why is getting the database connection causing this problem?
Why does it happen only on GET requests and not on POST and PUT?
Google and previous SO questions haven't been able to help so far.
Thanks.
The reason you are seeing the error is because you're placing request/response instances on the router itself. Don't do that. The router object is a "static" object, it's not a per-request thing. So currently this is what's happening (in order):
Request #1 comes in and sets req/res on router and starts the asynchronous model.create().
Meanwhile, request #2 comes in and overwrites req/res on router and starts its own asynchronous model.create().
Request #1's model.create() callback is called, sending the response to request #2's socket instead.
Request #2's model.create() callbacks is called, where it attempts to send a response to the same res that was just responded to just a moment ago. Trying to write headers to a response that has already been sent then results in the error you are seeing.
Related
I'm using the serverless apigateway websockets and I can successfully get messages sent back and forth between the lambda function and client.
However I can't figure out how to get my function to send only a single message. It's currently sending two messages due to the callback at the end of the function. This is more of a nodejs issue, but I've been trying for the past couple of hours to figure out how, but can't seem to.
var params2 = {
TableName: "UserConnections",
FilterExpression: "cameraId = :val",
ExpressionAttributeValues: { ":val": {"S" : JSON.parse(event.body).data.camera_id}}
};
DDB.scan(params2, function(err, data) {
if (err) throw err;
console.log("DATA: " + JSON.stringify(data));
for(var i = 0; i<data['Items'].length; i++){
var id = data['Items'][i]['connectionId'].S;
console.log("List of connection ids: " + id);
var params3 = {
ConnectionId: id,
Data: JSON.stringify(message)
};
apigatewaymanagementapi.postToConnection(params3, function(err, data) {
if (err){
throw err; // an error occurred
}else{
console.log("Success sending message to clients: " + JSON.stringify(data));
}
});
}
});
callback(null, {
statusCode: 200,
body: "Message Processed in Lambda!"
});
In postToConnection method, it sends a message back to multiple users, and the callback function sends the body to the same users. How can I just send the params3 back to the users and not use the callback to end the function
Edit1:______________________________________________
Adding
callback(null, {});
Still sends two messages except the second one is now empty. How can I get it to strictly send only one message
Return with a empty object, the return value is ignored when this function is invoked from WebSocket gateway
return {}; // callback(null, {});
I've developed a nodeJS API (using express) which allow users to login and get a list of files that they have stored in a remote server. And as you understand, the code must be non-blocking so the webserver can still responds to logging in requests, even if there are some users fetching theirs files lists.
Every time a user make a request to get his files list, the listOfFiles function is called.
This is the code:
exports.listOfFiles = function(req,res){
db.Account.find({where: {id:1}}).then(function(newAcc){
console.log("encontrou a account");
getFiles('/', newAcc.accessToken, '0', newAcc, function(error){
if (error) {
log.error('Error getting files');
}else{
console.log("callback!")
}
});
});
}
getFiles function: this function is responsible for fetching the file list from the remote server, and store them in a postgres database
function getFiles(path, accessToken, parentID, newAcc, callback){
var client = new ExternalAPI.Client({
key: config.get("default:clientId"),
secret: config.get("default:clientSecret")
});
client._oauth._token = accessToken;
var options = {
removed : false,
deleted : false,
readDir: true
}
//this is the instruction that fetch an array of items
//(metadata only) from a remote server
client.stat(path, options, function(error, entries) {
if (error) {
if (error.status == 429) {
console.log(accessToken + 'timeout')
setTimeout(
getFiles(path, accessToken, parentID, callback),
60000);
}else{
log.error(error);
callback(error,null);
}
}
else {
//When the array os items arrives:
console.log("RECEIVED FILES")
var inserted = 0;
var items = entries._json.contents;
for(var file in items){
var skyItemID = uuid.v1();
var name = items[file].path.split('/').pop();
var itemType;
if (items[file].is_dir) {
itemType = 'folder';
}else{
itemType = 'file';
}
newAcc.createItem({
name : name,
lastModified: items[file].modified,
skyItemID: skyItemID,
parentID: parentID,
itemSize: items[file].bytes,
itemType : itemType,
readOnly: items[file].read_only,
mimeType: items[file].mime_type
}).then(function(item){
console.log(item.name)
if (++inserted == items.length) {
console.log(inserted)
console.log(items.length)
console.log("callsback")
callback();
}
}).catch(function(error){
log.error('[DROPBOX] - Filename with special characters');
callback(new Error);
return;
});
}
}
});
}
The problem here is, the moment that webserver prints console.log("RECEIVED FILES") in our console, it stops responding to all other requests, such as log in or fetch files requests from other users.
And it starts responding again when it prints console.log("callback!"). So, i'm assuming that somehow nodeJS is blocking itself until getFiles function is finished and called back.
I think that this is not a normal behaviour. Shouldn't nodeJS be responding to responds to other requests even if there are some operations running in background? Shouldn't getFiles function being run in background and not affecting/blocking all other requests? What am I doing wrong?
Thanks!
I am facing the same kind of problem for long time server http request blocks the service for response other client requests. This is my topic. What is the correct behavior for Node.js while Node.js is requesting a long time http request for other servers Currently, I got no answer for that. If you got the answer, please reply me. Thanks.
I am trying to process Wikipedia articles, and want to receive a list of all Wikipedia articles. In order to do this I am frequently sending http requests to the Wikipedia API, which allows you to receive 500 titles at time and also returns an apcontinue string, which, when used in the following request, returns title starting from that string.
In order to do this, I am using the agentkeepalive module:
var http = require('http');
var Agent = require('agentkeepalive');
var keepaliveAgent = new Agent({
keepAlive: true,
maxSockets: 5,
timeout: 5000,
keepAliveTimeout: 3000
});
To send an http request to Wikipedia, I use the following code:
function wikipediaApiCall(params, callback) {
var options = {
host: 'en.wikipedia.org',
path: '/w/api.php?' + createParamString(params),
method: 'GET',
agent: keepaliveAgent
};
var callbackFunc = function(response) {
var err;
var str = '';
if (('' + response.statusCode).match(/^5\d\d$/)) {
err = new Error('Server error');
}
//another chunk of data has been recieved, so append it to `str`
response.on('data', function (chunk) {
str += chunk;
});
response.on('error', function (e) {
err = new Error('Request error');
});
response.on('timeout', function () {
err = new Error('Timeout');
response.abort();
callback(err);
});
response.on('end', function () {
var obj = JSON.parse(str);
if (obj.warnings) {
err = new Error('Request error');
}
callback(err, obj);
});
}
var req = http.request(options, callbackFunc);
req.setTimeout(5000);
req.on('error', function(err) {
callback(err, null);
return;
});
req.on('timeout', function () {
err = new Error('Timeout');
response.abort();
callback(err);
});
req.on('finish', function(){
console.log('ended');
});
req.end();
}
However, after sending between 16 and 20 request, I am not getting any response, but my request also does not time out.
Any ideas why this is happening?
Update
The request I send to Wikipedia contains the following parameters:
var params = {
list: 'allpages',
aplimit: limit,
apfrom: from,
continue: cont,
// apfilterredir: 'nonredirects'
};
Interestingly, after leaving out the nonredirects setting, I was able to send and receive up to 330 requests, but no more than that.
Update 2
I was able to register a finished event. It appears to be fired for the request that is failing as well. I modified the code accordingly.
Perhaps you need a bot flag to have higher API limits. Maybe there are too many requests in parallel; WMF recommendation is to make requests serially in case of such big tasks. Also, you should use the maxlag parameter with low values, per WMF API Etiquette.
I have some really strange problem with nodejs and express.
One of my functions that handles request, have to get something from DB and send it as JSON to client.
So it goes like this:
Get request
Call DB
Process data and pack it to JSON
response.json(JSON)
Normally it will go all OK but if there is timeout between 2 and 3 because it is asynchronously it will automatically create response and there will be error "Can\'t set headers after they are sent." when I call 4
Does anyone else have this problem? Is there any normal way to handle it or I just have to check if response._header is allready set?
exports.appstimebygroup = function (req, res) {
var resp = {};
var clientId = Webapi.extractClientId(req);
AppTime.getByGroupId(clientId, req.body.groupId, function(error, appstime){
if (error) {
handleError(error);
resp.returnCode = 0;
resp.message = "Some error have happened, please contact support!";
res.setHeader("Content-Type", "application/json");
res.json(resp);
return;
}
resp.returnCode = 1;
resp.appstime = appstime;
if(res._header){
console.log("header allready set!");
return;
}
res.setHeader("Content-Type", "application/json");
res.json(resp);
});
};
And AppTime.getByGroupId has asynchronous call inside.
Ok, problem is multipart-form-data handling timeout.
When that happens it calls next(err).
form.on('error', function(err){
if (!options.defer) {
err.status = 400;
next(err);
}
done = true;
});
By default on error it will do res.send(400) and when normally gets to code that you wanted to be executed there is problem.
I probably have some issues with the asyncness of Node.js.
rest.js
var Shred = require("shred");
var shred = new Shred();
module.exports = {
Request: function (ressource,datacont) {
var req = shred.get({
url: 'ip'+ressource,
headers: {
Accept: 'application/json',
},
on: {
// You can use response codes as events
200: function(response) {
// Shred will automatically JSON-decode response bodies that have a
// JSON Content-Type
if (datacont === undefined){
return response.content.data;
//console.log(response.content.data);
}
else return response.content.data[datacont];
},
// Any other response means something's wrong
response: function(response) {
return "Oh no!";
}
}
});
}
}
other.js
var rest = require('./rest.js');
console.log(rest.Request('/system'));
The problem ist if I call the request from the other.js I always get 'undefined'. If I uncomment the console.log in rest.js then the right response of the http request is written to the console. I think the problem is that the value is returned before the actual response of the request is there. Does anyone know how to fix that?
Best,
dom
First off, it is useful to strip down the code you have.
Request: function (ressource, datacont) {
var req = shred.get({
// ...
on: {
// ...
}
});
}
Your Request function never returns anything at all, so when you call it and console.log the result, it will always print undefined. Your request handlers for the various status codes call return, but those returns are inside of the individual handler functions, not inside Request.
You are correct about the asynchronous nature of Node though. It is impossible for you to return the result of the request, because the request will still be in progress when your function returns. Basically when you run Request, you are starting the request, but it can finish at any time in the future. The way this is handled in JavaScript is with callback functions.
Request: function (ressource, datacont, callback) {
var req = shred.get({
// ...
on: {
200: function(response){
callback(null, response);
},
response: function(response){
callback(response, null);
}
}
});
}
// Called like this:
var rest = require('./rest.js');
rest.Request('/system', undefined, function(err, data){
console.log(err, data);
})
You pass a third argument to Request which is a function to call when the request has finished. The standard Node format for callbacks that can fail is function(err, data){ so in this case on success you pass null because there is no error, and you pass the response as the data. If there is any status code, then you can consider it an error or whatever you want.