Can anyone explain to me why I am processing the 50 records but I never get console.log("all records processed"); to the console.
It is like I am closing a function too soon or too late. What is the best approach when working with call backs because I am sure that is why I do not get "all records processed". I am using node v0.10.26 with the NPM oracle plugin.
var oracle = require('oracle');
var connectData = {
hostname: "127.0.0.1",
port: 1521,
database: "xe", // System ID (SID)
user: "user",
password: "password"
};
oracle.connect(connectData, function(err, connection) {
if (err) {
console.log("Error connecting to db:", err);
return;
}
connection.setPrefetchRowCount(50);
var reader = connection.reader("SELECT * FROM CARS", []);
function doRead(cb) {
reader.nextRow(function(err, row) {
if (err) return cb(err);
if (row) {
// do something with row
console.log("got " + JSON.stringify(row));
// recurse to read next record
return doRead(cb)
} else {
// we are done
return cb();
}
});
}
doRead(function(err) {
if (err) throw err; // or log it
console.log("all records processed");
});
});
Does it make a difference that you don't have a semicolon after the closing brace of connectData? Personally, I just set more and more console logs until I figure out the line that's messing everything up. That, or use breakpoints if you can.
For these step by step callbacks, I suggest you use async to manage the callback pyramid.
Related
I'm trying to update a database using Mongoose, but I'm getting this Network error while running my node app.
const mongoose = require('mongoose')
mongoose.connect("mongodb://localhost:27017/fruitsDB")
const fruitsSchema = new mongoose.Schema({
name: {
type: String,
required: [true, "Why no Name?"]
},
rating: {
type: Number,
min: 1,
max: 10
},
review: String
});
const Fruit = mongoose.model("Fruit", fruitsSchema)
Fruit.find(function(err, fruits){
if(err){
console.log(err)
}
else{
mongoose.connection.close();
fruits.forEach(function(fruit){
console.log(fruit.name)
})
}
})
Fruit.updateOne({_id:"62b6a681eb136efde7ed17bc"}, {name: "Banana"}, function(err){
if(err){
console.log(err)
}
else{
console.log("Successfully updated the document")
}
})
Error: Commnad line error while running the node app
MongoNetworkError: connection establishment was cancelled
at connectionFailureError
at CancellationToken.<anonymous>
at Object.onceWrapper (node:events:641:28)
at CancellationToken.emit (node:events:527:28)
at ConnectionPool.close
at Server.destroy
at destroyServer
at eachAsync
It's a simple Node app created using Mongoose.
Calling the find function last worked for me. I mean, like this -
Fruit.updateOne({_id:"62b6a681eb136efde7ed17bc"}, {name: "Banana"}, function(err){
if(err){
console.log(err)
}
else{
console.log("Successfully updated the document")
}
})
Fruit.find(function(err, fruits){
if(err){
console.log(err)
}
else{
mongoose.connection.close();
fruits.forEach(function(fruit){
console.log(fruit.name)
})
}
})
Closing the connection should be at end which is the reason why the code is not getting properly executed.
I tried Calling the find function last but still getting the same error like this:
MongoNetworkError: connection establishment was cancelled
I don't know how to tackle this issue when it occurs in a running application but for now if you want to just insert the docs in collection then just comment the .find method completely and then run the application it will be inserted successfully and then comment the .updateOne method and uncomment the .find method by doing you will be successfully added the docs and could get the find result.
I did the same!
OR
I found out that for some reason .find method gets executed before .updateOne so the connection were being closed before the collection gets updated.
So, if we do this it works.
Fruit.updateOne({_id:"62b6a681eb136efde7ed17bc"}, {name: "Banana"}, function(err) {
if (err) {
console.log(err)
} else {
Fruit.find(function(err, fruits) {
if (err) {
console.log(err)
} else {
mongoose.connection.close();
fruits.forEach(function(fruit) {
console.log(fruit.name)
})
}
});
console.log("Successfully updated the document")
}
})
You can't close the connection in the find method. You are not able to interact with the db after you closed the connection. Just put mongoose.connection.close() at the end.
Just Do something like this:
`People.insertMany([man1, man2, man3],
function (err) {
if (err) {
console.log(err);
} else {
console.log("Successfully saved all
models in the database");
mongoose.connection.close();
}
});`
Closing the connection inside insertmany worked for me
After reading some solutions the TL:DR is: the mongoose.disconnect();, it's the one causing problems, the connection is being terminated before you can update anything
You should be careful while using crud methods on database. Because those methods are asynchronous.
In your case the find method executed first and closed the database connection prior to updateOne method.
Solution: You can solve that by simply changing the logic in your code like embedding find method inside updateOne method or viseversa according to your need.(By embedding we are making a way to execute them in order)
Fruit.find(function(err, fruits) {
if (err) {
console.log(err)
} else {
fruits.forEach(function(fruit) {
console.log(fruit.name)
})
Fruit.updateOne({_id:"62b6a681eb136efde7ed17bc"}, {name: "Banana"}, function(err) {
if (err) {
console.log(err)
} else {
mongoose.connection.close();
console.log("Successfully updated the document")
}
})
}
})
Followed a tutorial to get this working for stored procedures inside of Oracle. I have my GET/SELECT statements working correctly where based on the user making the GET call it changes the pool so that the SELECTs are from the correct user.
Pool creations that work for GET/SELECT
async function initialize() {
await oracledb.createPool({
user: 'user1',
password: 'pass1',
connectString: 'oracledb.website/dev',
poolAlias: 'pool1'
});
await oracledb.createPool({
user: 'user2',
password: 'pass2',
connectString: 'oracledb.website/dev',
poolAlias: 'pool2'
});
}
The tutorial I followed for stored procedures can be found here: https://blogs.oracle.com/opal/using-dbmsoutput-with-nodejs-and-node-oracledb
You will see that in this example he has a new pool being created for every request.
oracledb.createPool(
dbconfig,
function(err, pool) {
if (err)
console.error(err.message)
else
doit(pool);
});
var doit = function(pool) {
Note that the dbConfig used above is an array like:
dbconfig.hrPool.user = 'user3';
dbconfig.hrPool.password = 'pass3';
dbconfig.hrPool.connectString = 'oracle.site/dev';
This will cause issues if you specify a poolAlias and you will quickly end up trying to create a pool alias that already exists with an error like:
"NJS-046: poolAlias "pool1" already exists in the connection pool cache.
I have attempted to update this code myself but I am not familiar enough with asyc/waterfalls/callbacks to get it to keep going.
What I attempted is below (it never actually runs anything):
return new Promise(async (resolve, reject) => {
async.waterfall(
[
function(cb) {
oracledb.getConnection('pool1');
},
enableDbmsOutput,
createDbmsOutput,
fetchDbmsOutputLine
],
function (err, conn, cb) {
if (err) {
console.error("In waterfall error cb: ==>", err, "<== THIS IS WHERE THE ORACLE ERROR WILL SHOW!");
// Release the Oracle Connection
conn.release(function (err) {
if (err) console.error(err.message);
});
}
}
);
var enableDbmsOutput = function (conn, cb) {
conn.execute(
"BEGIN DBMS_OUTPUT.ENABLE(NULL); END;",
function(err) { return cb(err, conn); });
};
var createDbmsOutput = function (conn, cb) {
console.log('I NEVER MAKE IT HERE')
conn.execute(query
,function(err) { return cb(err, conn); });
};
var fetchDbmsOutputLine = function (conn, cb) {
conn.execute(
"BEGIN DBMS_OUTPUT.GET_LINE(:ln, :st); END;",
{ ln: { dir: oracledb.BIND_OUT, type: oracledb.STRING, maxSize: 32767 },
st: { dir: oracledb.BIND_OUT, type: oracledb.NUMBER } },
function(err, result) {
if (err) {
return cb(err, conn);
} else if (result.outBinds.st == 1) {
return cb(null, conn); // no more output
} else {
resolve(result);
return fetchDbmsOutputLine(conn, cb);
}
});
};
})
}
Would really appreciate any help!
The blog you quoted is a command-line script and only creates a pool once. That happens at the start of the script. It also is an old blog post. All its async module calls and JS callbacks should/would now be replaced by Node.js's newer async/await syntax. Also avoid using Promise() directly - code gets too confusing.
Since you are creating some kind of web listener, you should create the pool during app start up, but not for each web request.
Check the node-oracledb example webapp.js.
async function init() {
try {
await oracledb.createPool({
user: dbConfig.user,
password: dbConfig.password,
connectString: dbConfig.connectString
});
// Create HTTP server and listen on port httpPort
const server = http.createServer();
server.on('error', (err) => {
console.log('HTTP server problem: ' + err);
});
server.on('request', (request, response) => {
handleRequest(request, response);
});
await server.listen(httpPort);
console.log("Server is running at http://localhost:" + httpPort);
} catch (err) {
console.error("init() error: " + err.message);
}
}
async function handleRequest(request, response) {
... // Your code to handle each web request goes here.
}
init();
You may also wait to review the PL/SQL procedure example plsqlproc.js.
Hello you beautiful people you.
I'm trying to create REST APIs using node.js connected to an OracleDB, but i'm pulling my hair out trying to get these stupid bind variables working.
Here's my code:
app.get('/mailsummary/:SCHEMA', function (req, res) {
"use strict";
oracledb.getConnection(connAttrs, function (err, connection) {
if (err) {
// Error connecting to DB
res.set('Content-Type', 'application/json');
res.status(500).send(JSON.stringify({
status: 500,
message: "Error connecting to DB",
detailed_message: err.message
}));
return;
}
connection.execute("select * from :SCHEMA.event#db3", [req.params.SCHEMA], {
outFormat: oracledb.OBJECT // Return the result as Object
}, function (err, result) {
if (err || result.rows.length < 1) {
res.set('Content-Type', 'application/json');
var status = err ? 500 : 404;
res.status(status).send(JSON.stringify({
status: status,
message: err ? "Error getting vendor mailing summary." : "Vendor or DB does nto exist.",
detailed_message: err ? err.message : ""
}));
} else {
res.contentType('application/json').status(200).send(JSON.stringify(result.rows));
}
// Release the connection
connection.release(
function (err) {
if (err) {
console.error(err.message);
} else {
console.log("GET /mailsummary/" + req.params.SCHEMA + " : Connection released");
}
});
});
});
});
For some reason i'm getting the error
OracleDB : ORA-01036: illegal variable name/number
If I remove the bind variable, assign a static value and remove "req.params.SCHEMA" after the sql statement and leave the brackets blank, it works.
connection.execute("select * from peeps.event#db3", [], {
outFormat: oracledb.OBJECT // Return the result as Object
I know it's got to be something simple with the way i'm pulling in the bind variable, but i'm pulling my hair out.
Please help me Obi-Wan Kenobi... you're my only hope.
Thanks!
Bind variables are placeholders used to transfer data between database and client program. You are trying to transfer the text of the SQL statement - your usage won't work.
This bind behavior is not specific to node-oracledb; it's the way Oracle works. It helps keep data and statement text separate.
There is some general bind info at: http://docs.oracle.com/database/122/LNOCI/binding-and-defining-in-oci.htm#GUID-77A26CEA-1C41-46A2-866C-622F9FEB5482
I am attempting to use NodeJS with the Tedious (http://pekim.github.io/tedious/) sql server plugin to make multiple database calls. My intent is to:
1. Open a connection
2. Start a transaction
3. Make multiple database (stored procedure) calls, which will not return any data.
4. Commit transaction (or roll back on error).
5. Close connection
Here is an example .js file, (without using a transaction) for NodeJS where I am attempting to make multiple database calls and it is failing with the error "Requests can only be made in the LoggedIn state, not the SentClientRequest state." Nothing I try resolves this issue.
Does anyone know how to resolve this?
var Connection = require('tedious').Connection;
var Request = require('tedious').Request;
var config = {
userName: 'login',
password: 'password',
server: '127.0.0.1',
options: { rowCollectionOnDone: true }
};
var max = 1;
for (var i = 0; i < max; i++) {
var connection = new Connection(config);
function executeStatement() {
request = new Request("select 42, 'hello world'", function (err, rowCount) {
if (err) {
console.log(err);
} else {
console.log(rowCount + ' rows');
}
});
request.on('row', function (columns) {
columns.forEach(function (column) {
console.log(column.value);
});
});
request.on('doneInProc', function (rowCount, more, rows) {
});
request.on('doneProc', function (rowCount, more, rows) {
console.log('statement completed!')
connection.execSql(request);
});
request.on('returnStatus', function (status) {
console.log('statement completed!')
});
connection.execSql(request);
}
connection.on('connect', function (err) {
// If no error, then good to go...
executeStatement();
});
}
console.log('Done!');
You're trying to execute a statement on a connection that is not established. You're missing an error handler before you call executeStatement.
connection.on('connect', function (err) {
if (err) {
console.log(err); // replace with your code
return;
};
// If no error, then good to go...
executeStatement();
});
Edit:
How to execute multiple statements in a transaction in serial:
var statements = ["select 1", "select 2", "select 3"];
var transaction = new sql.Transaction(connection);
transaction.begin(function(err) {
// ... error checks
async.mapSeries(statements, function(statement, next) {
var request = new sql.Request(transaction);
request.query(statement, next);
}, function(err, results) {
// ... error checks
transaction.commit(function(err, recordset) {
// ... error checks
console.log("Transaction commited.");
});
});
});
You should use tedious connection pools to create a pool of multiple connections.
For node js, a npm module is available at : https://www.npmjs.com/package/tedious-connection-pool
For every new value inside for loop you can acquire a new connection and use connection.reset on doneInProc event.
The case which you have been doing is performing 1st iteration of for loop correctly(LoggedIn State) and as you have proceeded without closing or releasing the connection you are using same connection object (SentClientRequest state).
Hence the same object is at final state when the code reaches second iteration of for loop.
Hope it resolves your issue
you can use Tedious Connection pools https://github.com/pekim/tedious-connection-pool
As #zevsuld and #mannutech said, tedious-connection-pool will enable multiple connections, and prevent erring out when simultaneous requests come into your server.
Below is a generic example that allows you to write multiple queries within one connection pool, and expose them for use in your api. I'm just adding this in case others come along who are trying to accomplish this type of implementation.
const ConnectionPool = require('tedious-connection-pool');
const path = require('path');
require('dotenv').config({
path: path.join(__dirname, '../../.env')
})
let Request = require('tedious').Request;
let poolConfig = {
min: 10,
max: 50,
log: true
}
let connectionConfig = {
userName: process.env.user,
password: process.env.password,
server: process.env.server
};
//create the pool
let pool = new ConnectionPool(poolConfig, connectionConfig);
pool.on('error', function(err) {
console.error(err);
});
// At this point in the code, we have established a connection pool. If you run node, you'll see it log out all then connections to your database.
// Let's add some methods which your server might use in fulfilling requests to various endpoints.
let query1 = (cb, res, query) => {
// acquire a connection:
pool.acquire(function(err, connection) {
if (err) {
console.error(err);
return;
} else {
// form your query
let sql_query = `SELECT column1, colum2 from TABLE WHERE column1 LIKE '${query.param}%%' ORDER BY column1 ASC`
// use the connection as usual:
request = new Request(sql_query, (err, rowCount) => {
if (err) {
console.log(err);
return;
} else {
// console.log('rowCount:', rowCount);
}
//release the connection back to the pool when finished
connection.release();
});
let records = [];
request.on("row", function(columns) {
let rowArray = [];
columns.forEach(function(column) {
rowArray.push(column.value);
});
records.push(rowArray);
});
request.on("doneInProc", function() {
cb(records, res);
});
// lastly exectue the request on the open connection.
connection.execSql(request);
}
});
};
let query2 = (cb, res, query) => {
// acquire a connection:
pool.acquire(function(err, connection) {
if (err) {
console.error(err);
return;
} else {
// form your query
let sql_query = `SELECT column3, colum4 from TABLE2 WHERE column3 LIKE '${query.param}%%' ORDER BY column3 ASC`;
// use the connection as usual:
request = new Request(sql_query, (err, rowCount) => {
if (err) {
console.log(err);
return;
} else {
// console.log('rowCount:', rowCount);
}
//release the connection back to the pool when finished
connection.release();
});
let records = [];
request.on("row", function(columns) {
let rowArray = [];
columns.forEach(function(column) {
rowArray.push(column.value);
});
records.push(rowArray);
});
request.on("doneInProc", function() {
cb(records, res);
});
// lastly exectue the request on the open connection.
connection.execSql(request);
}
});
};
// Let's expose these two functions to the rest of your API:
module.exports = {
query1,
query2
}
I am currently testing how some code stands up against the following scenario:
Node.js application is started and successfully establishes a connection to mongodb
After it has successfully setup a connection, the mongodb server dies and all subsequent requests fail
To do this I have got the following code which makes use of the official driver (found here: https://github.com/mongodb/node-mongodb-native) :
MongoClient.connect('mongodb://localhost:27017/testdb', function(err, db) {
app.get('/test', function(req, res) {
db.collection('users', function (err, collection) {
console.log(err);
if (err) {
// ## POINT 1 ##
// Handle the error
}
else {
collection.find({ 'username': username }, { timeout: true }).toArray(function(err, items) {
console.log(err);
if (err) {
// ## POINT 2 ##
// Handle the error
}
else {
if (items.length > 0) {
// Do some stuff with the document that was found
}
else {
// Handle not finding the document
}
}
});
}
});
});
});
As the mongodb server is no longer running when the request is being handled, I'd made the assumption that at either the points which I have labelled ## POINT 1 ## or ## POINT 2 ##, it would return an error indicating a timeout; this however, isn't the case.
I have tried a number of different settings (including one you can see here that explicitly allows the cursor to timeout), however I cannot seem to enable it in any way. In every configuration I've tried Node.js will simply keep waiting for the find() operation to callback and it never does.
If I start the Node.js application before running mongodb, it catches the error in the connect callback fine, but if the connection dies after that it doesn't seem to handle it in any way.
Is there a setting I am missing or is there no way to detect connections being terminated after they've been established?
Edit: just to be clear, the username variable used in the find method is actually declared in my full code, the code I've put in this post is a cut down version to illustrate the structure and error checking.
UPD:
Based on this post, looks like they've deployed fix that will do the same as what we do here. Not sure if this is already within npm (15.10.13). https://github.com/mongodb/node-mongodb-native/issues/1092#ref-commit-2667d13
After some investigation I've managed to understand what is going on there:
Every time you call any method to deal with database (find, update, insert, etc.) it creates cursor, that has own ID and registers itself to EventEmitter of Db for being called back later. In meantime it registers itself to _notReplied object within same CallBackStore.
But once connection is closed, I couldn't locate anything that would iterate through _notReplied cursors and would trigger them with errors or any logic with timers (it still might be somewhere there). So I've managed to write small work around, that does force triggers cursors with error when DB emits close event:
new mongodb.Db('testdb', new mongodb.Server('localhost', 27017, { }), { safe: true }).open(function (err, db) {
if (!err) {
db.on('close', function() {
if (this._callBackStore) {
for(var key in this._callBackStore._notReplied) {
this._callHandler(key, null, 'Connection Closed!');
}
}
});
// ...
} else {
console.log(err)
}
});
I recommend using first approach instead of MongoClient. Reasons are few: for example when you close connection and then call .find it will properly trigger error in callback, while with MongoClient it won't.
If you are using MongoClient:
MongoClient.connect('mongodb://localhost:27017/testdb', function(err, db) {
if (!err) {
db.on('close', function() {
if (this._callBackStore) {
for(var key in this._callBackStore._notReplied) {
this._callHandler(key, null, 'Connection Closed!');
}
}
});
// ...
} else {
console.log(err);
}
});
What this will do? Once connection is closed, it will iterate through All _notReplied cursors and trigger events for them with error Connection Closed!.
Test case:
items.find({ }).toArray(function(err, data) {
if (!err) {
console.log('Items found successfully');
} else {
console.log(err);
}
});
db.close();
That will force close database connection and trigger close event that you handle earlier and will make sure that cursor will be closed.
UPD:
I've added Issue on GitHub: https://github.com/mongodb/node-mongodb-native/issues/1092 we'll see what they say regarding this.
I had the same problem, and found this page from google.
But your choosed answer didn't resolve the problem and it is as same as you, this._callBackStore can't use
but i tried to wrap the Mongo, and it seems work fine
var MongoClient = require('mongodb').MongoClient;
var mongo = {};
mongo.init = function() {
MongoClient.connect('mongodb://localhost:27017/testdb', function(err, db) {
if (err) {
mongo.DB = '';
} else {
mongo.DB = db;
}
db.on('close', function() {
mongo.DB = '';
});
db.on('reconnect', function() {
mongo.DB = db;
});
}
}
mongo.getdb = function(callback) {
if (mongo.DB) {
callback(null, mongo.DB);
} else {
callback('can not connect to db', null);
}
}
module.exports = mongo;
firstly start server and init() it
and then you can require it and use
mongo.getdb(function(err, db) {
if (err) {
console.log(err);
} else {
db.collection('user').find({'xxx':'xxx'}).toArray(function(err, items) {
console.log(items);
});
}
});
After some further investigation, it seems that you can't specify "offline" timeouts such as in the scenario outlined above. The only timeout that can be specified is one which informs the server to timeout the cursor after 10 minutes of inactivity, however as in the scenario above the connection to the server is down this does not work.
For reference, I found the information here: https://github.com/mongodb/node-mongodb-native/issues/987#issuecomment-18915263 by who I believed to be one of the main contributors to the project.
I'm making api with Hapi and Mongodb (w/o mongoose). Features:
Start responding to API request only if mongo db is available
Stop responding if mongo dies during cycle
Re-start when mongo available again
Keep single connection for all requests
Combining some ideas from other answers and this post https://productbuilder.wordpress.com/2013/09/06/using-a-single-global-db-connection-in-node-js/ my approach is this:
server.js
Utilities.initializeDb(() => {
server.start((err) => {
if (err) throw err;
console.log('Server running at:', server.info.uri);
});
}, () => {
server.stop((err) => {
if (err) throw err;
console.log('Server stopped');
});
});
Utilities.js
"use strict";
const MongoClient = require('mongodb').MongoClient;
const MongoUrl = 'mongodb://localhost:27017/db';
export const Utilities = {
initializeDb: (next, onCrash) => {
const ConnectToDatabase = (params) => {
MongoClient.connect(MongoUrl, (err, db) => {
if (err !== null) {
console.log('#t4y4542te Can not connect to mongo db service. Retry in 2 seconds. Try #' + params.retry);
console.error(err);
setTimeout(() => {
ConnectToDatabase({retry: params.retry + 1});
}, 2000);
} else {
db.on('close', () => {
onCrash();
console.log('#21df24sf db crashed!');
ConnectToDatabase({retry: 0});
});
global.db = global.db || db;
next();
}
});
};
ConnectToDatabase({retry: 0});
}
};
I'm exporting db connection to global space. It feels like not best solution, but I had projects where db connection was passed as param to all modules and that sucked more. Maybe there should be some modular approach where you import db connection where you need it, but in my situation i need it almost everywhere, I would have to write that include statement in most files. This API is pointless w/o connection to db, so I think it might be best solution even if I'm against having something flying magically in global space..