Reputation: 733
Current State of the Problem (After drilling down through many issues)
So, it comes down to this. The following independent code [not a prt of the project] fails to function in Hapi JS while works using simple Node.
var Deasync = require('deasync');
var Hapi = require('hapi');
var mysql = require('mysql');
var server = new Hapi.Server();
server.connection({ port: 4000 });
var someAsyncFunction = function () {
var connection, done, preCoins;
console.log("fetchPreCoins called!");
connection = mysql.createConnection({
host: 'xx.xx.xx.xx',
port: 3306,
user: 'xxx',
password: 'xxxx',
database: 'db_xxx'
});
connection.connect();
preCoins = null;
done = false;
console.log("Just before connection!");
connection.query('SELECT preCoins from tbl_nzk_user_orbs WHERE userId = "' + "abc" + '"', function(err, rows, fields) {
if (err) {
console.log("MySQL DB Failed!");
}
if (!rows[0]) {
connection.end();
preCoins = null;
done = true;
console.log("Did not find");
} else {
connection.end();
preCoins = rows[0].preOrbs;
done = true;
}
});
require('Deasync').loopWhile(function() {
return !done;
});
console.log("fetchPreCoins about to return");
return preOrbs;
};
server.route({
method: 'GET',
path: '/',
handler: function (request, reply) {
reply(someAsyncFunction());
}
});
server.start(function () {
console.log('Started server!');
});
Original Question: I have a queer scenario where the output code of my Coffee Script works if I put it in an independent file and run it with Node. But fails to run when it is inside a route in a restful API server written using HAPI JS.
First of all, to give an idea of the use-case; there is already a huge codebase in which this is a late addition for an extra check. I am basically out of time to refactor everything and use this in the default asynchronous way.
After some research, I am using the wonderful npm module "deasync". The following is output code on Coffee compilation, is there a configuration issue or something which prevents HAPI from making request to this external server. There is no error or anything on the terminal console, it just freezes and then times out:
mysql = require('mysql');
deasync = require('deasync');
fetchPreCoins = function(userId) {
var connection, done, preCoins;
console.log("fetchPreCoins called!");
connection = mysql.createConnection({
host: 'xx.xx.xx.xx',
port: 3306,
user: 'xxxx',
password: 'xxxxx',
database: 'xxxx'
});
connection.connect();
preCoins = null;
done = false;
console.log("Just before connection!");
connection.query('SELECT preCoins from tbl_xxx WHERE userId = "' + userId + '"', function(err, rows, fields) {
if (err) {
console.log("MySQL DB Failed!");
}
if (!rows[0]) {
connection.end();
preCoins = null;
done = true;
console.log("Did not find");
} else {
connection.end();
preCoins = rows[0].preCoins;
done = true;
}
});
require('deasync').loopWhile(function() {
return !done;
});
console.log("fetchPreCoins about to return");
return preCoins;
};
Calling it like this works perfectly in an external JS file when executed with Node:
fetchPreCoins();
The packages are properly included in the shrinkwrap and installed for my HAPI-based API server. However, the last log is "Just before connection!" which is where it gets stuck and times out. I really need this method to be synchronous or basically to wait for the response from the MySQL server and return the value.
In case of HAPI route, am just calling this like: preCoins = Helpers.fetchPreCoins(userId)
And I tried the same thing in the external file as:
value = fetchPreCoins(userId);
console.log("Value is: " + value);
The function returns properly. How can I make this work with Hapi JS. Is there a compatibility issue between Node's MySQL module and HAPI ? I had a look at hapi-mysql but I don't think that solves anything either.
Here is a subset of the HAPI route's code [CoffeeScript] which works perfectly except for when this method is called where it gets stuck.
method: 'POST'
path: '/sessions/users'
config:
description: 'Create session/get token (login) for a user'
auth: false
validate:
payload:
password: Joi.string().required()
username: Joi.string().regex(Models.usernameRegex).lowercase().trim()
pre:
[
assign: 'user'
method: (request, next) ->
Models.User.findOne(username: request.payload.username, '+passwordHash +passwordHashSalt').execute(next)
,
(request, next) ->
Helpers.passwordCheck(request.payload.password, request.pre.user.passwordHashSalt, request.pre.user.passwordHash, next)
# TODO: Could reset shared secret to improve security
]
handler: (request, reply) ->
prevDate = new Date(request.pre.user.lastSeen).setHours(0,1,0)
if (Helpers.timeDifference(prevDate, 60) >= 24)
if request.pre.user.experience < 100
addAmount = 4
else if request.pre.user.experience >= 100 and request.pre.user.experience < 1000
addAmount = 7
else if request.pre.user.experience >= 1000 and request.pre.user.experience < 4000
addAmount = 10
else if request.pre.user.experience >= 4000 and request.pre.user.experience < 10000
addAmount = 13
else if request.pre.user.experience >= 10000
addAmount = 16
else
addAmount = 3 # this should not happen though!
if request.pre.user.membership != "free"
addAmount += 10 # extra bonus for members
console.log "user Id: ", request.pre.user.id
preCoins = Helpers.fetchPreCoins(request.pre.user.id)
console.log "Pre coins fetched: ", preCoins
if preCoins != null
if preCoins == request.pre.user.coins
request.pre.user.coins += addAmount
else
request.pre.user.coins = preCoins + addAmount # the case when bug had occured and this reverts it
else
request.pre.user.coins += addAmount
Helpers.writePreCoins(request.pre.user.id, request.pre.user.coins)
Helpers.updateNotification("continuous-day-member-coins", request.pre.user.notifications, request.pre.user)
Helpers.updateNotification("continuous-day-coins", request.pre.user.notifications, request.pre.user)
request.pre.user.lastSeen = Date.now()
request.pre.user.store()
reply(request.pre.user.toObject(transform: true))
Update: Just to test, I stopped the "deasync" for a moment and on the HAPI API console, I get this timeout error. I can't figure out why this should happen since it is a very simple select query:
11:02:16 web.1 | Debug: hapi, internal, implementation, error
11:02:16 web.1 | Error: connect ETIMEDOUT
11:02:16 web.1 | at Connection._handleConnectTimeout (/Users/nikhilkhullar/Desktop/hapi-server/node_modules/mysql/lib/Connection.js:358:13)
11:02:16 web.1 | at Socket.g (events.js:180:16)
11:02:16 web.1 | at Socket.emit (events.js:92:17)
11:02:16 web.1 | at Socket._onTimeout (net.js:326:8)
11:02:16 web.1 | at Timer.unrefTimeout [as ontimeout] (timers.js:427:13)
Update 2: I have found out that the actual problem is not MySQL as even writing is working when asynchronous. So, it drills down to the fact that the module deasync CAN NOT BE used in Hapi JS. But, sadly this renders this fetch code useless. Is there a way to make this function wait for the reply now, i.e. basically to make connection.query behave synchronously.
Although, it's sad that the same "deasync" is working like a charm outside of Hapi JS...
Upvotes: 0
Views: 707
Reputation: 13570
As the original author of deasync I strongly recommend you (and everyone else) not to use it for anything. It's fragile and only works in some subset of cases.
It's definitely not supposed to work inside of running event loop at all: https://github.com/joyent/node/issues/7555#issuecomment-42295204
Upvotes: 1