Have you any idea why I don't get any error in the first example? But if I use a callback, I get MongoTimeoutError.
await server.stop();
try {
const r = await db.things.insertOne({ a: 1 }); // no error, r is undefined
assert(!r);
} catch (err) {
console.log(err);
}
await server.restart();
const r = await db.things.insertOne({ a: 1 });
assert(r.insertedCount === 1);
db.things.insertOne({ a: 1 }, (err, result) => {
if (err) {
//"MongoTimeoutError" "Server selection timed out after 10000 ms"
return reject(err);
}
resolve(result);
})
In options I have bufferMaxEntries = 0.
There is an issue with mongodb driver. See the JIRA ticket
Related
I have the following API, the API is inserting into a table based on user selection from the client. User can select different material belonging to same experiment. In my payload, I have materials as array, experiment as string. I tried several ways to resolve my error. Following was the last try:
app.post("/insertMaterials", (req, res) => {
for (let mat of req.body["material"]) {
try {
oracledb.getConnection(
{
user: "some_user",
password: "some_pw",
connectString: "someConnStr",
},
function (err, connection) {
if (err) {
console.error("1" + err);
return;
}
connection.execute(
"INSERT INTO MATERIALS (ID, MAT_NAME, EXPR) VALUES((SELECT max(ID) + 1 FROM MATERIALS), :1, :2)",
[mat, req.body["experiment"]],
(err, result) => {
if (err) {
console.error("log " + err);
}
connection.commit();
connection.close();
}
);
}
);
} catch (error) {
console.log(error);
}
}
return res.status(200).json({
title: "SUCCESS: Materials Inserted",
});
});
I always get:
triggerUncaughtException(err, true / fromPromise /);
^
[Error: DPI-1002: invalid dpiConn handle] { errorNum: 0, offset: 0 }
Before I had a separate function of the block inside the for loop and I also tried with execeuteMany. Still same error. After trying lot other ways and reading in internet, I couldn't solve the issue. Except for finally catching uncaughtException and logging the error:
process.on('uncaughtException', (error, next) => {
let date = new Date()
errorLogStream.write(`Date: ${date}. Err: ${error.stack} \n`)
return
})
By catching this exception, my program does not break anymore and data is always inserted. But it would be great to know how and when this is raised and how this can be resolved or where if I am doing a mistake.
UPDATE
Payload example: {'material': ['F99999.7', 'J84845.4'], 'experiment': 'NA32R'}
Function:
async function addMatToExpr(exp, mat) {
let connection;
try {
connection = await oracledb.getConnection(
{
user: "some_user",
password: "some_pw",
connectString: "someConnStr",
});
result = await connection.execute("INSERT INTO MATERIALS (ID,
MAT_NAME, EXPR) VALUES((SELECT max(ID) + 1 FROM MATERIALS), :1, :2)",
[exp, mat], { autoCommit: true })
} catch (error) {
return res.status(404).json({
title: error,
});
} finally {
if (connection) {
try {
await connection.close()
} catch(error) {
console.log(error)
}
}
}
}
API:
app.post("/insertMaterials", (req, res) => {
for (let mat of req.body["materials"]) {
addMatToExpr(req.body["experiment"], mat)
}
});
Added the async/await function and the api that calls the function.
You need to 'await' the Oracle function calls so each completes before continuing. Currently the connection is being closed before the statement is executed.
See all the node-oracledb documentation and examples.
E.g.
async function run() {
let connection;
try {
connection = await oracledb.getConnection(dbConfig);
result = await connection.execute(sql, binds, options);
console.dir(result, { depth: null });
} catch (err) {
console.error(err);
} finally {
if (connection) {
try {
await connection.close();
} catch (err) {
console.error(err);
}
}
}
}
After searching through countless posts I don't understand why I'm still getting a Promise pending after my awaits. The code below should explain it but I'm trying to pull a MongoDB query of the max value of a column/schema. The console.log within the function is giving me the correct timestamp but I'm trying to pass that out of the inner scope and function to another function.
This is pure NodeJS with only MongoDB imported. Can this be done without any external packages?
export async function getMaxDate() {
var time = MongoClient.connect(url, { useUnifiedTopology: true }, function (err, db) {
if (err)
throw err;
var dbo = db.db(getDB);
dbo.collection(getColl)
.find()
.limit(1)
.sort({ 'timestamp': -1 })
.toArray(function (err, result) {
if (err)
throw err;
time = result[0].time; // THIS IS GIVING THE CORRECT VALUE
console.log(time)
db.close();
});
});
return time
}
export async function getMax() {
var block = await getMaxDate();
return block
}
var t = getMax();
console.log(t); // THIS IS GIVE ME A PROMISE PENDING
getMax() returns a promise, you have to wait for it.
var t = await getMax()
Also getMaxDate uses an async callback that you want to promisify:
export async function getMaxDate() {
return new Promise((resolve,reject) => {
MongoClient.connect(url, { useUnifiedTopology: true }, function (err, db) {
if (err)
return reject(err);
var dbo = db.db(getDB);
dbo.collection(getColl)
.find()
.limit(1)
.sort({ 'timestamp': -1 })
.toArray(function (err, result) {
if(err)
reject(err);
else {
let time = result[0].time; // THIS IS GIVING THE CORRECT VALUE
console.log(time)
db.close();
resolve(time);
}
});
})
});
}
For reference, A is the same thing as B here:
async function A(x) {
if(x)
throw new Error('foo');
else
return 'bar';
}
function B(x) {
return new Promise((resolve,reject)=>{
if(x)
reject(new Error('foo'));
else
resolve('bar');
});
}
Promises came first, then async/await notation was introduced to make common Promise coding practices easier
You can use A and B interchangeably:
async function example1() {
try {
await A(1);
await B(0);
}
catch(err) {
console.log('got error from A');
}
}
async function example2() {
return A(0).then(()=>B(1)).catch((err)=>{
console.log('got error from B');
})
}
I have following code, I need to wait till mongodb operation completed.
const nativeClient = require("mongodb").MongoClient;
async function ingestData() {
var data = [
{name: "a", address: "a_add"},
{name: "b", address: "b_add"}
];
console.log(" start ingestData");
await insertPersons(data);
console.log("end ingestData");
}
async function insertPersons(data) {
nativeClient.connect("mongodb://localhost:27017", function(err, client) {
if (err) throw err;
var db = client.db("testdb");
db.collection("person")
.insertMany(data)
.then(function(res) {
console.log(`Number of rec inserted: ${res.insertedCount}`);
return true;
})
.catch(function(err) {
console.log(`Failed to persist ${data.length} rec in db: ${err}`);
return false;
});
});
}
ingestData();
Current Output
start ingestData
end ingestData
Number of rec inserted: 2
Expected output
start ingestData
Number of rec inserted: 2
end ingestData
You are using async/await incorrectly.
If you use await, the called function must return a promise, so there is something to wait for.
If you are not using await anywhere in a function, that function does not need to be marked as async.
You're not doing either of these things.
You can return a promise that resolves when the work is done:
function insertPersons(data) {
return nativeClient.connect("mongodb://localhost:27017").then(client => {
return client.db("testdb").collection("person").insertMany(data).then(res => {
console.log(`Number of rec inserted: ${res.insertedCount}`);
return true;
});
}).catch(function(err) {
console.log(`Failed to persist ${data.length} rec in db: ${err}`);
return false;
});
}
or you can use await to wait until the work is done.
async function insertPersons(data) {
try {
let client = await nativeClient.connect("mongodb://localhost:27017"):
let db = client.db("testdb");
let res = await db.collection("person").insertMany(data);
console.log(`Number of rec inserted: ${res.insertedCount}`);
return true;
} catch (err) {
console.log(`Failed to persist ${data.length} rec in db: ${err}`);
return false;
}
}
I am making a request to a mySQL Database and want to work with the returned results. I have the async function
async function doQuery(query) {
try {
console.log('0. connection start');
var connection = MySQL.createConnection({
host: "xxxxxxxx",
user: "yyyyyyyy",
password: "zzzzzzzz"
});
console.log('1. Connection set up. Connecting...');
var queryResults;
await connection.connect(function(err) {
if (err) console.log(err);
console.log("2. Connected!");
connection.query(query, function(err, result) {
if (err) console.log(err);
queryResults = JSON.parse(JSON.stringify(result));
console.log("3. " + util.inspect(result, {depth: 4}));
console.log("3.5 " + util.inspect(queryResults, {depth: 4}));
connection.end();
});
})
console.log ("results before return:" + JSON.stringify(queryResults));
return queryResults;
}
catch (err){
console.log("async err: " + err);
}
}
The function is then called from another function:
function handle async(handlerInput) {
console.log("ExecuteDBSearchHandler");
var query = buildQuery(handlerInput);
try{
var resultsPromise = doQuery(query)
.then(results => {
console.log("4. final results: " + results);
const count = results.length;
var speechText = 'Abfrage Erfolgreich, es gab ' + count + " Ergebnisse.";
return handlerInput.responseBuilder
.speak(speechText)
.withSimpleCard('Abfrage Erfolgreich', speechText)
.withShouldEndSession(false)
.getResponse();
})
}
catch (err){
console.log("error:" + err);
}
}
This code is to be used in a nodejs Alexa skill. When the function runs, I would expect the outputs in the log to be ordered ascending. The output I consistently get is 0, 1, 4, 2, 3, 3.5. The .then part is executed before the actual connection is established and the database queried.
Instead of the .then, I tried a simple await before, making the line
var results = await doQuery(query);
This lead to the exact same result. I am at a loss as to why await is infact not waiting. As a sidenote: While inside the block
connection.query(query, function (err, result) {
if (err) console.log(err);
queryResults = JSON.parse(JSON.stringify(result));
console.log("3. " + util.inspect(result, {depth: 4}));
console.log("3.5 " + util.inspect(queryResults, {depth: 4}));
connection.end();
});
queryResults shows results (in the debug log step 3.5). In the line
console.log ("results before return:" + JSON.stringify(queryResults));
it returns undefined. I tried so many things to get the results to the function scope of doQuery, but I can't seem to achieve that. Does the await fail due to this? And how can I fix it? I have read upwards of a dozen articles and docs about async/await and promises, yet I can't figure this out.
You need to wrap your callback function in Promise to make use of await keyword
For example:
async function dbQuery(query) {
try {
const connection = MySQL.createConnection({
host: "xxxxxxxx",
user: "yyyyyyyy",
password: "zzzzzzzz"
});
return new Promise(function(resolve, reject) {
connection.connect(function(err) {
if (err) reject(err);
connection.query(query, function (err, result) {
if (err) reject(err);
connection.end();
resolve(JSON.parse(JSON.stringify(result)));
});
});
});
} catch {
console.error("async err: " + err);
}
}
The one can also use util.promisify to wrap callback in promise
Hi I have a problem running a loop and getting the return data using Promises.
I have a getStudentMarks method for getting students marks from the database in subject wise.
getStudentMarks: function(studentId, studentStandard) {
console.log("getStudentMarks invoked...");
return new Promise(function(resolve, reject) {
r.table('student_subjects').filter({
"studentId": studentId,
"studentStandard": studentStandard
}).pluck("subjectId", "subjectName").run(connection, function(err, cursor) {
if (err) {
throw err;
reject(err);
} else {
cursor.toArray(function(err, result) {
if (err) {
throw err
} else {
console.log(result.length);
if (result.length > 0) {
studentSubjectArray = result;
var studentMarksSubjectWiseArray = [];
studentSubjectArray.forEach(function(elementPhoto) {
r.table('student_marks').filter({
"studentId": studentId,
"subjectId": studentSubjectArray.subjectId
}).run(connection, function(err, cursor) {
if (err) {
throw err;
reject(err);
} else {
cursor.toArray(function(err, result_marks) {
var studnetMarksDataObject = {
subjectId: studentSubjectArray.subjectId,
subjectName: studentSubjectArray.subjectName,
marks: result.marks
};
studentMarksSubjectWiseArray.push(studnetMarksDataObject);
});
}
});
});
resolve(studentMarksSubjectWiseArray);
}
}
});
}
});
});
}
I'm invoking the method by,
app.post('/getStudentMarks', function(req, reqs) {
ubm.getStudentMarks(req.body.studentId, req.body.studentStandard)
.then((data) => {
console.log('return data: ' + data);
})
.catch((err) => {
console.log(err);
});
});
When I run the code its working absolutely fine there is no error. I get all the student marks object in the studentMarksSubjectWiseArray array. But the problem is even before the studentSubjectArray loops gets completed, the resolve is getting executed and I'm getting a blank array as return. How do I solve the problem. I understand that I'm not doing the Promises right. I'm new to Promises so I'm not being able to figure out the right way.
That happens because inside your studentSubjectArray.forEach statement you perform set of asynchronous operations r.table(...).filter(...).run() and you push their result into the array. However, those actions finish after you perform the resolve(), so the studentMarksSubjectWiseArray is still empty. In this case you would have to use Promise.all() method.
let promisesArray = [];
studentSubjectArray.forEach((elementPhoto) => {
let singlePromise = new Promise((resolve, reject) => {
// here perform asynchronous operation and do the resolve with single result like r.table(...).filter(...).run()
// in the end you would perform resolve(studentMarksDataObject)
r.table('student_marks').filter({
"studentId": studentId,
"subjectId": studentSubjectArray.subjectId
}).run(connection, function(err, cursor) {
if (err) {
throw err;
reject(err);
} else {
cursor.toArray(function(err, result_marks) {
var studnetMarksDataObject = {
subjectId: studentSubjectArray.subjectId,
subjectName: studentSubjectArray.subjectName,
marks: result.marks
};
resolve(studnetMarksDataObject);
});
}
});
});
promisesArray.push(singlePromise)
});
Promise.all(promisesArray).then((result) => {
// here the result would be an array of results from previously performed set of asynchronous operations
});