I currently have an issue in Node.js whereby I make a call to a mysql database using the promise-mysql and bluebird packages. I followed the tutorials on the webpage and it seems to work. I keep ending up with a timeout error as once the database has been queried the process doesn't break (if that's the right term) and I either have to terminate the process using ctrl + c or when testing on Alexa when the skill replies with there was a problem. Is there a way to end the promise and db connection once I have retrieved the value of my query? Below is an example of the code.
index.js
'use strict';
const Alexa = require('alexa-sdk');
const mysql = require('promise-mysql');
const querydb = require('./sqlQuery.js');
const insertdb = require('./sqlInsert.js');
var testSQL = 'SELECT weight, height from users where pin=1100';
var valuesPromise = querydb.getItem(testSQL);
valuesPromise.then((result)=>{
rows = result.height;
row2 = result.weight;
console.log(rows);
console.log (row2);
return true;
}).catch(function(error){
console.log (error);
});
databaseConnection.js
var mysql = require('promise-mysql');
pool = mysql.createPool({
host: "hostURL",
database: "dbName",
user: "dbUser",
password: "dbPassword",
connectionLimit: 1
});
function getSqlConnection() {
return pool.getConnection().disposer(function(connection) {
pool.releaseConnection(connection);
});
}
module.exports = getSqlConnection;
sqlQuery.js
var Promise = require("bluebird");
var getSqlConnection = require('./databaseConnection');
function getItem(sql){
return Promise.using(getSqlConnection(), function(connection) {
return connection.query(sql).then(function(rows) {
return rows[0];
}).catch(function(error) {
return (error);
});
})
};
module.exports.getItem = getItem;
Since you are in node process you can just exit from it with process.exitCode = 1 and then process.exit() or process.exit(1).
Here is the documentation.
That however is not the best practice. The easiest way would be to just throw an error:
throw "Exit!" or some error you would like to catch and re-throw to exit (in the scenario where you have a catch statement)
Related
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);
});
I have a script that I want to run on a scheduled basis in node. The script is not terminating and exiting. I suspect that this is because my database client is still open.
var client = new pg.Client(conString);
client.connect();
function registerBundle (innerHash, outterHash) {
// some stuff here
}
var query = client.query("SELECT id, chain FROM mytable where \
state_ready = true and transaction_id='' ");
query.on('row', function(row) {
var chain = row['chain'];
var pg_record = row['id'];
console.log(pg_record);
var innerHash = "something";
var outerHash = "something else";
var registrar = registerBundle(innerHash, outerHash);
var update = client.query('UPDATE mytable SET transaction_id = $1::text \
where id=$2::int', [transactionHash, pg_record]);
console.log(chain);
});
if I include the following, the client connection closes before the updates have a time to fire.
query.on('end', function() {
client.end();
});
I cannot use setTimeout or any other such mechanism because I don't know how long to wait for the registerBundle function to complete. Also I think query.on('end' will fire when the update is completed. Not sure how to test this.
My question, I need things to fire in order.
Query DB
Process each row (query.on
Update each row with value returned from registerBundle
Close db client/connection when all rows have been processed.
Terminate script and exit node
Seems pretty straightforward from a python/php world but falls apart in my javascript world.
A promise-based interface like pg-promise is the way to go:
var bluebird = require('bluebird');
var pgp = require('pg-promise')({
promiseLib: bluebird
});
var db = pgp(/*connection details*/);
db.tx(t => {
// BEGIN executed
return t.map('SELECT id, chain FROM mytable where state_ready = $1 and transaction_id = $2', [true, 123], a => {
var chain = data.chain;
var pg_record = data.id;
return t.none('UPDATE mytable SET transaction_id = $1::text where id=$2::int', [transactionHash, pg_record]);
}).then(t.batch); // settling all internal queries
})
.then(data => {
// success, COMMIT executed
})
.catch(error => {
// error, ROLLBACK executed
})
.finally(pgp.end); // shuts down the connection pool
The example above does exactly what you asked for, plus it uses a transaction. But in reality you're gonna want to do it all in one query, for performance reasons ;)
See more examples.
Been testing mysql vs mysql2, seems like 2 has made some improvments however it's not an exact drop in replacement. At the same time Q is a good library which seems easier to integrate with however bluebird seems to take less memory and run faster so...
My current mysql-bluebird connector is as follows and allows for straight forward use of query('SELECT email FROM users.users WHERE id=?',id).then(function(res){var email=res[0][0];});
/* global module, require */
var conf=require('./conf.js').conf;
var mysql = require('mysql');
var Promise = require('bluebird');
var using = Promise.using;
Promise.promisifyAll(require('mysql/lib/Connection').prototype);
Promise.promisifyAll(require('mysql/lib/Pool').prototype);
var pool = mysql.createPool(conf.mysql);
var getConnection = function () {
return pool.getConnectionAsync().disposer(function (connection) {
return connection.release();
});
};
var query = function (command) {
return using(getConnection(), function (connection) {
return connection.queryAsync(command);
});
};
function queryWrapper(q,a){
if(a){
return query(mysql.format(q,a));
}
else{
return query(mysql.format(q));
}
}
module.exports = {
query: queryWrapper
};
So far my attempts ad doing this with mysql2 haven't panned out.
Does anyone have any insights on how to convert this?
Thanks, Jegsar
You can use mysql2-promise. It's a simple wrapper, using q, that promisifies mysql2. If you'd rather use Bluebird, you can look at how this wrapper was created and do it yourself.
node-mysql2 now has Promise api built in, and you can choose which promise implementation you want to use
var mysql = require('mysql2/promise');
mysql.createConnection({
Promise: require('bluebird'), // if not set, global Promise is used
user: 'foo',
password: 'bar',
database: 'baz'
})
.then((conn) => conn.query('select 1+1 as test'))
.then(([rows, fields]) => console.log(rows[0]))
How do I "promisify" my own function (that lives in another directory)? Here's my code:
// app.js
// include database
var mongo = require('./mongo');
var promise = require('bluebird');
var u = require('./util.js');
var mongo.connect = promise.promisify(require(mongo.connect));
// connect to database
var ago = new Date(new Date()-60000); // 10m ago
var scope = {size:0.01};
mongo.connect()
.then(
mongo.endor.mapReduceAsync(u.mapTile, u.reduceTile,{
out: {replace:'deathstar'}, scope:scope,
query: {time:{$gte:ago}}, finalize:u.finalTile}))
.then(deathstar.find({},{fields:{_id:0, value: 1}})
.toArray(function(err, docs){
endor.insertAsync(_.map(docs, function(doc){return doc.value;}),{w:1});
)
);
And here's the other file
// mongo.js
var promise = require('bluebird');
var mongodb = require('mongodb');
var MongoClient = mongodb.MongoClient;
promise.promisifyAll(mongodb);
module.exports.connect = promise.method(function(){
// database environment
var database;
if (process.env.NODE_ENV == 'production'){database = pro;}
else{database = database = dev;}
module.exports.database = database;
// connect to database
return MongoClient.connectAsync(database)
.then(function(db){
module.exports.db = db;
promise.all([
db.createCollectionAsync('hoth', {w:1})
.then(function(collection){
collection.ensureIndexAsync()
.then(function(index){
module.exports.hoth = collection;
console.log(index+' hoth');
});
}),
db.createCollectionAsync('endor', {w:1})
.then(function(collection){
collection.ensureIndexAsync({})
.then(function(index){
module.exports.endor = collection;
console.log(index+' endor');
});
}),
db.createCollectionAsync('alderaan', {w:1})
.then(function(collection){
collection.ensureIndexAsync({time:-1, location:'2dsphere'})
.then(function(index){
module.exports.endor = collection;
console.log(index+' alderaan');
});
})
]).then(function(){callback(null);});
}
);
});
and here's the error
/Users/Josh/Development/xWing/app.js:12
mongo.connectAsync()
^
TypeError: Object #<Object> has no method 'connectAsync'
Promises bring back the qualities of synchronous code.
Given your API is already promisified - You simply return a promise from your function and then use that.
module.exports.connect = Promise.method(function(){
// do a bunch of stuff
return mongo.connectAsync().then(...
});
Note the Promise.method bit isn't required, it just turns thrown errors into rejections to give you throw safety and makes sure you return a promise so it's best practice and guards you against mishaps.
Problem with Promised Connections
I recently converted my Node app from running on my local machine to utilizing an Amazon EC2 for the Node app and a VPN for the file-serving and MySQL.
I learned just enough about Promises to write the following connection snippet (which runs 3 queries before responding to the client), utilizing Bluebird. The connections worked on my machine, but with the VPN hosted MySQL settings, the connections crashed every time, about 30 seconds after the app started, which I realized was probably because I'd forgotten to close them.
EDIT: Based on the comments, it appears the issue is not in the connection closures.
So I modified my script in the best way I knew to close the connections, but with Promises, this is confusing. This version of the connection doesn't work. It doesn't fail or cause any errors. It just returns no results on the server side. I think my problem is in the way I've closed the connections.
What's causing the issue?
Is it the connection closures?
If so, how would I close them properly?
My (Simplified) MySQL Connection Attempt with Bluebird Promises
var mysql = require('mysql');
var Promise = require('bluebird');
var moment = require('moment');
function createConnection() {
var connection = mysql.createConnection({
dateStrings : true,
host : 'hostname',
user : 'username',
password : 'password',
database : 'database'
});
connection = Promise.promisifyAll(connection);
return connection;
}
function sendGame(req, res, sales, settings, categories, players) {
var game = new Object();
game.sales = sales;
game.players = players;
game.settings = settings;
game.categories = categories;
var JSONgame = JSON.stringify(game);
console.log("Game: " + JSON.stringify(game, undefined, 4));
}
var retrieveSales = Promise.method(function (username, connection, timeFrame) {
console.log('User ' + username + ' retrieving sales...');
var q = 'select * from sales_entries where date BETWEEN ? AND ?';
return connection.queryAsync(q, timeFrame).then(function (results) {
return results[0];
});
});
var retrieveSettings = Promise.method(function (username, connection) {
console.log('User ' + username + ' retrieving settings...');
var q = 'select * from sales_settings';
return connection.queryAsync(q).then(function (results) {
return results[0];
});
});
var retrieveCategories = Promise.method(function (username, connection) {
console.log('User ' + username + ' retrieving categories...');
var q = 'select * from sales_categories';
return connection.queryAsync(q).then(function (results) {
return results[0];
});
});
var retrievePlayers = Promise.method(function (username, connection) {
console.log('User ' + username + ' retrieving players...');
var q = 'select * from users';
return connection.queryAsync(q).then(function (results) {
return results[0];
});
});
var gameSucceed = Promise.method(function gameSucceed(req, res) {
var username = req.body.username;
console.log('User ' + req.body.username + ' retrieving game...');
var timeFrame = [moment().days(0).hour(0).minute(0).second(0).format("YYYY-MM-DD HH:mm:ss"), moment().days(6).hour(0).minute(0).second(0).format("YYYY-MM-DD HH:mm:ss")];
//var connection = Promise.promisifyAll(createConnection());
return connection.connectAsync().then(function () {
console.log('Connection with the MySQL database openned for Game retrieval...');
return Promise.all([retrieveSales(username, connection, timeFrame), retrieveSettings(username, connection), retrieveCategories(username, connection), retrievePlayers(username, connection)]);
}).then(function () {
connection.end(),
console.log("...Connection with the MySQL database for Game retrieval ended")
});
});
function getGameData(req, res) {
gameSucceed(req, res).spread(function (sales, settings, categories, players) {
return sendGame(req, res, sales, settings, categories, players);
});
};
var req = new Object();
var res = new Object();
req.body = {
"username" : "user123",
"password" : "password"
}
getGameData(req, res);
Console Result
User user123 retrieving game...
Connection with the MySQL database openned for Game retrieval...
User user123 retrieving sales...
User user123 retrieving settings...
User user123 retrieving categories...
User user123 retrieving players...
...Connection with the MySQL database for Game retrieval ended
Game: {}
var gameSucceed = function gameSucceed(req, res) {
…
var connection = createConnection());
return connection.connectAsync().then(function () {
return Promise.all([…]);
}).then(function () {
connection.end();
});
};
The promise that is ultimately returned from this method does not have a resolution value. It is created by that then call from whose callback you do not return - which will lead to undefined. To fix this, just route the result through:
.then(function(results) {
connection.end();
return results;
});
However, if you do it like that the connection won't be closed in case of an error. The best solution is to use the finally() method, which just works like a finally clause in synchronous code. It's callback will be invoked both for resolutions and rejections, and the resulting promise will automatically carry on the value.
.finally(function() {
connection.end();
})
// .then(function(results) { })
Your code has a particular resource management problem like Bergi put it. You have to keep remembering when to close the collection and when not to.
The optimal solution would be to use Promise.using however, that's only available in the v2 branch of Bluebird so you're going to have to wait a while.
Until then, you can create your own wrapper method that does more basic scoped resource management:
function connect(fn,timeout){
timeout = (timeout === undefined) ? 8000 : timeout; // connection timeout
return createConnection().then(function(connection){
// run the function, when it resolves - close the connection
// set a 7 second timeout on the connection
return fn(connection).timeout(timeout).finally(function(){
connection.end();
});
});
}
Which would let you do:
connect(function(connection){
return gameSucceed(req,resp,connection); // connection is injected to that fn now
}).then(function(val){
// gameSucceed resolution value here
});
Now, when the gameSucceed is done, the connection will close itself automatically. This would make gameSucceed itself look like:
var gameSucceed = Promise.method(function gameSucceed(req, res,connection) {
var username = req.body.username;
console.log('User ' + req.body.username + ' retrieving game...');
var timeFrame = [moment().days(0).hour(0).minute(0).second(0).format("YYYY-MM-DD HH:mm:ss"), moment().days(6).hour(0).minute(0).second(0).format("YYYY-MM-DD HH:mm:ss")];
return connection.connectAsync().then(function () {
console.log('Connection with the MySQL database openned for Game retrieval...');
return Promise.all([retrieveSales(username, connection, timeFrame), retrieveSettings(username, connection), retrieveCategories(username, connection), retrievePlayers(username, connection)]);
}); // no longer its responsibility to handle the connection
});
Generally, you might also want to consider a more OOPish style of coding for your code.
Good luck, and happy coding.