Tcp socket server freezes after some time - mysql

I have data coming from different GPS tracker devices. Its a unidirectional data which means I am receiving the data and pushing it into the MySQL DB and firebase. I have a total of 300 Devices connected which are sending data to my server every 10 seconds.
My Server Specs are
AWS t2.xlarge
CPU: 4
Ram: 16GB
what happens is that after 3 days, It stops sending the data into the database. It doesn't stop the server. It just freezes. If I do this
sudo netstat -tulnap | grep :8050
It does show the process and all that but I do not see any data pushing into the DB. It just freezes. I had to reboot the server or I had to stop it using forever and restart it again
forever stop --minUptime 36000000000 server.js
And when I go to my PHPMyAdmin and check the monitor screen what I can see that I have very little free memory left and cache memory is into GBs. It seems like all the memory went into cached memory which left my server freezes. I have no idea where I am doing wrong which is causing it to freeze. For example, at the moment as I am posting a question this is my server current status
As you can see above that in 19 hours cache has been increased and its keep growing. Below is the code
if (cluster.isMaster) {
// Fork workers.
for (var i = 0; i < numCPUs; i++) {
cluster.fork();
}
cluster.on('death', function(worker) {
// console.log('worker ' + worker.pid + ' died');
cluster.fork();
});
} else {
net.createServer(function(socket) {
// console.log('received connection...');
socket.on("error", function(err) {
// console.log("socket error: ")
// console.log(err.stack);
socket.destroy();
});
socket.on('data', function(data) {
});
});
}
Recently I have made one change in the code but still it didn't work out which was to close the socket after receiving the data every 5 seconds
socket.on('data', function(data) {
//parse data and push data into db and firebase
socket.end();
});
That's how I am doing MySQL queries
database.js
var mysql = require('mysql');
var pool = mysql.createPool({
connectionLimit : 8,
waitForConnections : true,
queueLimit : 300,
host : 'localhost',
user : 'username',
password : '123456',
database : 'dummy'
});
module.exports = pool;
Server.js file (only database code I have pasted as full code has 1400 lines of code)
const db = require('./database');
function getCarDetails(car_id,callback) {
db.getConnection((err, connection) => {
if(err) throw err;
console.log('connected as id ' + connection.threadId);
let selectQuery = 'SELECT * FROM ?? join user_info ON car.user_id = user_info.user_id WHERE ?? = ?';
let query = mysql.format(selectQuery, ["car", "id", car_id]);
// query = SELECT * FROM `todo` where `user` = 'shahid'
connection.query(query, (err, data) => {
connection.release();
if(err) {
console.error(err);
//throw err;
return;
}
// rows fetch
if(data.length > 0){
return callback(data[0]);
}else{
return callback(false);
}
});
});
}
function updateIgnitionNotification(car_id,acc_on,acc_off,acc,speed,updated,callback) {
db.getConnection((err, connection) => {
let updateQuery = 'UPDATE car SET notification_acc_on = ?,notification_acc_off = ?,acc = ?,speed = ?,updated = ? Where id = ?';
let query = mysql.format(updateQuery, [acc_on, acc_off,acc,speed,updated, car_id]);
// query = UPDATE `todo` SET `notes`='Hello' WHERE `name`='shahid'
connection.query(query, (err, data) => {
connection.release();
if(err) {
console.error(err);
//throw err;
return;
}else{
return callback(data);
}
});
});
}
function updateLastUpdatedData(car_id,current_datetime,status,acc,monitoring,max_speed,callback){
db.getConnection((err, connection) => {
let updateQuery = 'UPDATE car SET updated = ?,status = ?,acc = ?,monitoring = ?, max_speed = ? Where id = ?';
let query = mysql.format(updateQuery, [current_datetime,status,acc.toUpperCase(),monitoring, max_speed, car_id]);
// query = UPDATE `todo` SET `notes`='Hello' WHERE `name`='shahid'
connection.query(query, (err, data) => {
connection.release();
if(err) {
console.error(err);
//throw err;
return;
}else{
return callback(data);
}
});
});
}
function updateCommand(car_id,command,callback){
db.getConnection((err, connection) => {
let updateQuery = 'UPDATE car SET command = ? Where id = ?';
let query = mysql.format(updateQuery, [command, car_id]);
// query = UPDATE `todo` SET `notes`='Hello' WHERE `name`='shahid'
connection.query(query, (err, data) => {
connection.release();
if(err) {
console.error(err);
//throw err;
return;
}else{
return callback(data);
}
});
});
}
function updateCarLockNotification(car_id,lock_notification,callback){
db.getConnection((err, connection) => {
let updateQuery = 'UPDATE car SET lock_notification = ? Where id = ?';
let query = mysql.format(updateQuery, [lock_notification, car_id]);
// query = UPDATE `todo` SET `notes`='Hello' WHERE `name`='shahid'
connection.query(query, (err, data) => {
connection.release();
if(err) {
console.error(err);
//throw err;
return;
}else{
return callback(data);
}
});
});
}
function updateOverSpeedNotification(car_id,notification_over_speed,callback){
db.getConnection((err, connection) => {
let updateQuery = 'UPDATE car SET notification_over_speed = ? Where id = ?';
let query = mysql.format(updateQuery, [notification_over_speed, car_id]);
// query = UPDATE `todo` SET `notes`='Hello' WHERE `name`='shahid'
connection.query(query, (err, data) => {
connection.release();
if(err) {
console.error(err);
//throw err;
return;
}else{
return callback(data);
}
});
});
}
function updateGeoFenceOutsideAttempt(car_id,geofence_attempt,callback){
db.getConnection((err, connection) => {
let updateQuery = 'UPDATE car SET geofence_outside_attempt = ? Where id = ?';
let query = mysql.format(updateQuery, [geofence_attempt, car_id]);
// query = UPDATE `todo` SET `notes`='Hello' WHERE `name`='shahid'
connection.query(query, (err, data) => {
connection.release();
if(err) {
console.error(err);
//throw err;
return;
}else{
return callback(data);
}
});
});
}
function updateGeoFenceInsideAttempt(car_id,geofence_attempt,callback){
db.getConnection((err, connection) => {
let updateQuery = 'UPDATE car SET geofence_inside_attempt = ? Where id = ?';
let query = mysql.format(updateQuery, [geofence_attempt, car_id]);
// query = UPDATE `todo` SET `notes`='Hello' WHERE `name`='shahid'
connection.query(query, (err, data) => {
connection.release();
if(err) {
console.error(err);
//throw err;
return;
}else{
return callback(data);
}
});
});
}
function updateBatteryNotification(car_id,battery,callback){
db.getConnection((err, connection) => {
let updateQuery = 'UPDATE car SET notification_battery = ? Where id = ?';
let query = mysql.format(updateQuery, [battery, car_id]);
// query = UPDATE `todo` SET `notes`='Hello' WHERE `name`='shahid'
connection.query(query, (err, data) => {
connection.release();
if(err) {
console.error(err);
//throw err;
return;
}else{
return callback(data);
}
});
});
}
function saveNotificationLog(log,callback){
db.getConnection((err, connection) => {
// let insertQuery = 'INSERT INTO ?? (??,??) VALUES (?,?)';
//let query = mysql.format(insertQuery,["log","user","notes",data.user,data.value]);
// query = UPDATE `todo` SET `notes`='Hello' WHERE `name`='shahid'
connection.query('INSERT INTO log SET ?', log, (err, res) => {
connection.release();
if(err) {
return callback(res);
}else{
return callback(res);
}
});
});
}
function saveHistory(history,callback){
db.getConnection((err, connection) => {
// let insertQuery = 'INSERT INTO ?? (??,??) VALUES (?,?)';
//let query = mysql.format(insertQuery,["log","user","notes",data.user,data.value]);
// query = UPDATE `todo` SET `notes`='Hello' WHERE `name`='shahid'
connection.query('INSERT INTO car_history SET ?', history, (err, res) => {
connection.release();
if(err) {
return callback(res);
}else{
return callback(res);
}
});
});
}
function updateGeoFenceNotification(car_id,notification_inside,notification_outside,callback){
db.getConnection((err, connection) => {
let updateQuery = 'UPDATE car SET notification_inside = ?,notification_outside = ? Where id = ?';
let query = mysql.format(updateQuery, [notification_inside, notification_outside, car_id]);
// query = UPDATE `todo` SET `notes`='Hello' WHERE `name`='shahid'
connection.query(query, (err, data) => {
connection.release();
if(err) {
console.error(err);
//throw err;
return;
}else{
return callback(data);
}
});
});
}
Please help me to resolve this issue

I'll attempt an answer, or at least some observations.
It's probably worth your effort to get your nodejs app working robustly without clustering. Keeping things simple helps with troubleshooting. Once it is solid you can add clustering.
Your server is overprovisioned. The workload you have is 1,800 connections per minute (every six seconds from 300 devices) or 30/sec. A 2-core server with 4-8GiB of RAM should be just fine for this workload if your program is written well. A 1-core server may be enough. And, a program that's expected to stay up for thousands of hours necessarily must be written well. (Disregard this advice if your database server runs on the same virtual machine as your nodejs app.)
You didn't describe how you connect to your database server from your nodejs code. You should use connection pooling to take away the need to close and reopen connections a lot.
Do something like this in your initialization code:
const mysql = require('mysql')
const mySqlPool = mysql.createPool({
connectionLimit : 8, /* make this as small as possible */
waitForConnections : true,
queueLimit : 300, /* enough for 6sec worth of workload */
host : 'dbhost',
user : 'bob',
password : 'secret',
database : 'my_db',
})
/* make the pool available to other code.
* There may be a better way to do this. */
global.mySqlPool = mySqlPool
/* make a little monitor function to
* let you know of database problems.
* this pings the database every 10sec */
let monitorTimeout = setTimeout ( function() {
global.mySqlPool.ping ( function (err) {
if (err) {
clearTimeout(monitorTimeout)
console.error('database connection error', err)
throw err
}
})
}, 10000)
Then when you need to access your database use global.mySqlPool. in place of db. in the code you have. Each query will use a connection from the pool. If all the connections are in use, the query will wait in a queue until a connection is free.
This pooling / queueing strategy puts a much more predictable load on both your nodejs program and your MySQL database.
The same sort of thing should be possible for firebase.

Once you have made database connection pooling work properly, keeping the pool sizes small, your next step for performance improvement is a little harder. But it will make a vast difference.
What you do is this: put your UPDATE operations into a simple queue inside your app. That is, modify your updateWhatever( car_id, whatever ) functions to push() their SQL statements and parameters onto a shared array.
Then, write a function that uses shift() to fetch those items from the array and run them one after the other on the dbms. When the array has multiple items in it, wrap those items in a single database transaction.
This helps performance a lot: most of the MySQL server's work to handle INSERT and UPDATE operations happens when those operations are COMMITed. (If you don't start transactions explicitly, MySQL uses autocommit for every operation.) So if you bundle them together in transaction bundles of a few tens of operations, you put a lot less load on MySQL.
It also reduces your application's potential need to handle many UPDATE operations concurrently on different connections. That in turn reduces contention for access to the tables.
(It's true that the shift() operation on an array has an in-memory performance hit if the array is large. If that turns out to be a problem you can replace the easy-to-program array / push() / shift() queue discipline with a purpose-built queue package.)

Related

How to handle a full pool of mysql connections in nodejs

In my Node script I use MySQL and to be able to handle multiple connections I use a connection pool.
Today I forgot to release a connection in the mysql pool. It took me a long time to figure out what the problem was because there was no error shown anywhere.
My code:
const mysql = require('mysql');
const pool = mysql.createPool({
host : 'x',
user : 'x',
password : '#x',
database : 'x',
connectionLimit: 2
});
function executeQuery(){
pool.getConnection((err, connection) => {
if(err) console.log(err);
let query = mysql.format("SELECT * FROM users WHERE id = ?", 1);
connection.query(query, (err, rows) => {
if(err) console.log(err);
console.log(rows);
});
});
}
executeQuery(); // outputs the user as expected
executeQuery(); // outputs the user as expected
executeQuery(); // there is no output in the console, it just looks like nothing happened
My question: How to find out if there are still connections available and if there are no connection available anymore show an error or handle it in a different way?
You forgot to release your connection:
function executeQuery(){
pool.getConnection((err, connection) => {
if(err) console.log(err);
connection.query("SELECT * FROM users WHERE id = ?", [ 1 ], (err, rows) => {
connection.release(); // Give it back or else it gets lost
if(err) console.log(err);
console.log(rows);
});
});
}
There's also no reason to grab the connection like that, you can just use the pool:
function executeQuery() {
pool.query("SELECT * FROM users WHERE id = ?", [ 1 ], (err, connection) => {
if(err) console.log(err);
console.log(rows);
});
}

How to return the response of Node.js mysql query connection

I am new at Node.js and I want to find something from database by using select query.
Here is my code.
var address = socket.request.client._peername.address;
var ip_addrss = address.split("::ffff:");
let mine = ip_addrss[1];
var location = iplocation_find(mine);
connection.connect( function () {
// insert user data with IP, location --- has got a status.
let stranger = "";
var values = [];
if (mine == null){
mine = "local server";
}
values.push(mine);
values.push('location');
var sql = "INSERT INTO user_list (IP_address, location) VALUES (?)";
connection.query(sql, [values], function (err, res){
if (err) throw err;
});
// control chatting connection between users
connection.query("SELECT IP_address FROM user_list WHERE status = ? AND location = ?", [0, "location"], function (err, res){
if (err) throw err;
stranger = res[0].IP_address;
console.log(stranger);
});
var room_users = [];
room_users.push(mine);
room_users.push(stranger);
console.log(room_users);
connection.query("INSERT INTO chatting_status (IP_client_1, IP_client_2) VALUES (?)", [room_users], function (err, res){
if (err) throw err;
console.log('inserted');
});
});
Now the problem is "stranger". It is not working anymore. Just always null.
Please tell me how I can return value in mysql query statement.
on my console, shows this.
[ 'local server', '' ]
127.0.0.1
inserted
[ '192.168.1.100', '' ]
127.0.0.1
inserted
Above, 'local server' and '192.168.1.100' are values of mine. And also '127.0.0.1' is the value of stranger only in query. But out of query it is just null.
You are using asynchronous operations with your .connect() and .query() calls. To sequence code with asynchronous callbacks like this, you have to continue the flow of control inside the callback and then communicate back errors or result via a callback.
You could do that like this:
let address = socket.request.client._peername.address;
let ip_addrss = address.split("::ffff:");
let mine = ip_addrss[1];
let location = iplocation_find(mine);
function run(callback) {
connection.connect( function () {
// insert user data with IP, location --- has got a status.
let values = [];
if (mine == null){
mine = "local server";
}
values.push(mine);
values.push('location');
var sql = "INSERT INTO user_list (IP_address, location) VALUES (?)";
connection.query(sql, [values], function (err, res){
if (err) return callback(err);
// control chatting connection between users
connection.query("SELECT IP_address FROM user_list WHERE status = ? AND location = ?", [0, "location"], function (err, res){
if (err) return callback(err);
let stranger = res[0].IP_address;
console.log(stranger);
let room_users = [];
room_users.push(mine);
room_users.push(stranger);
console.log(room_users);
connection.query("INSERT INTO chatting_status (IP_client_1, IP_client_2) VALUES (?)", [room_users], function (err, res){
if (err) return callback(err);
console.log('inserted');
callback(null, {stranger: stranger, room_users: room_users});
});
});
});
});
}
run((err, result) => {
if (err) {
console.error(err);
} else {
console.log(result);
}
});
Personally, this continually nesting callback code is a drawback of writing sequenced asynchronous code with plain callbacks. I would prefer to use the promise interface to your database and write promise-based code using async/await which will allow you to write more linear looking code.

Nested Queries not being called in MariaDB using nodeJS

This is my first project using MySQL and NodeJS, I am used to Mongo, so I might be doing something stupid here. Locally everything works fine (using MySQL) but when I deploy the following code to my hosting (that uses MariaDB) only the parent query inserts into the table (leads). The other table stays empty. Another issue is I don't have access to the NodeJS logs when it is deployed, but as far as I can tell the nested queries never get called.
var mysql = require('mysql');
global.db = mysql.createPool({
host : 'localhost',
user : 'client',
password : '******',
database : 'db'
});
router.post('/', function(req, res){
const d = req.body
let subscribe = (d.subscribe ? 1 : 0)
global.db.getConnection((err, conn) => {
if (err) {
res.end(JSON.stringify(err));
} else {
let lead = [null, d.voornaam, d.achternaam, d.email, d.postcode, d.opmerkingen, d.soort, subscribe]
let sql = 'INSERT INTO leads VALUES ?';
conn.query(sql, [[lead]], (err, results) => {
if (err) {
res.end(JSON.stringify(err));
conn.release();
} else {
const lead_id = results.insertId
d.types.forEach(w => {
let wens = [null, lead_id, w.woningType, w.slaapkamers, w.prijs, w.oplevering]
let sql = 'INSERT INTO wensen VALUES ?';
conn.query(sql, [[wens]], (err, results) => {
if(err) {
res.end(JSON.stringify(err));
conn.release();
}
})
})
res.end('True');
conn.release();
}
})
}
})
});
Check syntax. Note parens:
'INSERT INTO leads VALUES (?)'
Did this fail to tell you that?
if (err) { res.end(JSON.stringify(err)); ... }

Nested Query in Nodejs Mysql

I want to count a line in table that has FOO table.
The following code has a bug which show only the last db_name.
RESULT IS LOOK LIKE THIS:
db_0099,0
db_0099,5
db_0099,10
db_0099,3
Could you please suggest me how to fix the nodejs code?
var mysql = require('mysql');
var sql1 = "SELECT table_schema as db_name from information_schema.tables WHERE table_name = 'FOO' ";
var sql2 = "SELECT COUNT(*) as solution FROM {0}.FOO";
var connection = mysql.createConnection({
host : '$$$$$$$',
user : '$$$$$$$',
password : '$$$$$$$',
});
connection.connect(function(err){
console.log('connected as id ' + connection.threadId);
});
connection.query(sql1, function(err, result) {
if (err) throw err;
for (var i = 0, len = result.length; i < len; i++) {
var db_name = result[i].db_name;
console.log(db_name);
connection.query(sql2.replace("{0}",db_name), function(err, result) {
if (err) throw err;
console.log(db_name+','+result[0].solution); //Here db_name is showed only the last one.
});
};
connection.end();
});
i advice a two step solution to this problem:
use connection pooling
var pool = mysql.createPool({
host : 'xxxxx',
user : 'xxxxx',
password : 'xxxxx',
connectionLimit : 100
});
pool can do auto connection, so don't connect to your db, just
pool.query(sql,function(err,res){})
this way you use one connection for each query, which will be closed automatically after using it.
use async await for asyncronous sequential queries.
for that create a getResult function which returns a promise
function getResult(sql){
return new Promise(function(resolve,reject){
pool.query(sql, function(err, result){
if(err){
reject(err)
}else{
resolve(result)
}
})
})
}
then you can await each query in the loop
pool.query(sql1, async function(err, result) {
if (err) throw err;
for (var i = 0; i < result.length; i++) {
var db_name = result[i].db_name;
console.log(db_name);
var sql = sql2.replace("{0}",db_name)
var res = await getResult(sql)
console.log(db_name+','+res[0].solution); //Here db_name is showed only the last one.
};
pool.end()
});
P.S.: async await is a feature of the upcomming node 8.0.0 release in april. for node 7.x you will have to start your script with a commandline switch
node --harmony-async-await yourscript.js
Have you verify the content of result ?
console.log(result);
If it's okay try this :
solutions = results.map(result => {
let dbName = result.db_name;
let queryResult;
connection.query(sql2.replace("{0}", dbName), function(err, result) {
if (err) {
throw err;
} else {
queryResult = `${db_name}, ${result[0].solution}`
console.log(queryResult);
}
});
return queryResult;
})
console.log(solutions);
However, try to use a ORM or a sql parser for your query !
Try this one :)
https://hiddentao.com/squel/

Add every item of an Array to mysql db

I'm pretty new to Java Script and tried to write a small programm to add users to a database. My problem is, that my programs doesn't add every user ones. It adds the last user as often as there are users in the list. users has more than 2 Objects and all the Objects have all field filled.
for(i = 0; i<users.length; i++)
{
var user = users[i];
console.log(user.lastonline)
pool.getConnection(function(err, connection) {
if (err) throw err;
var quer = connection.query('INSERT INTO users SET `steamid` = '+ connection.escape(user.steamid)+', `name`='+connection.escape(user.name)+', `lastonline`='+connection.escape(user.lastonline)+' ON DUPLICATE KEY UPDATE `name`='+connection.escape(user.name)+', `lastonline`='+connection.escape(user.lastonline)+'', function(err, result) {
connection.release();
});
console.log(quer.sql);
});
}
I tried to rewrite this in a lot of different way, but most time I get something like this:
TypeError: Cannot read property 'steamid' of undefined
for(i = 0; i<users.length; i++)
{
pool.getConnection(function(err, connection) {
console.log(users[i]["steamid"]);
if (err) throw err;
var quer = connection.query('INSERT INTO users SET `steamid` = '+ connection.escape(users[i]["steamid"])+', `name`='+connection.escape(users[i].name)+', `lastonline`='+connection.escape(users[i].lastonline)+' ON DUPLICATE KEY UPDATE `name`='+connection.escape(users[i].name)+', `lastonline`='+connection.escape(users[i].lastonline)+'', function(err, result) {
connection.release();
});
console.log(quer.sql);
});
}
EDIT:
Rest of the programm
var mysql = require('mysql');
var Promise = require("bluebird");
var pool = mysql.createPool({
connectionLimit : 10,
host : 'localhost',
user : 'zar',
password : 'qxLLPa06iEs2Bzsu',
database : 'zar',
socketPath: '/var/run/mysqld/mysqld.sock'
});
pool.on('connection', function (connection) {
console.log("connection made")
});
//my testing users
var users = [];
times = Date.now();
user1 = {steamid:012345678912345658,name:"user1",lastonline:times};
user2 = {steamid:012345678912345628,name:"user2",lastonline:times};
user3 = {steamid:012345678912345618,name:"user3",lastonline:times};
users.push(user1);
users.push(user2);
users.push(user3);
Edit: Fixed to use only one connection.
Previous version was getting a new connection for every user.
You should use Promises:
pool.getConnection((err, connection) => {
if (err) {
console.log(err);
return;
}
var tasks = users.map((user) => {
return new Promise((resolve, reject) => {
if (err) {
return reject(err);
}
var quer = connection.query('INSERT INTO users SET `steamid` = ' + connection.escape(user.steamid) + ', `name`=' + connection.escape(user.name) + ', `lastonline`=' + connection.escape(user.lastonline) + ' ON DUPLICATE KEY UPDATE `name`=' + connection.escape(users.name) + ', `lastonline`=' + connection.escape(users.lastonline) + '', function (err, result) {
if (err) {
return reject(err);
}
resolve(result);
});
});
});
Promise.all(tasks)
.then((results) => {
// Array of results passed in resolve
connection.release();
})
.catch((err) => {
// All errors you reject are catched here
});
});
This should workd but still, you are executing all queries in parallel, which can be pretty aggressive for the DB.
I suggest you to look into bluebird Promise.each for better results.