Cannot modify a WriteBatch that has been committed in cloud functions - node.js

I am deploying this code on cloud functions, getting Cannot modify a WriteBatch that has been committed, I have tried committing after getting every collection but it's not a right way and is inconsistent, could not spot the error after trying several hours. Also the code run on first time after cold start , this post has same problem Batch write to firebase cloud firestore , where to create
a new batch for each set of writes.
in this code .
var batch = db.batch();
db.collection("myposts")
.doc(post_id)
.collection("fun")
.get()
.then(snapshot => {
return snapshot.forEach(doc => {
batch.delete(doc.ref);
});
})
.then(
db
.collection("relations_new")
.where("uid", "==", uid)
.get()
.then(snapshot => {
return snapshot.forEach(doc => {
batch.delete(doc.ref);
});
})
)
.then(
db
.collection("relation")
.where("uid", "==", uid)
.get()
.then(snapshot => {
return snapshot.forEach(doc => {
batch.delete(doc.ref);
});
})
.catch(err => {
console.log(err);
})
)
.then(
db
.collection("posts")
.doc(post_id)
.get()
.then(snap => {
return batch.delete(snap.ref);
})
.then(batch.commit())
)
.catch(err => {
console.log(err);
});`

Make sure to return promises from your then functions, and to ultimately return a promise from your cloud function. ESLint is a great tool for catching these kinds of errors.
let batch = db.batch();
return db.collection("myposts").doc(post_id)
.collection("fun").get()
.then(snapshot => {
snapshot.forEach(doc => {
batch.delete(doc.ref);
});
return null;
})
.then(() => {
return db.collection("relations_new")
.where("uid", "==", uid)
.get();
})
.then(snapshot => {
snapshot.forEach(doc => {
batch.delete(doc.ref);
});
return null;
})
.then(() => {
return db.collection("relation")
.where("uid", "==", uid)
.get();
})
.then(snapshot => {
snapshot.forEach(doc => {
batch.delete(doc.ref);
});
return null;
})
.then(() => {
return db.collection("posts").doc(post_id).get();
})
.then(snap => {
batch.delete(snap.ref);
return null;
})
.then(() => {
return batch.commit();
})
.then(() => {
console.log("Success");
return null;
})
.catch(err => {
console.log(err);
});

Related

How to escape from .then() without executing the next statement in Express Js Promise?

By using mysql2 with promise, I have the controller.js as below :
exports.company_add = (req, res) => {
company_model.company_add(admin_email, admin_info).then((result) => { ... })
.catch((err) => { ... })
}
And the model.js as below :
exports.company_add = (admin_email, admin_info) => {
return new Promise((resolve, reject) => {
connectionPool.getConnection((connectionError, connection) => {
if (connectionError) reject(connectionError);
return connection.promise().query('SELECT * FROM admin WHERE admin_email = ?', [admin_email])
.then(([rows, field]) => {
if (rows.length) reject('Email exist');
else return connection.promise().query('INSERT INTO companydb.admin SET ?', [admin_info])
})
.then((result) => {
console.log('result')
if (result[0].affectedRows === 1) resolve(result);
else reject('INSERT FAIL');
})
.catch((err) => {
reject(err);
})
.finally(() => {
connection.release();
})
});
});
};
I wonder how I escape from the .then() statement when the line if (rows.length) occurred, since there is no need to continue with the next .then() for this case if the email already exist in the database. Do I need to throw it and add a .catch() just before the next .then() executed?
One cannot really break out of a then chain other than by nesting or throwing an exception. Yes, in this case you probably should throw, but you don't need to .catch it when you avoid the Promise constructor antipattern and just make it a chained promise:
exports.company_add = (admin_email, admin_info) => {
return connectionPool.getConnection().then(connection => {
return connection.promise().query('SELECT * FROM admin WHERE admin_email = ?', [admin_email])
.then(([rows, field]) => {
if (rows.length)
throw new Error('Email exist');
else
return connection.promise().query('INSERT INTO companydb.admin SET ?', [admin_info])
})
.then((result) => {
console.log('result')
if (result[0].affectedRows !== 1)
throw new Error('INSERT FAIL');
return result;
})
.finally(() => {
connection.release();
})
}, err => {
console.error(err);
throw new Error('CONNECTION FAIL');
});
};

Firestore Update Document field and get immidiate response issue getting old data

I am trying to doing update firestore field value. It is updating, but I want immediately to use updated document. When I am getting those data that time, it will giving me old data from Firestore first time i hit the api. If I hit the same api twice, then I am getting updated data.
So, i don't understand what is the actual problem
updateProductDetail: async(req, res)=>{
console.log("reqeuest", req.body.creator);
try {
var productFilterArray = [];
var counter = 0;
let collectionRef = db.collection("product_details_temp");
let query = collectionRef.where('creator', '==', req.body.creator).get()
.then(snapshot => {
if (snapshot.empty) {
console.log('No matching documents.');
return;
} else {
snapshot.forEach(doc => {
db.collection("product_details_temp").doc(doc.id).update({ "product": req.body.product });
});
collectionRef.where('creator', '==', req.body.creator).get()
.then(snapshot => {
let a =[];
snapshot.forEach(doc => {
// a = doc.data();
a.push(doc.data());
});
res.send(functions.responseGenerator(200, "successfull", a));
})
}
})
} catch (error) {
res.send(
functions.responseGenerator(error.code, error.message, error.data)
);
}
Please help me
It sounds like you have two operations:
A write operation
A query/read operation that must see the result of the write operation
Code that reads data from or writes data to Firestore runs asynchronously. And to prevent blocking the app, your normal code flow is allowed to continue while the read/write operations runs in the background. That means that the query/read currently runs in your code, the write operation hasn't completed yet, and hence you don't see the result of that write operation in your query.
The solution is to wait until the write operation has completed before starting the query. You already do this with the query, where you use the .then(snapshot => { construct to wait for the results. You'll need to do the same with the update(...) calls, with something like:
let collectionRef = db.collection("product_details_temp");
let query = collectionRef.where('creator', '==', req.body.creator).get()
.then(snapshot => {
if (snapshot.empty) {
console.log('No matching documents.');
return;
} else {
let promises = [];
snapshot.forEach(doc => {
promises.push(db.collection("product_details_temp").doc(doc.id).update({ "product": req.body.product }));
});
Promise.all(promises).then(() => {
collectionRef.where('creator', '==', req.body.creator).get()
.then(snapshot => {
let a =[];
snapshot.forEach(doc => {
a.push(doc.data());
});
res.send(functions.responseGenerator(200, "successfull", a));
})
})
}
The main changes here:
This code captures the result of the update calls in a promises array. This gives us a list of promises, which resolve when the write operation completes.
It then uses Promise.all() to wait for all write operations to complete before starting the query.
I also have the same issue, I tried the code in this way :
updateProductDetail: async (req, res)=>{
try {
db.collection("product_details_temp")
.where('creator', '==', req.body.creator).get()
.then(snapshot => {
snapshot.forEach(doc => {
db.collection("product_details_temp").doc(doc.id).update({
"product": req.body.product });
});
controller.getAPI(req,res);
});
} catch (error) {
res.send(
functions.responseGenerator(error.code, error.message, error.data)
);
}
},
getAPI: async (req, res) =>{
try{
db.collection("product_details_temp").where('creator', '==',
req.body.creator).get()
.then(snapshot => {
var A = [];
snapshot.forEach(doc => {
A.push(doc.data());
});
res.send(functions.responseGenerator(200, "successfull", A));
}) ;
} catch (error) {
res.send(
functions.responseGenerator(error.code, error.message, error.data)
);
}
}
};
I got the solution for this issue by using the setTimeout() function:
setTimeout(() => {
collectionRef.where('creator', '==', req.body.creator).get()
.then(snapshot => {
let a = [];
snapshot.forEach(doc => {
a.push(doc.data());
});
res.send(functions.responseGenerator(200, "successfull", a));
})
}, 2000);
Use this with code like:
updateProductDetail: async(req, res)=>{
try {
let collectionRef = db.collection("product_details_temp");
let query = collectionRef.where('creator', '==', req.body.creator).get()
.then(snapshot => {
if (snapshot.empty) {
console.log('No matching documents.');
return;
} else {
snapshot.forEach(doc => {
db.collection("product_details_temp").doc(doc.id).update({
"product": req.body.product });
});
}
setTimeout(() => {
collectionRef.where('creator', '==', req.body.creator).get()
.then(snapshot => {
let a = [];
snapshot.forEach(doc => {
a.push(doc.data());
});
res.send(functions.responseGenerator(200, "successfull", a));
})
}, 2000);
})
} catch (error) {
res.send(
functions.responseGenerator(error.code, error.message, error.data)
);
}

"n" number of API calls in the sequentially order in the Node.js?

How to call "n" number of API calls in the sequentially order(each API response as input parameter for next API call) in the Node.js?
Please, review below example of promise in which you may find that it will execute synchronously as your requirement.
let firstPromise = (() => {
return new Promise((resolve) => {
resolve("first promise");
});
});
let secondPromise = (() => {
return new Promise((resolve) => {
resolve("second promise");
});
});
let thirdPromise = (() => {
return new Promise((resolve) => {
resolve("third promise");
});
});
let fourthPromise = (() => {
return new Promise((resolve) => {
resolve("fourth promise");
});
});
firstPromise()
.then((res) => {
console.log(res);
return secondPromise();
})
.then((res) => {
console.log(res);
return thirdPromise();
})
.then((res) => {
console.log(res);
return fourthPromise();
})
.then((res) => {
console.log(res);
})
.catch((err)=>{
throw new Error(err);
});

amqplib nodejs to consume rabbitmq

this is how i consume the queue in rabbit, i wonder why this promise still hangs and return no result. i am trying to hit an endpoint that call this promise and expect it to return just ok to make sure it gives me a 200 OK status. now it acts like it somehow does not get any return from this service, i wonder what i did wrong. thanks
return new Promise((resolve,reject) => {
try{
rabbit.then(conn => {
return conn.createChannel();
})
.then(ch => {
let shit = ch.assertQueue(USER_LOG_QUEUE)
.then(ok => {
if(ok.messageCount > 0){
return ch.consume(USER_LOG_QUEUE, function(msg) {
if (msg !== null) {
ch.ack(msg);
writeToFile(msg.content.toString())
.then(rs => {
console.log(rs);
})
.catch(err => reject(err));
}
console.log('finished');
ch.close();
resolve(ok);
})
}else{
console.log('empty list : finished');
ch.close();
resolve(ok);
}
})
.then(ok => {
console.log('empty list2 : finished');
resolve(ok)
})
.catch(err => reject(err));
}catch(err){
reject(err);
}
});

Promise not returned warning in Bluebird

I am getting an error that seems to suggest I'm not returning some of my statements, but I think I'm doing everything correctly. Here's the warning:
Warning: a promise was created in a handler at /src/api/podcasts.js:51:18 but was not returned from it
This is the code of the function in question:
'findPodcastById': (db, podcastId, callback) => {
var queryString = "SELECT * FROM podcasts WHERE id=$1;";
db.one(queryString, [podcastId])
.then((result) => {
return callback(null, result);
})
.catch((err) => {
return callback(err, null);
});
},
And the parent function that it's called from:
app.post('/getepisodes', (req, res, next) => {
var podcastId = req.body.podcastId;
var userId = req.body.userId;
var podcast;
podcasts.findPodcastByIdAsync(db, podcastId)
.then((result) => {
podcast = result;
return request(podcast.rss);
})
.then((result) => {
return podcastParser.parseAsync(result, {})
})
.then((result) => {
return Promise.resolve(result.channel.items);
})
.map((item) => {
var date = new Date(item.pubDate).toLocaleString();
return podcasts.addEpisodeAsync(db, podcast.id, item.title, item.enclosure.url.split('?')[0], striptags(item.description), date, item.duration);
})
.map((episode) => {
return posts.addPostAsync(db, 'podcast', episode.id, episode.title, episode.description);
})
.then(() => {
return podcasts.findEpisodesByPodcastIdAsync(db, podcastId, userId);
})
.then((result) => {
return res.json(result);
})
.catch((err) => {
next(err);
});
});
I have a return statement in each promise block, so I'm not sure what I'm doing wrong, I would really appreciate some help!
findPostCastBy id is not returning the promise, try this
'findPodcastById': (db, podcastId) => {
return db.one("SELECT * FROM podcasts WHERE id=$1;", [podcastId])
}

Resources