knex.js - reading then updating throws an Error - javascript

I'm trying to chain read and update operations. First I read (fetch) data from the database based on the id, then I change it and want to update the data in the database as well but I get a Unhandled rejection Error: There is no pool defined on the current client error.
index.js
var id = args.id;
var proj = new Project(id);
proj.read(knex).then(function(rows) {
proj.set(args);
proj.update(knex).then(console.log); // <== ERROR THROWN HERE
});
Project.js (only related functions)
read(knex) {
var self = this;
return knex('projects').where('id', this.id).then(function(rows) {
if (rows.length == 0)
throw '[panda] Read project: no project with id = ' + self.id;
self.set(rows[0]);
return self;
});
}
update(knex) {
console.log(knex('projects').where('id', this.id).update(this).toString());
return knex('projects').where('id', this.id).update(this);
}
Command
$ node index.js projects update --id=5 --name=Test-update
update "projects" set "completion" = 0, "currentIteration" = NULL, "id" = 5, "name" = 'Test-update' where "id" = 5
Unhandled rejection Error: There is no pool defined on the current client
at /home/flawyte/development/projects/js/panda/node_modules/knex/lib/client.js:202:25
at tryCatcher (/home/flawyte/development/projects/js/panda/node_modules/bluebird/js/main/util.js:26:23)
at Promise._resolveFromResolver (/home/flawyte/development/projects/js/panda/node_modules/bluebird/js/main/promise.js:480:31)
at new Promise (/home/flawyte/development/projects/js/panda/node_modules/bluebird/js/main/promise.js:70:37)
at Client.acquireConnection (/home/flawyte/development/projects/js/panda/node_modules/knex/lib/client.js:200:12)
at /home/flawyte/development/projects/js/panda/node_modules/knex/lib/runner.js:136:49
at tryCatcher (/home/flawyte/development/projects/js/panda/node_modules/bluebird/js/main/util.js:26:23)
at Function.Promise.attempt.Promise.try (/home/flawyte/development/projects/js/panda/node_modules/bluebird/js/main/method.js:31:24)
at Runner.ensureConnection (/home/flawyte/development/projects/js/panda/node_modules/knex/lib/runner.js:135:26)
at Runner.run (/home/flawyte/development/projects/js/panda/node_modules/knex/lib/runner.js:30:31)
at QueryBuilder.Target.then (/home/flawyte/development/projects/js/panda/node_modules/knex/lib/interface.js:27:43)
at /home/flawyte/development/projects/js/panda/index.js:80:31
at tryCatcher (/home/flawyte/development/projects/js/panda/node_modules/bluebird/js/main/util.js:26:23)
at Promise._settlePromiseFromHandler (/home/flawyte/development/projects/js/panda/node_modules/bluebird/js/main/promise.js:507:31)
at Promise._settlePromiseAt (/home/flawyte/development/projects/js/panda/node_modules/bluebird/js/main/promise.js:581:18)
at Promise._settlePromises (/home/flawyte/development/projects/js/panda/node_modules/bluebird/js/main/promise.js:697:14)
If I remove the then(console.log) in index.js no error is thrown but data in database remains unchanged after script ending. Why ?

I had a line knex.destroy(); at the very end of my script and as knex is asynchronous, the line was being executed after the read() instruction (due to quick execution) but before update(), causing the error above.
Project.js
var done = false;
proj.read(knex).then(function(rows) {
proj.set(args);
proj.update(knex).then(function(res) {
done = true;
console.log(res);
knex.destroy();
});
});
// ...
if (!done)
// End database connection here
// in case an error prevented it from being destroyed after update
// which would in turn prevent the script from ending
knex.destroy();

Related

Why am I getting "Cannot access 'server' before initialization" error in NodeJS?

I am getting the dreaded Cannot access 'server' before initialization error in code that is identical to code that's running in production.
The only things that have changed are my OS version (macOS 10.11->10.14) my NodeJS version (10->12) and my VSCode launch.json, but I cannot see anything in either that would cause an issue. My Node version went from 10 to 12, but in production it went from 8 to 15 without issue. I routinely keep launch.json pretty sparse, and the same error happens using node server in Terminal.
Here is the offending code. The issue occurs because I have shutdown() defined before server and it references server. It's written to add an event-handler and then cause the event. Yes, it could be refactored but it already works. It works, really. In 21 instances spread over 7 servers.
I have tried changing the declaraion/init of server from const to var but that does not fix it. As mentioned, this is code that's running in prod! What's wrong with my environment?
Maybe a better question is: why did this ever work?
'use strict'
const fs = require('fs');
const https = require('https');
const cyp = require('crypto').constants;
const stoppable = require('./common/stoppable.js');
const hu = require('./common/hostutil');
process.on('uncaughtException', err => {
wslog.error(`Uncaught Exception: ${err} ${err.stack}`);
shutdown();
});
process.on('unhandledRejection', (reason, p) => {
wslog.error(`Unhandled Promise Rejection: ${reason} - ${p}`);
});
// 'shutdown' is a known static string sent from node-windows wrapper.js if the service is stopped
process.on('message', m => {
if (m == 'shutdown') {
wslog.info(`${wsconfig.appName} has received shutdown message`);
shutdown();
}
});
process.on('SIGTERM', shutdown);
process.on('SIGINT', shutdown);
process.on('SIGHUP', shutdown);
function shutdown() {
httpStatus = 503; // Unavailable
wslog.info(`${wsconfig.appName} httpStatus now ${httpStatus} - stopping server...`);
// Error happens on this next line; It should not execute till after server is running already
server.on('close', function () {
wslog.info(`${wsconfig.appName} HTTP server has stopped, now exiting process.`);
process.exit(0)
});
server.stop();
}
// Init and start the web server/listener
var combiCertFile = fs.readFileSync(wsconfig.keyFile, 'utf8');
var certAuthorityFile = fs.readFileSync(wsconfig.caFile, 'utf8');
var serverOptions = {
key: combiCertFile,
cert: combiCertFile,
ca: certAuthorityFile,
passphrase: wsconfig.certPass,
secureOptions: cyp.SSL_OP_NO_TLSv1 | cyp.SSL_OP_NO_TLSv1_1
};
var server = https.createServer(serverOptions, global.app)
.listen(wsconfig.port, function () {
wslog.info(`listening on port ${wsconfig.port}.`);
});
server.on('clientError', (err, socket) => {
if (err.code === 'ECONNRESET' || !socket.writable) { return; }
// ECONNRESET was already logged in socket.on.error. Here, we log others.
wslog.warn(`Client error: ${err} ${err.stack}`);
socket.end('HTTP/1.1 400 Bad Request\r\n\r\n');
});
server.on('error', (err)=>{
if ( err.code === 'EADDRINUSE' ) {
wslog.error(`${err.code} FATAL - Another ${wsconfig.appName} or app is using my port! ${wsconfig.port}`);
} else {
wslog.error(`${err.code} FATAL - Server error: ${err.stack}`);
}
shutdown();
})
combiCertFile = null;
certAuthorityFile = null;
// Post-instantiation configuration required (may differ between apps: need an indirect way to plug in app-specific behavior)
stoppable(server, wsconfig.stopTimeout);
// Load all RESTful endpoints
const routes = require('./routes/');
This is a runtime error, which happens only in a very specific situation. But actually this exact error shouldn't happen with var server = ... but only with const server = ... or let server = .... With var server = ... the error message should say "Cannot read properties of undefined"
What happens
You have an error handler for uncaughtException which is calling shutdown() and in shutdown() you are referencing your server. But consider what happens if your code throws an exception before you initialized your server. For instance if your cert or key cannot be read from the disk, cert or key are invalid ... So nothing will be assigned to server, and an exception will be raised.
Then the handler for your uncaught exception will fire and call the shutdown() function, which then tries to access the server, which of course hasn't been initialized yet.
How to fix
Check what the unhandled exception is, that is thrown before your server is initialized and fix it. In your production environment, there is probably no exception, because the configuration and environment is properly set up. But there is at least one issue in your develepment environment, which causes an exception.
Difference between var and const
And the difference between var server = ... and const server = ... is quite a subtle one. For both, the declaration of the variable is hoisted up to the top of their respective scope. In your case it's always global, also for const. But variables declared as var are assigned a value of undefined whereas variables declared as let/const are not initialized at all.
You can easily reproduce this error if you uncomment either error1 or error2 in the following code. But error3 alone won't produce this ReferenceError because bar will already be initialized. You can also replace const bar = with var bar = and you will see, that you get a different error message.
process.on("uncaughtException", err => {
console.log("uncaught exception");
console.log(err);
foo();
});
function foo() {
console.log("foo");
console.log(bar.name);
}
function init() {
// throw new Error("error1");
return { name: "foobar"}
}
// throw new Error("error2");
const bar = init();
//throw new Error("error3");

Get multiples database on the same server

I use AdonisJS and MSSQL. I have some databases on the same server : https://i.imgur.com/d4Eqfpt.png
In my .env I have this configuration :
DB_CONNECTION=mssql
DB_HOST=127.0.0.1
DB_PORT=1433
DB_USER=sa
DB_PASSWORD=123456
DB_DATABASE=WEB_PANEL
The problem is, i have my own API to make requests like that :
connectToDatabase('mssql://id:pw#localhost').then(async () => {
let onlinePlayers = await User.getOnlinePlayers()
let numberOfStaff = staff.length
let numberOfOnlinePlayers = onlinePlayers.recordset.length
return view.render('system.index', { totalPlayers: numberOfOnlinePlayers, numberOfStaff: numberOfStaff })
})
}
And I have this error :
warning:
warning:
WARNING: Adonis has detected an unhandled promise rejection, which may
cause undesired behavior in production.
To stop this warning, use catch() on promises or wrap await
calls inside try/catch.
TypeError: Cannot read property 'substr' of null
at parseConnectionURI (C:\Users\didi\Desktop\drpanel\panel\node_modules\mssql\lib\connectionstring.js:21:32)
at Object.resolveConnectionString [as resolve] (C:\Users\didi\Desktop\drpanel\panel\node_modules\mssql\lib\connectionstring.js:205:72)
at new ConnectionPool (C:\Users\didi\Desktop\drpanel\panel\node_modules\mssql\lib\base.js:127:40)
at new ConnectionPool (C:\Users\didi\Desktop\drpanel\panel\node_modules\mssql\lib\tedious.js:175:1)
at Object.connect (C:\Users\didi\Desktop\drpanel\panel\node_modules\mssql\lib\base.js:1592:22)
at connectToDatabase (C:\Users\didi\Desktop\drpanel\panel\drapi\src\core\connection.handler.js:4:15)
at SystemController.showSystemPage (C:\Users\didi\Desktop\drpanel\panel\app\Controllers\Http\Panel\SystemController.js:8:9)
at Server._routeHandler (C:\Users\didi\Desktop\drpanel\panel\node_modules\#adonisjs\framework\src\Server\index.js:121:31)
at MiddlewareBase._resolveMiddleware (C:\Users\didi\Desktop\drpanel\panel\node_modules\#adonisjs\middleware-base\index.js:195:28)
at Runnable._invoke (C:\Users\didi\Desktop\drpanel\panel\node_modules\co-compose\src\Runnable.js:76:42)
at C:\Users\didi\Desktop\drpanel\panel\node_modules\co-compose\src\Runnable.js:73:34
at f (C:\Users\didi\Desktop\drpanel\panel\node_modules\once\once.js:25:25)
at Authenticated.handle (C:\Users\didi\Desktop\drpanel\panel\app\Middleware\Authenticated.js:16:19)
at async ConvertEmptyStringsToNull.handle (C:\Users\didi\Desktop\drpanel\panel\app\Middleware\ConvertEmptyStringsToNull.js:13:5)
at async AuthInit.handle (C:\Users\didi\Desktop\drpanel\panel\node_modules\#adonisjs\auth\src\Middleware\AuthInit.js:60:5)
at async Shield.handle (C:\Users\didi\Desktop\drpanel\panel\node_modules\#adonisjs\shield\src\Shield\index.js:417:5)
And for example getOnlinePlayers() is :
static async getOnlinePlayers() {
let onlinePlayers = await sql.query`
USE DR2_USER
SELECT TOP 10 * FROM TB_CharacterSub
WHERE f_ConnectionChannel != 0`
return onlinePlayers
}
For example I want to use the DR2_USER database but i can't :/
Please someone has a solution ?
Thanks !
You can edit your config/database.js to configure multiple database connections and set the Lucid Model to use that connection
https://adonisjs.com/docs/4.1/lucid#_connection
class User extends Model {
static get connection () {
return 'mysql'
}
}

Node.js : how to handle callback with async.js and websocket?

I'm creating a REST API to store setting from a specific camera sending data through TCP.
Camera communicate through TCP with a request/response pattern.
e.g : You can send "Get rate\n" to the camera and it responds "100 fps", in order to get the current framerate of the camera.
How to get data and store it in API?
var command = "rate"; //Array with setting I want to store in my API
function getDataFromCamera(command) { // This function will be iterate in another function
async.series([
console.log('Test1'); // console log to test output
function(callback) {
CAMERA_TCP_SOCKET.send("get "+command+"\n");
callback(null,command);
},
function(callback) {
console.log('Test2'); // console log to test output
CAMERA_TCP_SOCKET.onmessage = function(event) {
callback(null, event.data); // THIS line is the problem. I can't retrieve event.data because i don't know how to callback this variable
}
}],
function(err, results) {
if (err) {
//Handle the error in some way. Here we simply throw it
throw err;
}
if (results) {
console.log(results); // expected output : // ['rate','100']
}
});
}
At the moment, I get this error:
Test1
Test2
/Users/maximecongi/Desktop/Hologram/node_modules/async/dist/async.js:966
if (fn === null) throw new Error("Callback was already called.");
^
Error: Callback was already called.
at /Users/maximecongi/Desktop/Hologram/node_modules/async/dist/async.js:966:32
at /Users/maximecongi/Desktop/Hologram/node_modules/async/dist/async.js:3885:13
at W3CWebSocket.CAMERA_TCP_SOCKET.onmessage (/Users/maximecongi/Desktop/Hologram/core/api.js:91:13)
at W3CWebSocket._dispatchEvent [as dispatchEvent] (/Users/maximecongi/Desktop/Hologram/node_modules/yaeti/lib/EventTarget.js:107:17)
at W3CWebSocket.onMessage (/Users/maximecongi/Desktop/Hologram/node_modules/websocket/lib/W3CWebSocket.js:234:14)
at WebSocketConnection.<anonymous> (/Users/maximecongi/Desktop/Hologram/node_modules/websocket/lib/W3CWebSocket.js:205:19)
at WebSocketConnection.emit (events.js:188:13)
at WebSocketConnection.processFrame (/Users/maximecongi/Desktop/Hologram/node_modules/websocket/lib/WebSocketConnection.js:554:26)
at /Users/maximecongi/Desktop/Hologram/node_modules/websocket/lib/WebSocketConnection.js:323:40
at process.internalTickCallback (internal/process/next_tick.js:70:11)
[nodemon] app crashed - waiting for file changes before starting...
How to solve my problem?
It's because you are listening on every message, and looks like you're sending 3 commands, and I guess, every command get a response. This callback after first serie is not destroyed, its alive. For me, you should check if the response is for this specific command, then execute the problematic callback.

How do I call a Python function from Node.js?

I'm working on making a Homebridge plugin for a project. Homebridge is a Node.js server which I have running on a Raspberry Pi which emulates an Apple HomeKit Bridge.
Using this link, I was able to execute Python code from the following Node.js code:
var Service, Characteristic;
var spawn = require('child_process').spawn;
var py = spawn('python', ['/home/pi/Desktop/RFbulb/nRF24L01PLUS.py']);
var data = [10,10,10];
var dataString = '';
var RFstatus = true;
module.exports = function(homebridge) {
Service = homebridge.hap.Service;
Characteristic = homebridge.hap.Characteristic;
homebridge.registerAccessory("homebridge-RFbulb", "RFbulb", RFbulbAccessory);
}
function RFbulbAccessory(log, config) {
this.log = log;
this.config = config;
this.name = config["name"];
this.address = config["address"];
this.service = new Service.Lightbulb(this.name);
this.service
.getCharacteristic(Characteristic.On)
.on('get', this.getOn.bind(this))
.on('set', this.setOn.bind(this));
}
RFbulbAccessory.prototype.setOn = function(on, callback) { // This is the function throwing the error
var state = on ? "on": "off";
if (state == "on") {
data = [1,parseInt(this.address, 10),100];
dataString = '';
py.stdout.on('data', function(data) {
dataString += data.toString();
});
py.stdout.on('end', function() {
console.log(dataString);
});
py.stdin.write(JSON.stringify(data));
py.stdin.end();
RFstatus = true;
}
callback(null);
}
RFbulbAccessory.prototype.getServices = function() {
return [this.service];
}
Interestingly enough, when I activate the setOn function the first time (for example, to turn the device on) it works fine, but when I activate the setOn function a second time (to turn the device off) I get the following errors and the server exits:
events.js:141
throw er; // Unhandled 'error' event
^
Error: write after end
at writeAfterEnd (_stream_writable.js:166:12)
at Socket.Writable.write (_stream_writable.js:211:5)
at Socket.write (net.js:642:40)
at RFbulbAccessory.setOn (/usr/lib/node_modules/homebridge-RFbulb/index.js:47:12)
at emitThree (events.js:97:13)
at emit (events.js:175:7)
at Characteristic.setValue (/usr/lib/node_modules/homebridge/node_modules/hap-nodejs/lib/Characteristic.js:155:10)
at Bridge.<anonymous> (/usr/lib/node_modules/homebridge/node_modules/hap-nodejs/lib/Accessory.js:710:22)
at Array.forEach (native)
at Bridge.Accessory._handleSetCharacteristics (/usr/lib/node_modules/homebridge/node_modules/hap-nodejs/lib/Accessory.js:655:8)
What could be causing this error? Especially since the function appears to work fine for a single use.
You're getting that error because you're closing the input stream:
py.stdin.end();
After a stream has been closed, you can no longer write to it like you are here:
py.stdin.write(JSON.stringify(data));
If the Python program you're running accepts multiple commands over STDIN then simply remove the py.stdin.end() line.
However, it's likely that your Python program runs once then completes. If that's the case, you will need to respawn the process every time you want the program to run.
if (state === "on") {
py = spawn('python', ['/home/pi/Desktop/RFbulb/nRF24L01PLUS.py']);
...
}

Reference error is not thrown from MongoDB callback

Introduction
All people know that if we call undefined.test we will receive the following error (same for both: NodeJS and Javascript):
$ node
> undefined.test
TypeError: Cannot read property 'test' of undefined
at repl:1:11
at REPLServer.self.eval (repl.js:110:21)
at Interface.<anonymous> (repl.js:239:12)
at Interface.EventEmitter.emit (events.js:95:17)
at Interface._onLine (readline.js:202:10)
at Interface._line (readline.js:531:8)
at Interface._ttyWrite (readline.js:760:14)
at ReadStream.onkeypress (readline.js:99:10)
at ReadStream.EventEmitter.emit (events.js:98:17)
at emitKey (readline.js:1095:12)
That's correct!
How did I find the problem?
Passed week I wasted about 30 minutes in debugging the following problem: A script was stopping accidentally and no error was thrown.
I had the urls variable that was supposed to be an object:
var urls = settings.urls || {};
Then in next lines I needed to get shop key of urls that was a string:
var shop = urls.shop || "/";
I started adding console.log to find the values of variables:
console.log(urls); // undefined
var shop = urls.shop || "/";
console.log("Passed"); // This isn't run.
The problem in my script was that I was redefining a new urls variable that was making the urls undefined, but the question is: why cannot read property "shop" of undefined didn't appear here? Because urls was really undefined.
We know that the following is happening in both: NodeJS and Javascript:
var a = 10;
foo(function () {
console.log(a); // undefined
var a = 10;
});
function foo(callback) { callback(); }
The question
After debugging the problem I found that this problem comes from Mongo: inside of Mongo callbacks if we call undefined.something we DON'T get the error.
I've created a small script that demonstrates this:
var mongo = require("mongodb");
// Mongo server
var server = mongo.Server("127.0.0.1", 27017);
var db = new mongo.Db("test", server, { safe: true });
console.log("> START");
// Open database
console.log("Opening database.");
db.open(function(err, db) {
if (err) { return console.log("Cannot open database."); }
// get collection
console.log("No error. Opening collection.");
db.collection("col_one", function(err, collection) {
if(err) { return console.log(err) }
// do something with the collection
console.log("No error. Finding all items from collection.");
collection.find().toArray(function(err, items) {
if(err) { return console.log(err) }
console.log("No error. Items: ", items);
console.log("The following line is: undefined.shop." +
"It will stop the process.");
console.log(undefined.test); // THE ERROR DOES NOT APPEAR!
console.log("> STOP"); // this message doesn't appear.
});
});
});
My questions are:
Why the error doesn't appear? Which is the reason? (It would be great to debug together the MongoDB source code to find it.)
Why the process is stopped when calling undefined.something?
How can be this solved?
I've created a Github repository where you can download my small application that demonstrates the issue.
Interesting:
If we add a try {} catch (e) {} statement we find the error and the process continue showing the STOP message.
try {
console.log(undefined.test);
} catch (e) {
console.log(e);
}
LOGS:
> START
Opening database.
No error. Opening collection.
No error. Finding all items from collection.
No error. Items: []
The following line is: undefined.shop. It will stop the process.
[TypeError: Cannot read property 'test' of undefined]
> STOP
Looking on github.com at node-mongodb-native driver issues, you will notice that issue is solved in 1.3.18 version. But, I tested it and it does not work as expected.

Categories