How to stop infinite loop caused by callback in asynchronous function - javascript

I have two async functions defined in my program; one of them pings a given IP address(using the net-ping module) and 'returns' (through a callback) whether the ping was successful, and the other creates an SSH connection to a given IP address (using the ssh2 module) and also 'returns' whether the connection was successful or not.
My problem arises when I try to read in IP data from a data.txt file using the readline functionality. I use readline module to read in each line from the file, use the callbacks to call ping and ssh in a blocking fashion, and then place these return values into an array to use later. I've been able to verify that the functions are executed in the order I expect, and that the return values are truly returned.. However, when the program reaches the end of the file - instead of terminating, it just reads the file again, and again, and again.
The SSH function is defined as:
function SSH(ipAdress, callback) {
connection.on('ready', function(err) {
if(err) {
throw err;
returnValue = "ErrorWithReadyConnection";
callback(null, returnValue);
}
//runs the uptime command once we have SSH'd into the machine
connection.exec('uptime', function(err, stream) {
if(err) {
throw err;
returnValue = "ErrorRunningCommandFromSSH";
callback(null, returnValue);
}
else {
stream.on('close', function(code, signal) {
//code holds the response from running 'uptime'
console.log("Stream on close. Code : " + code +
". Signal: " + signal);
connection.end();
returnValue = "success";
callback(null, returnValue);
}).on('data', function(data) {
console.log("STDOUT: " + data);
}).stderr.on('data', function(data) {
console.log("STDERR: " + data);
});
}
});
//Parameters for the SSH connection
}).connect({
host: ip,
port: 22,
username: userName,
privateKey: privateKey,
passphrase: passPhrase
}); //connect
//handle any connection errors done whilst creating a connection
connection.on('error', function(err) {
if(err) {
returnString = "ErrorWaitingForHandshake";
callback(null, returnString);
}
}); //end connect
}
The ping function is defined as:
function pingFunc(ip, callback) {
var returnString;
//Create a ping session, passing the IP of the machine to ping
sessions.pingHost(ip, function(error, target) {
//if error, then examine what type of error has occured
if(error) {
if(error instanceof ping.RequestTimedOutError) {
returnString = "RequestTimedOut";
}
//else, different error - output the error string.
else {
returnString = "Error";
}
} //End error handling
//else, ping was successful
else {
returnString = "Alive";
}
callback(null, returnString);
});
//return returnString;
}
And the code where I call the functions is:
var allMachines = new Array();
var lineReader = require('readline').createInterface({
input: require('fs').createReadStream('data.txt');
});
lineReader.on('line', function(line) {
pingFunc(line, function(err, pingResult) {
SSH(line, function(err, sshResult) {
var machineTemp = [{'ping': pingResult, 'ssh': sshResult }];
allMachines = allMachines.concat(machineTemp);
})
})
}
I plan on later using the allMachines array to create a JSON file, however this infinite loop is halting all potential progress. I've tried to move the connection.on('error', ...) in the SSH function which I think is causing the infinite loop, however this has proved to be fruitless.
Any help would be greatly appreciated - thanks!
P.S If anyone knows how I would be able to register when readlines has finished, and the allMachines array has been filled with the necessary data I would also be very grateful! I've tried to use readline.on('close', ...), however this gets called before SSH and ping finish executing, so is no use to me! Thanks again

Related

In Node, how to execute sql from global database connection

I am unable to execute the sql, when using the global database connection in node.js.
I have followed the steps as in Azure documentation: https://learn.microsoft.com/en-us/azure/mysql/connect-nodejs and able to display the output on the console. But, I want to put all my Azure SQL database connection in a separate file, but the select query is not printing the output on the console.
DatabaseManager.js
var Connection = require('tedious').Connection;
var Request = require('tedious').Request;
var sqlConnection = function sqlConnection() {
// Create connection to database
var config =
{
userName: 'uname',
password: 'password',
server: 'dbserver.database.windows.net',
options:
{
database: 'mydatabase',
encrypt: true
}
}
var connection = new Connection(config);
// Attempt to connect and execute queries if connection goes through
connection.on('connect', function(err) {
if (err)
{
console.log(err)
}
else
{
console.log('CONNECTED TO DATABASE');
}
}
);
}
module.exports = sqlConnection;
app.js
var restify = require('restify');
var builder = require('botbuilder');
var botbuilder_azure = require("botbuilder-azure");
var azure = require('azure-storage');
var dbconnection = require('./DatabaseManager');
bot.dialog('profileDialog',
(session) => {
session.send('You reached the profile intent. You said \'%s\'.', session.message.text);
console.log('Reading rows from the Table...');
dbconnection("select FNAME from StudentProfile where ID=1"),
function (err, result, fields) {
if (err) throw err;
console.log(result);
}
session.endDialog();
}
Console Output:
Reading rows from the Table...
CONNECTED TO DATABASE
I was expecting the output of FNAME, but nothing is printing on the console. Is there anything, I am missing?
Thank you.
There's a couple of problems here. First off, you should only ever import a module once per file. This is just a performance consideration and won't actually break your code.
Next, pay attention to what you're exporting from your DatabaseManager module. Right now, you're exporting a function that creates the connection and then doesn't do anything with it. We can fix this by using a pattern called a "callback" which lets us provide a function that will then be called with the connection as an argument.
I added a ton of comments to the code explaining things. This code won't run as-is - there's a couple places where I have "do this or this". You'll have to choose one.
var Tedious = require('tedious'); // Only require a library once per file
var Connection = Tedious.Connection;
var Request = Tedious.Request;
// Or using the object spread operator
var { Connection, Request } = require('tedious');
// You called this `sqlConnection`. I'm going to use a verb since it's a
// function and not a variable containing the connection. I'm also going
// to change the declaration syntax to be clearer.
function connect(cb) { // cb is short for callback. It should be a function.
var config = {
userName: 'uname',
password: 'password',
server: 'dbserver.database.windows.net',
options: {
database: 'mydatabase',
encrypt: true
}
}; // Put a semi-colon on your variable assignments
var connection = new Connection(config);
// Attempt to connect and execute queries if connection goes through
connection.on('connect', function(err) {
if (err) {
console.log(err);
return; // Stop executing the function if it failed
}
// We don't need an "else" because of the return statement above
console.log('CONNECTED TO DATABASE');
// We have a connection, now let's do something with it. Call the
// callback and pass it the connection.
cb(connection);
});
}
module.exports = connect; // This exports a function that creates the connection
Then back in your main file, you can use it like so.
var restify = require('restify');
var builder = require('botbuilder');
var botbuilder_azure = require('botbuilder-azure');
var azure = require('azure-storage');
var connect = require('./DatabaseManager'); // renamed to be a verb since it's a function.
bot.dialog('profileDialog', (session) => { // Hey, this is a callback too!
session.send('You reached the profile intent. You said \'%s\'.', session.message.text);
console.log('Creating a connection');
connect((connection) => {
// or with the traditional function notation
connect(function(connection) {
console.log('Reading rows from the Table...');
// Execute your queries here using your connection. This code is
// taken from
// https://github.com/tediousjs/tedious/blob/master/examples/minimal.js
request = new Request("select FNAME from StudentProfile where ID=1", function(err, rowCount) { // Look another callback!
if (err) {
console.log(err);
} else {
console.log(rowCount + ' rows');
}
connection.close();
});
request.on('row', function(columns) { // Iterate through the rows using a callback
columns.forEach(function(column) {
if (column.value === null) {
console.log('NULL');
} else {
console.log(column.value);
}
});
});
connection.execSql(request);
});

How do i display the javascript error object?

I am using Node.js and Express web application framework and
mysql package from here
https://www.npmjs.com/package/mysql
here's my Parent.js file .. it's my model file
var db = require('../dbconnection');
var Parent = {
findIfParentMobileNumberExists: function (parentmobilenumber, callback) {
db.query('SELECT parentmobilenumber from parents where parentmobilenumber=?', parentmobilenumber, function (err, rows) {
if (err) {
callback(err, null);
}
if (rows.length < 1) {
console.log("rows length less than 1");
callback(err, null);
}
else if (rows.length !== 0) {
console.log("rows length greater than 1");
callback(null, rows[0].parentmobilenumber);
}
else {
}
});
}
module.exports = Parent;
and here's my registerParent.js file .. this is my route
var express = require('express');
var router = express.Router();
var Parent = require('../models/Parent');
router.post('/', function (req, res, next) {
var countrycode = req.body.countrycode;
var parentmobilenumber = (countrycode) + (req.body.inputMobileNumber);
//remove hyphens from mobile number
var parentmobilenumberwithouthyphens =
parentmobilenumber.replace(/-/g, "");
//remove spaces from mobile number
var parentmobilenumberwithouthyphensandspaces =
parentmobilenumberwithouthyphens.replace(/ /g, '');
Parent.findIfParentMobileNumberExists(parentmobilenumberwithouthyphensandspaces, function (err, parentmobilenumberfromdb) {
if (err) {
res.json({registerErrorMessage: err.message});
}
else {
if (parentmobilenumberwithouthyphensandspaces === parentmobilenumberfromdb) {
console.log(parentmobilenumberfromdb);
}
else {
res.json({registerErrorMessage: 'The Mobile Number does not exist in our database'});
}
}
});
});
if i enter a correct mobile number i get the mobile number displayed in a console.log message .. however when i enter an incorrect mobile number which is not in the database the resulting rows.length is < 1
i get the message
rows length less than 1
but i don't understand when this block runs
if (rows.length < 1) {
console.log("rows length less than 1");
callback(err, null);
}
the err object is never displayed .. i.e this block is never run in the router.post function
if (err){
res.json({registerErrorMessage: err.message});
}
so i want to display the err object correctly first ..
and then i want to display an error message that is readable by end-users
how can i do this ?
i tried reading the documentation here
https://github.com/mysqljs/mysql#error-handling
but i really don't understand how to properly execute callbacks.
You don't start http server with express.
Try to insert code below to start listening:
const app = express();
app.listen( insert port here )
At first, you might want to terminate your logic if an error occurs:
if(err){
return callback(err);
}
Now the error is passed to the client side, where you can catch it and display a message. E.g. (using jquery):
$.ajax({
url:"your/backend",
dataType: "json",
success(res){
if(res.code){
return alert("an error ("+res.code+") occured. Sorry :/");
}
//continue with valid response
}
});
However, you may change your API to be more general, e.g. always return an error property, thats either false or the error object:
on error:
res.json({error:err});
on success:
res.json({error:false,data:"whatever"});
So that you could do this on client side:
$.ajax({
url:"your/backend",
dataType: "json",
success(res){
if(res.error){
return alert("an error ("+res.error.code+") occured. Sorry :/");
}
//continue with valid response
alert(res.data);
}
});

Node.js process cannot recover after MySQL turned off, then turn on

I am using Node.js with MySQL and restify.
I have the following code which is run as part of a REST API. It works fine.
server.get('/test', function (req, res, next) {
var query_string =
"SELECT DATE(date_transacted) AS transaction_date, " +
" MonthReports.tb AS MonthReports__tb " +
" FROM monthly_reports MonthReports " +
" WHERE ( date_transacted >= \'2015-01-00\' AND date_transacted <= \'2015-09-00\' ) ";
connection.query(
query_string
, function (err, rows, fields) {
if (err) throw err;
res.send(rows);
});
});
If I deliberately turn off the MySQL database and makes a REST API call which will run the query, I will get the error
Cannot enqueue Query after fatal error.
At this point, I turn on the MySQL database. The node.js process is unable to recover and the same error keeps appearing when I make a REST API call. The REST API server is dead.
What can be done to make the Node.js REST API server code recoverable?
I am assuming you are connecting globally inside your script.
One simple way would be to create a connection per request:
server.get('/test', function (req, res, next) {
var query_string =
"SELECT DATE(date_transacted) AS transaction_date, " +
" MonthReports.tb AS MonthReports__tb " +
" FROM monthly_reports MonthReports " +
" WHERE ( date_transacted >= \'2015-01-00\' AND date_transacted <= \'2015-09-00\' ) ";
var connection = getConnection(function connected(err) {
if (err) {
// error connecting to mysql! alert user
} else {
connection.query(
query_string
, function (err, rows, fields) {
if (err) throw err;
res.send(rows);
});
}
});
});
The above code is psuedo code as i'm not familiar with the node mysql library. This will allow each request to see if mysql is able to be connected to, at the expense of having a connection per web request.
Another strategy could be to check err when you issue a query, and if there is an error try to reestablish the global connection
server.get('/test', function (req, res, next) {
var query_string =
"SELECT DATE(date_transacted) AS transaction_date, " +
" MonthReports.tb AS MonthReports__tb " +
" FROM monthly_reports MonthReports " +
" WHERE ( date_transacted >= \'2015-01-00\' AND date_transacted <= \'2015-09-00\' ) ";
connection.query(
query_string
, function (err, rows, fields) {
if (err) {
// Try to reconnect here instead of throwing error and stopping node process, and reissue query
}
res.send(rows);
});
});
This website gives a complete answer. Credit goes to the writer of this article, not me.
https://www.exratione.com/2013/01/nodejs-connections-will-end-close-and-otherwise-blow-up/
/**
* #fileOverview A simple example module that exposes a getClient function.
*
* The client is replaced if it is disconnected.
*/
var mysql = require("mysql");
var client = mysql.createConnection({
host: "127.0.0.1",
database: "mydb",
user: "username",
password: "password"
});
/**
* Setup a client to automatically replace itself if it is disconnected.
*
* #param {Connection} client
* A MySQL connection instance.
*/
function replaceClientOnDisconnect(client) {
client.on("error", function (err) {
if (!err.fatal) {
return;
}
if (err.code !== "PROTOCOL_CONNECTION_LOST") {
throw err;
}
// client.config is actually a ConnectionConfig instance, not the original
// configuration. For most situations this is fine, but if you are doing
// something more advanced with your connection configuration, then
// you should check carefully as to whether this is actually going to do
// what you think it should do.
client = mysql.createConnection(client.config);
replaceClientOnDisconnect(client);
client.connect(function (error) {
if (error) {
// Well, we tried. The database has probably fallen over.
// That's fairly fatal for most applications, so we might as
// call it a day and go home.
//
// For a real application something more sophisticated is
// probably required here.
process.exit(1);
}
});
});
}
// And run this on every connection as soon as it is created.
replaceClientOnDisconnect(client);
/**
* Every operation requiring a client should call this function, and not
* hold on to the resulting client reference.
*
* #return {Connection}
*/
exports.getClient = function () {
return client;
};
This answer was extracted from another link nodejs mysql Error: Connection lost The server closed the connection
The extracted code;
var db_config = {
host: 'localhost',
user: 'root',
password: '',
database: 'example'
};
var connection;
function handleDisconnect() {
connection = mysql.createConnection(db_config); // Recreate the connection, since
// the old one cannot be reused.
connection.connect(function(err) { // The server is either down
if(err) { // or restarting (takes a while sometimes).
console.log('error when connecting to db:', err);
setTimeout(handleDisconnect, 2000); // We introduce a delay before attempting to reconnect,
} // to avoid a hot loop, and to allow our node script to
}); // process asynchronous requests in the meantime.
// If you're also serving http, display a 503 error.
connection.on('error', function(err) {
console.log('db error', err);
if(err.code === 'PROTOCOL_CONNECTION_LOST') { // Connection to the MySQL server is usually
handleDisconnect(); // lost due to either server restart, or a
} else { // connnection idle timeout (the wait_timeout
throw err; // server variable configures this)
}
});
}
handleDisconnect();

node.js mongodb - collection.find().toArray(callback) - callback doesn't get called

I am just starting out with mongodb, but I am running into a problem when trying to use .find() on a collection.
I've created a DataAccessObject which opens a specific databate and then lets your perform operations on it. Here is the code:
The constructor:
var DataAccessObject = function(db_name, host, port){
this.db = new Db(db_name, new Server(host, port, {auto_reconnect: true}, {}));
this.db.open(function(){});
}
A getCollection function:
DataAccessObject.prototype.getCollection = function(collection_name, callback) {
this.db.collection(collection_name, function(error, collection) {
if(error) callback(error);
else callback(null, collection);
});
};
A save function:
DataAccessObject.prototype.save = function(collection_name, data, callback){
this.getCollection(collection_name, function(error, collection){
if(error) callback(error);
else{
//in case it's just one article and not an array of articles
if(typeof (data.length) === 'undefined'){
data = [data];
}
//insert to collection
collection.insert(data, function(){
callback(null, data);
});
}
});
}
And what seems to be the problematic one - a findAll function:
DataAccessObject.prototype.findAll = function(collection_name, callback) {
this.getCollection(collection_name, function(error, collection) {
if(error) callback(error)
else {
collection.find().toArray(function(error, results){
if(error) callback(error);
else callback(null, results);
});
}
});
};
Whenever I try to dao.findAll(error, callback), the callback never gets called.
I've narrowed the problem down to the following part of the code:
collection.find().toArray(function(error, result){
//... whatever is in here never gets executed
});
I've looked at how other people do it. In fact, I'm following this tutorial very closely. No one else seems to have this problem with colelction.find().toArray(), and it doesn't come up in my searches.
Thanks,
Xaan.
You are not using the open callback so if you are trying to make the findall request right after creating the dao then it won't be ready.
If your code is like this, it will not work.
var dao = new DataAccessObject("my_dbase", "localhost", 27017);
dao.findAll("my_collection",function() {console.log(arguments);});
I tested it and it doesn't find records, and it also gives no error. I think it should give an error.
But if you change it so that you give a callback to the constructor, then it should work.
var DataAccessObject = function(db_name, host, port, callback){
this.db = new Db(db_name, new Server(host, port, {auto_reconnect: true}, {}));
this.db.open(callback);
}
And make your code like this.
var dao = new DataAccessObject("my_dbase", "localhost", 27017, function() {
dao.findAll("my_collection",function() {console.log(arguments);});
});

Node.js Variable scope

I have a http server setup which basically needs to look up stuff in the database.
Here is the code snippet :
var sys = require('sys');
var Client = require('mysql').Client;
var client = new Client();
client.host = '_';
client.user = '_';
client.password = '_';
client.database = '_';
var http = require('http');
http.createServer(function(req, res) {
req.on('end', function() {
client.connect(function(error, results) {
if (error) {
console.log('Connection Error:');
return;
}
ClientConnectionReady(client);
});
ClientConnectionReady = function(client) {
var final = '';
client.query('select * from table', function selectCb(error, result, fields) {
if (error) {
console.log('ERROR');
client.end();
return;
}
final += "{" + JSON.stringify(result);
});
client.query("SELECT COUNT(*) from table", function selectCb(error, result, fields) {
if (error) {
console.log('ERROR');
client.end();
return;
}
final += "," + JSON.stringify(result) + "}";
});
res.writeHead(200, {
'Content-Type': 'text/plain'
});
res.write(final);
res.end();
client.end();
};
});
}).listen(8007, "127.0.0.1");
  
If I print the values of the variable 'final' at the places where I assign them, I see valid values, but at the lines when I do 'res.write(final)', final is still blank.
How do I make this work and why is this failing?? Thanks for the help, I am new to node.js
The Node.js environment is asynchronous. Those statements that modify "final" are inside callbacks that are executed only when the database operations finish. The code immediately after the initiation of the database operations, where you write the result, are executed long before those callbacks run.
You've almost stumbled upon the answer to the problem already: you must not write the result until the operations are finished, which you know will be the case inside the callbacks. If you must wait for both to finish (seems like you do), then you can do something like keep a counter in the outer scope. Each callback can increment the counter, and call the same result-writer function only when the counter indicates that both callbacks are complete. (I have the idea that the Node runtime has a fancier way of doing that sort of thing, but I'm not that familiar with it. In a simple case like this, keeping something like a counter is easy enough to do.)
Also, an unrelated note: that "ClientConnectionReady" variable should probably either be written as a function definition:
function ClientConnectionReady(client) {
// ...
}
or else it should be declared with var. (I'm a little surprised in fact that it's not throwing an error, but again I'm not that familiar with Node.js.)
By the looks of it, you are trying to write final before it is ever assigned a value.
I'm assuming that client.query is asynchronous. Given that, the callback function is most likely being called after the res.writeHead and res.write lines. What you need to do is put other calls and the client.write* lines within the first callback.
This should give you an idea (didn't check if it compiles)
ClientConnectionReady = function(client)
{
var final = '';
//Get the rows
client.query('select * from table',
function selectCb(error, result, fields)
{
if (error)
{
console.log('ERROR');
client.end();
return;
}
final+="{"+JSON.stringify(result);
//Get the count query
client.query("SELECT COUNT(*) from table",
function selectCb(error, result, fields)
{
if (error)
{
console.log('ERROR');
client.end();
return;
}
final+=","+JSON.stringify(result)+"}";
//Return the final results to the client now
res.writeHead(200, {'Content-Type': 'text/plain'});
res.write(final);
res.end();
client.end();
});
});
};
What this does is first gets the rows. In that callback, it then gets the count. Finally, when that works, it sends the data to the client within the count callback.

Categories

Resources