Node - Mysql Transaction is not rolling back if one query fails - node.js

I am trying to perform node-mysql transaction which has three query in total. All three are 'INSERT' queries. I intensionally write third query wrong to test rollback but transaction is making entry to database without fail for first two queries.
I know similar question has already been asked several times and I tried almost all of them but no luck
exports.registerNewUserTransaction = async (
res,
userToBeAdded,
nameToBeAdded,
emailToBeAdded) => {
const conn = await db.getConnection();
await conn.beginTransaction();
try {
await this.insertOne('user', userToBeAdded);
await this.insertOne('name', nameToBeAdded);
await this.insertOne('email', emailToBeAdded);
await conn.commit();
res.status(200);
} catch(err) {
await conn.rollback();
res.status(400);
} finally {
await conn.release();
}
};
As you can see I am getting connection object from Pool, beginning the transaction and performing queries one by one. My third query has wrong column name, hence transaction should rollback but I see entry of first two queries. I would really appreciate the correct direction.
node version: 12.8.0
mysql (running in docker): 8.0.15
mysql (npm version): 2.17.1

After struggling a lot, finally figured it out. Here's the answer:
exports.registerNewUserTransaction = async (
res,
userToBeAdded,
nameToBeAdded,
emailToBeAdded) => {
const conn = await db.getConnection();
// My first mistake was not to promisify connection query
conn.query = util.promisify(conn.query);
await conn.beginTransaction();
try {
// My second mistake was not to use same connection
await conn.query('INSERT INTO ...', userToBeAdded);
await conn.query('INSERT INTO ...', nameToBeAdded);
await conn.query('INSERT INTO ...', emailToBeAdded);
await conn.commit();
return res.status(200);
} catch(err) {
await conn.rollback();
return res.status(400);
} finally {
await conn.release();
}
};
Hope this might help someone!

This is important part I missed.
conn.query = util.promisify(conn.query);

Related

node.js firestore delete with a where clause

I've tried to figure this out but don't know enough about node.js or firestore to make it happen. I want to delete a bunch of documents based on a where clause. I have tried a bunch of iterations of this but none of them delete, and using the Google provided code returns an async error:
db.collection("testcollection")
.where("office", "==", 12345).limit(3).get().then((snapshot)=> {
snapshot.docs.forEach(doc => {
//const res = await db.collection('testcollection').doc(id).delete();
const data = doc.data();
console.log(doc.id);
delete(doc.id);
})
});```
Well shoot. Right after posting this I figured it out by using an answer from here Cloud Firestore delete function
and putting doc.id into a variable:
snapshot.docs.forEach(doc => {
const tempid = doc.id;
db.collection("testcollection").doc(tempid).delete().then(function() {
console.log("Document successfully deleted!");
}).catch(function(error) {
console.error("Error removing document: ", error);
});
})
});

Unable to handle promise rejection with mongodb and mongoose

I have the following code, that basically skips the mongodb document entry stage if the incoming documents are already in the database. However this function causes an UnhandledPromiseRejectionWarning when trying to close the db connection immediately after the async is complete.
async dbInsertMany(asset, timeframe, stream){
try{
const [currentAssetModel, multiStream] = this._getMultiModel(asset, timeframe, stream);
// Don't run the db stage if zero length stream received.
if(!multiStream.length==0){
// Refactor this into a try..catch..finally block.
try{
await currentAssetModel.insertMany(multiStream);
console.log('<SUCCESS>');
}catch(err){
console.log('<ERROR>', err);
}finally{
console.log('__DBINSERTMANY:FINALLY__');
};
}else{
// await sleep(1000);
console.log('[WARNNING] No unique documents to add.');
}
}catch(err){
throw new Error('Application failure has occurred!');
}
}
I'm not sure what I'm missing.
The functions that follow are below:
async function dbCycle(){
try{
let asset = new AssetDatabaseHandler();
try{
await asset.dbInsertMany('BTC/EUR', '5m', demoMultiExchangeStream)
console.log('DB_INSERT_MANY: Finished.');
}catch(error){
console.log('DB_INSERT_MANY: [ERROR]', error);
}
}catch(failure){
console.log('[SEVERE_FAILURE]', failure)
}
};
and
(async () => {
try{
await dbConnection.dbConnect();
console.log('Connected.');
const result = await dbCycle();
console.log('> Finished.');
}catch(err){
console.log('> Failed.', err);
}finally{
// dbConnection.dbDisconnect();
console.log('Disconnected.');
}
})();
The .exists() method is returning a promise, something I have overlooked. Waiting for the promise to be resolved after the query is sent fixed the problem. Because I was not waiting for the promise and closing the server for cases where there was nothing new to be added to the db, the orphaned promises were causing the unhandled exception. Something like the code below, solved the problem:
async checkStates(stream, model){
let state;
let states = [];
for(const item of stream){
const date = new Date(millisecondsToTimestring(item[0]));
const ISODate = date.toISOString();
state = await model.exists({date: ISODate});
states.push(state);
// console.log(item, date, state);
}
return states;
}

How to use multi query with nodejs express mongodb

How to use multi query like find , update , insert , delete in one service on mongodb
I can query this below
router.get('/userlist', function(req, res) {
  User.find({},function (err, docs) {
    res.json(docs);
  });
});
but i want to query like this is an error
router.get('/userlist', function(req, res) {
  var data = array();
  User.find({},function (err, docs) {
     data['data1'] = docs
  })
  Content.find({},function (err, docs) {
    data['data2']  = docs
  })
  res.json(docs)
});
Can anyone help me or It is possible to use query like this?
Thank you
You can use async await to run multiple mongo queries inside a function like below:
router.get('/userlist', async function(req, res) {
var data = array();
try{
//first query
let user_data = await User.find({}).exec();
data['data1'] = user_data;
//second query
let content_data = await Content.find({}).exec();
data['data2'] = content_data;
return res.status(200).json(data)
}
catch(err){
return res.status(400).json({err})
}
});
My question is what is stopping you? Yes, you can do this. I did those kinds of works.
There remain two points.
If you need to perform multiple queries, based on the response of the previous query, you can use async/await or promises. If you don't wanna try those, there is still a way. You can check the response of the previous query, make conditions, and if right, execute the seconde one. And thus you can make a chain of queries.
And if you don't need to rely on the responses, do whatever you want. There is nothing wrong...
Happy Coding!
You have to wait for your database call response. So, You can use promise for this. MDN: Promise Make promise of database call and resolve that after you got data from database. Promise.all will wait until your both the promises resolved. MDN: Promise.all()
router.get('/userlist', function(req, res) {
var data = [];
let promise1 = new Promise((resolve, reject) => {
User.find({},function (err, docs) {
data['data1'] = docs;
resolve();
})
});
let promise2 = new Promise((resolve, reject) => {
Content.find({},function (err, docs) {
data['data2'] = docs
})
});
Promise.all([promise1, promise2]).then(result => res.json(docs))
});

Redis hGet method goes to deadlock on second call when called asynchronously

The issue is getMasterData when called never prints 'Passed the worst barrier' :(. God knows what I'm doing wrong. This is what I actually want to achieve.
What am I doing wrong here?
Please let me know. Any help will really be appreciated.
Below is my implementation of node redis implementation:
If I run the same this.getAll() in the loop without any await it works perfectly.
For second time await it never returns back from this.cacheStore.getByKey('organizations') and goes in deadlock forever.
No error and no response either.
Also if I remove data2 await. Still, it works fine.
async setKey(key, data) {
try {
const flatteredData = JSON.stringify(data);
return this.cacheStore.hmset(key, key, flatteredData);
} catch (error) {
return error;
}
}
getByKey(key) { // eslint-disable-line consistent-return
return new Promise((resolve, reject) => {
this.cacheStore.hget(key, key, (err, res) => {
if (err) {
reject(err);
} else {
resolve(JSON.parse(res));
}
});
});
}
async getAll() {
const cache = await this.cacheStore.getByKey('organizations');
if (cache) return cache;
const organizations = await this.db
.collection('organizations')
.find({})
.toArray();
await this.cacheStore.setKey('organizations', organizations);
return organizations;
}
async getMasterData(){
const data1 = await this.getAll();
const data2 = await this.getAll();
console.log('Passed the worst barrier');
}
You declare setKey() as an async function but when you call it in getAll() you forgot to await for it.
Either remove the async or call it with await. I am not sure this will resolve your error but try it out!

Combine file API with transactions for file saving or deleting

I am using node 8.11.1 with pg-promise 8.4.4 to handle queries and transactions in PostgreSQL. This is about node, but I guess is the same logic in other servers/tools too.
The scenario is common. I want to save an image file in a folder, then if this is successful, insert its details in the database, get the returned id and then do another insert in a secondary, many-to-many table.
Clearly, I need a transaction for the insert queries. But what about the actual file saving? My approach is
fs.rename(oldpath, newpath, (err) => {
if (err){throw new Error ;}
db.tx('my-transaction', t => {
return t.one('INSERT INTO images(whatever) VALUES($1) RETURNING id', ['whatever'])
.then(user => {
return t.batch([
t.none('INSERT INTO mtm(userId, name) VALUES($1, $2)', [user.id, 'created'])
]);
});
})
.then(data => {
// success
})
.catch(error => {
// error
});
}); //fs rename
Ok, if there is no error while saving the image file with fs.rename, then proceed with the transaction.
If there is an error while saving the image, nothing will execute, so all good.
But the problem is, what if the image is saved and there is an error in the transaction? I will end up with an image saved and nothing in the database. Sure, user will get an error and will have to re-upload, but I still have images in my server that are not related to anything. I would like to avoid this.
The solution would be to incorporate the image saving in the transaction, so if anything fails, nothing is completed. How can I do this? I dont know if the file API can be inside a query-related transaction. I dont even know if I am in the right mindset here.
Please advice or help me code this.
Thank you
Simply remove the file, if transaction fails (or you can rename it back, if you prefer):
const fs = require('fs-extra');
async function saveAll(oldpath, newpath) {
await fs.rename(oldpath, newpath);
try {
return await db.tx('my-transaction', async t => {
const imageId = await t.one('INSERT INTO images(whatever) VALUES($1) RETURNING id', ['whatever'], a => a.id);
await t.none('INSERT INTO mtm(userId, name) VALUES($1, $2)', [imageId, 'created']);
return imageId;
});
} catch (e) {
await fs.unlink(newpath); // deleting the file
throw e;
}
}
Function saveAll will return the new imageId, if successful, or throw an error, if anything fails:
async test() {
try {
const imageId = await saveAll('old-path', 'new-path');
// we are all good
} catch(e) {
// something failed, as per the error details
}
}

Resources