I have a MySQL, Express, Angular, NodeJS application and sometimes when I log in I get the following error in my node console:
TypeError: Cannot read property 'query' of undefined
The error occurs in my passport.local.js file, this is the line:
connection.query('SELECT * FROM users WHERE username LIKE ?', [username], function (err, user) {
This is the passport function
passport.use(new LocalStrategy(
function(username, password, done) {
console.log('app.js');
pool.getConnection(function(err, connection) {
console.log('err: ' + err);
console.log(connection);
connection.query('SELECT * FROM users WHERE username LIKE ?', [username], function (err, user) {
if (err) throw err;
for (var i = user.length - 1; i >= 0; i--) {
var current = user[i];
}
if(current){
if(bcrypt.compareSync(password, current.password)){
return done(null, user);
} else {
return done(null, false);
}
} else {
console.log('no user');
return done(null, false);
}
});
connection.release();
});
}
));
I require my pool in the top of my file
var pool = require('../../config/connection');
When the error occurs the:
console.log(connection);
Gets:
undefined
I also log the error:
console.log('err: ' + err);
Shows:
err: Error: ER_USER_LIMIT_REACHED: User 'bfe4a8980ede74' has exceeded the 'max_user_connections' resource (current value: 10)
I am assuming that your max_user_connections is set to 10. Please increase the max_user_connection value.
show global variables like '%connections%';
Will help you in giving the no of connections you have set. Increase the number of connections If its less than 25 or 50. Max connections can be more than 16000 I guess, it all depends on your cpu, no of threads It can handle etc.
SET GLOBAL max_user_connections=100;
max_user_connections is a dynamic variable, which means you can directly run this query. You donot have to shutdown mysql.
The error you're getting is stating the issue: your MySQL server is only allowing 10 connection per user, and that limit has been reached.
The default for the mysql connection pool also happens to be 10, which is cutting it really close. If any other MySQL client besides your Express app is connected to the database with the same user credentials, you may run into that particular error. I would suggest increasing max_user_connections in the MySQL configuration.
Aside from that, there's another issue with your code: it's releasing the connection before the query has finished, which may lead to unexpected behaviour. Move the call to connection.release() to inside the callback:
pool.getConnection(function(err, connection) {
...
connection.query('SELECT * FROM users WHERE username LIKE ?', [username], function (err, user) {
connection.release();
...
});
});
If this is a common way you're using MySQL (get a connection, perform a query, release the connection), you can make life a bit easier by using pool.query() instead. See this example.
And finally, if you're working with async code, don't throw errors but pass them to the callback (and make sure that you actually handle them, because you're not handling any errors from pool.getConnection now, besides logging them):
pool.getConnection(function(err, connection) {
if (err) return done(err);
...
});
Related
This is the function in question:
castVote: function (req, res, ip, mysql) {
// POST return codes
const ALREADY_VOTED = '100';
const SUCCESS = '200';
const FAILURE = '300';
// Create connection to db
// Keep connection while the client is connected
var con = mysql.createConnection({
host: sqlHOST,
user: sqlUSER,
password: sqlPASS,
database: dbNAME
});
// Connect to db
con.connect(function (err) {
// MySQL error, return
if (err) {
res.send(FAILURE);
return;
}
});
// Get link voted for
var link = req.body.song_voted;
// Check if user has already voted
con.query(`SELECT * FROM VotedUsers WHERE ip = '${ip}'`, function (err, result, fields) {
if (err) {
res.send(FAILURE);
return;
}
// User hasn't voted
if (!result.length) {
// Cast vote here
// ...
// Add user's IP to voted list
con.query(`INSERT INTO VotedUsers (ip) VALUES ('${ip}')`, function (err, result) {
if (err) {
res.send(FAILURE);
return;
}
});
res.send(SUCCESS);
return;
}
// User already voted
else {
res.send(ALREADY_VOTED);
return;
}
});
}
I call the function like this, every time the user clicks a button
mysql_backend.castVote(req, res, uipv4, mysql);
(mysql is the mysql module. uipv4 is the user's ip).
Every time I click said button, This is the error output:
Error [ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent to the client
I've got 2 questions:
What's wrong with my code?
Is there a better way of managing a mysql connection for each client (not opening a new one every time the user clicks the button)
1. What is wrong with your code
I can see a couple of things.
Firstly, the most obvious problem is your con.connect and con.query functions.
You have them implemented incorrectly. con.connect takes a callback that all your implementation should be in i.e. all your query logic, once you have created a connection.
Anyway the con.connect function is not required. You can remove it as con.query will create a connection for you.
Secondly, and this is causing the error that you are seeing. You need to check your application to ensure that the res.send (or equivalent) is only being invoked once per request.
So for example, this is going to throw the error you are seeing.
// User hasn't voted
if (!result.length) {
// Cast vote here
// ...
// Add user's IP to voted list
con.query(`INSERT INTO VotedUsers (ip) VALUES ('${ip}')`, function (err, result) {
if (err) {
res.send(FAILURE);
return;
}
});
res.send(SUCCESS);
return;
}
If the insert fails, res.send(SUCCESS) has already being executed, then the con.query callback is executed and the res.send(FAILURE) will be called.
This is just one example. You will need to check your code through the entire request to ensure that a response is only ever sent once. If you send more than once you will see the ERR_HTTP_HEADERS_SENT (or a similar error - depending on the function you invoke on the response object). The key takeaway is that you should only send a response once and only once! If the below is not fixing your error you need to check the rest of your application to make sure that you are not sending a response again e.g. after you call mysql_backend.castVote(req, res, uipv4, mysql);
To fix the above problem depends on the desired result.
If you want to return to the caller that the error has occurred, you must wait until the callback has returned. e.g.
if (!result.length) {
// Cast vote here
// ...
// Add user's IP to voted list
con.query(`INSERT INTO VotedUsers (ip) VALUES ('${ip}')`, function (err, result) {
if (err) {
res.send(FAILURE);
return;
}
res.send(SUCCESS);
return;
});
}
2. Is there a better way of Managing Connections
Yes - use connection pools. Connection pooling allows you to limit the number of connections that can be created, e.g. if you have 100 parallel requests and a maximum pool size of 10, you will only ever create 10 connections.
I am making a Discord Level Bot, the bot will insert a random amount of XP each time a user's typed a message in the chat. To see a user's level I have a !level command. Like this :
sql.query(`SELECT * FROM WMembers where DiscordID = ${message.author.id}`, (err, rows) => {
if(err) console.log(err)
if(!rows[0]) return message.channel.send("The user has no XP!")
let xp = rows[0].XP
let level = rows[0].Level
let nextLevel = level * 40
message.channel.send(**Level: **${level - 1}\n**Points: **${xp} / ${nextLevel}`)
})
However, when I call the command more than 2-3 times,the queries start executing extremely slowly, taking 5 minutes to finally return the value.
Here is my sql code :
const pool = mysql.createPool({
host : keys.dbHost,
port : 3306,
user : keys.dbUser,
password: keys.dbPass,
database: keys.dbName
});
let sql = {};
sql.query = function(query, params, callback) {
pool.getConnection(function(err, connection) {
if(err) {
if (callback) callback(err, null, null);
return;
}
connection.query(query, params, function(error, results, fields) {
connection.release();
if(error) {
if (callback) callback(error, null, null);
return;
}
if (callback) callback(false, results, fields);
});
});
};
If someone can help me, I will greatly appreciate it. Thank you.
you clearly don't have enough memory for your database: https://dev.mysql.com/doc/mysql-monitor/4.0/en/system-prereqs-reference.html
MySQL's minimum requirements dictate 2GB of memory. you won't get far with 128MB. On that note, just like it has been adviced by #ExploitFate, limiting the number of connections your application can make to the database will also save you some memory
I am getting strange behavior using Node.JS and MySQL with this driver - https://github.com/mysqljs/mysql
Essentially, I have a button on the frontend that triggers an app.get that makes a query in the database and I can happily use the results in my backend.
This works nicely, until I press the button 4-5 times in a second, where as the queries lock up and I have to wait for 2-3 minutes until they continue executing. I have a similar write function that behaves the same way.
Is it possible this is a problem, because I'm trying to execute the exact same query asynchronously? I.e. do I have to limit this from the front end or is it a backend problem?
Any ideas on how to debug what exactly is going on?
// database.js
var mysql = require('mysql');
var pool = mysql.createPool({
connectionLimit: 100,
host : 'localhost',
user : 'secret',
password : 'secret',
database : 'mydb'
});
exports.getConnection = function(callback) {
pool.getConnection(function(err, connection) {
callback(err, connection);
});
};
// dbrw.js
var con = require('../config/database');
function read(id, done) {
con.getConnection(function(err, connection){
if(!err){
connection.query("SELECT * FROM users WHERE id = ?",[id], function(err, rows) {
connection.release();
if (err)
done(err);
if (rows.length) {
console.log("rows " + JSON.stringify(rows));
done(rows[0].progress);
};
});
}
else {
console.log(err);
}
});
}
exports.read = read;
// routes.js
var dbrw = require('./dbrw.js');
app.get('/read', isLoggedIn, function(req, res) {
dbrw.read(req.user.id, function(result) {
console.log(result);
});
});
// Frontend - angular app.js
$scope.tryread = function() {
$http.get('/read');
}
Thanks in advance for any input.
I see a few issues:
function read(id, done) {
con.getConnection(function(id, connection){...}
}
Notice how you overwrite the id passed to read by giving that same name to an argument of the callback to getConnection.
Also, your Express route doesn't actually end the request by sending back a response, which will make your browser time out the connection. At some point, it will even refuse to send more requests because too many are still pending.
So make sure to end the request:
app.get('/read', isLoggedIn, function(req, res) {
dbrw.read(req.user.id, function(result) {
console.log(result);
res.end(); // or `res.send(result)`
});
});
And a tip: you should use the callback calling convertion for Node, where the first argument represents an error (if there is any) and the second argument represents the return value.
I've been struggling to get MySQL working with node for a while. When I run the following code no errors are thrown, but simultaneously none of the console messages are being printed (except for the obvious one).
var app = require('express')();
var http = require('http').Server(app);
var mysql = require('mysql');
var connection = mysql.createConnection({
host : 'localhost',
user : 'root',
password : '******',
database : 'blogDB'
});
connection.connect(function(err) {
if(err) {
console.log('error when connecting to database:', err);
}
console.log('Connected to the database');
});
var queryString = 'SELECT * FROM blogs';
connection.query(queryString, function(err, rows, fields) {
if (err) throw err;
for (var i in rows) {
console.log('Post: ', rows[i].id);
}
});
connection.end();
http.listen(3306, function(){
console.log('listening on *:3306');
});
Output:listening on *:3306
On top of this, when I go to "localhost:3306" in the browser, a download is immediately started and nothing appears on the web page. The download is a file with no extensions, but contained the following:
J
5.6.19 tscvKP3M ÿ÷ € g?F!q6X:Y2*z mysql_native_password ! ÿ„#08S01Got packets out of order
I am not sure if that is relevant, but it certainly was not happening when I was not running MySQL. I have no idea how to troubleshoot this. Any ideas what could be going wrong?
The error here is you're coding node.js as if it were procedural. It's not.
connection.connect(function(err) {
if(err) {
console.log('error when connecting to database:', err);
}
console.log('Connected to the database');
var queryString = 'SELECT * FROM blogs';
//change from connection to "this" because you're inside the context of the connection object now
this.query(queryString, function(err, rows, fields) {
if (err) throw err;
for (var i in rows) {
console.log('Post Titles: ', rows[i].id);
}
});
});
Node.js uses a series of callbacks that run when a task is completed. So when you want to do something AFTER you're connected to the DB, you run that code inside the callback.
What your code is doing is attempting to connect to the database, then while attempting to connect to the database you're querying a database you're not connected to, and so on and so forth.
For sake of illustrating the principle a little more, node functions use the following general methodology.
//1
myObj.myFunc( function( err , foo , bar ) {
//A
});
//2
myObj.myOtherFunc( function( err , someVar ) {
//B
});
1 will always run before 2. A and B may run in either order depending on when 1 and 2 finish executing. A will always run after 1 is done. B will always run after 2 is done.
Hopefully that helps clear things up ;)
As it turns out, MySQL and the app were running using the same port (3306). Changing the app's port to 3307 did the trick.
I am writing a nodejs application and want to use connection pooling.
However, the following application does not terminate - although I would expect it to terminate after the call to connection.end()
Application works just fine, if I use one connection instead of the pool. Do I need to terminate the pool in some way?
Library used: https://github.com/felixge/node-mysql
node.js version: 0.10.4 on Ubuntu
var mysql = require('mysql');
var pool = mysql.createPool({
host : 'example.org',
user : 'myuser',
password : 'youbet',
database : 'notrevealingdetails',
insecureAuth: true
});
function getCampaignData(callback)
{
pool.getConnection(function(err, connection) {
if(err) throw err;
connection.query(
'SELECT cam.id, cam.name AS campaign_name, cam.subdomain, usr.email, usr.display_name AS user_displayname ' +
'FROM campaigns AS cam INNER JOIN users AS usr ON usr.id = cam.user_id ' +
'WHERE cam.state=2',
function(err, rows) {
callback(err, rows,connection);
//console.log('called end()');
}); // callback function for connection.query
}); // end pool.GetConnection
}
getCampaignData(function(err, rows, connection) {
if (err) throw err;
connection.end();
console.log("I expect my app to terminate");
});
I was having the very same problem, but looking at the source code
https://github.com/felixge/node-mysql/blob/master/lib/Pool.js
I found that the pool, at least in its current implementation, has an end() method that is turns call end() on all connections.
It also accept a callback function to be called after all connections are actually ended (or whenever an error occur).
pool.end(function (err) {
if (err) console.error("An error occurred: " + err);
else console.log("My app terminated");
});
I would use
getCampaignData(function(err, rows, connection)
{
if (err) throw err;
connection.release();
console.log("I expect my app to terminate");
});