Executing different batch inserting into one transaction in Nodejs - mysql

I need to execute two different inserts for two different collections, one depends on the other one, so I need to execute the first one and then being able to execute the second one. My problem is I need to be able to roll back all operations if some error happens so I need to roll back both inserts if an error occurs.
I'm trying to do something like this with my two batch calls inside of the transactions but it doesn't work.
conn.beginTransaction()
.then(() => {
conn.query("INSERT INTO testTransaction values ('test')");
return conn.query("INSERT INTO testTransaction values ('test2')");
//instead of this two query I have my batch inserts
})
.then(() => {
conn.commit();
})
.catch((err) => {
conn.rollback();
})
Any example of how to do it?

Just if someone has the same situation and what to know how I figure this out, here is a working code:
return connectionMaria.beginTransaction()
.then(() => {
return conn.batch(insert1, values1)
.then(() => {
return conn.batch(insert2, values2)
})
.then(() => {
connectionMaria.commit();
})
.catch((error) => {
connectionMaria.rollback();
throw error;
});

If you have lots of promises you need to wait on, you can use Promise.all:
conn.beginTransaction()
.then(() => {
return Promise.all([
conn.query("INSERT INTO testTransaction values ('test')"),
conn.query("INSERT INTO testTransaction values ('test2')")
])
})
.then(() => {
conn.commit();
})
.catch((err) => {
conn.rollback();
})

Related

update query in promise not running

please help, the update query isn't running in my promise.
It just skips to the last statement "done with six". The row that it's meant to update in the database table doesn't get updated. how can I make the update query run successfully?
1. crud statements(select+insert statements) that I've placed above the update statement
code would be here
2. update statement that does not seem to run
var insertcctblintblbotagents = await new Promise((resolve, reject) => {
var sql='UPDATE db.tblagentstoadd SET db.tblagentstoadd.ccAgentID =? WHERE
db.agentstoadd.AgentID=? ;';
DB.query(sql,[ctx.session.AgentID, ctx.session.tempAgentID],function(err,result){
if (err){
return reject(err);
};
return resolve(result);
})
})
3. the promise statement (allows the crud statements to run synchronously because the statements are dependent on one another)
await insertbotagentstoadd
.then(() => {
console.log("done with one");
})
.then(() => selectbotagentstoadd)
.then((results) => {
AgenttoaddIDStore = [];
results.forEach((agent) => {
AgenttoaddIDStore.push({
AgentID: agent.AgentID,
});
ctx.session.tempAgentID = agent.AgentID;
});
return AgenttoaddIDStore;
})
.then((res) => {
console.log("agent ID: "+ctx.session.tempAgentID);
console.log("done with two");
return res;
})
.then((results) => insertcctblricaagents)
.then((res) => {
console.log("done with three");
return res;
})
.then((results) => selectcctblricaagents)
.then((res) => {
console.log("done with four");
return res;
})
.then((res)=>selectcctblricaagentsnum)
.then((result)=>{
AgentNewIDStore=[];
result.forEach((agent)=>{
AgentNewIDStore.push({
AgentID:agent.AgentID,
MainNumber:agent.MainNumber,
});
ctx.session.AgentID=agent.AgentID;
ctx.session.agentnumber=agent.MainNumber;
});
return AgentNewIDStore;
})
.then((res)=>{
console.log("cctblricaagentsnum agent ID: "+ ctx.session.AgentID);
console.log("done with five");
return res;
})
.then((result)=>insertcctblintblbotagents) //Doesn't run this area of code
.then((res)=>{
console.log("done with six");
return res;
});
4.results displayed in the terminal or console
done with one
agent ID: 151
done with two
done with three
done with four
cctblricaagentsnum agent ID: 96661
done with five
done with six
It does run the query, but it runs before you intend it to... You execute the query when you define the promise, not when you "use" it. The code is looking weird so I won't redo everything, but I suggest you use awaits instead of a chain of then(), it will make things more readable. If you inline the promise you defined, things will work:
.then((result)=>insertcctblintblbotagents) //Doesn't run this area of code
To
.then((result)=>{
return new Promise((resolve, reject) => {
var sql='UPDATE db.tblagentstoadd SET db.tblagentstoadd.ccAgentID =? WHERE
db.agentstoadd.AgentID=? ;';
DB.query(sql,[ctx.session.AgentID, ctx.session.tempAgentID],function(err,result){
if (err){
return reject(err);
};
return resolve(result);
})
})

How to delete both element and the associated elements in join table with express.js and react.js

I have a project where I have two main tables: Contacts and Workers
I also have a join table called WorkerContacts
In my project, I give the user the option of deleting contacts, something that would also require deleting elements of the join table. My concern is that with my current setup (seen below), if I run into an error where I successfully delete a contact, but then fail to delete the associated join tables (resulting from an error), that would throw off everything. So my question is, is there a way to refactor this so that it ensures that both have been completed before doing the actual deletions then sending the promise to the front end?
Here's my current situation:
Frontend:
export const destroyContact = (contact_id) => dispatch => {
axios.post(`http://localhost:3001/contacts/destroy`, {id: contact_id})
.then(() => {
dispatch({type: 'CONTACT_DESTROYED', payload: contact_id});
axios.post(`http://localhost:3001/workerContacts/destroy`, {id: contact_id}) //I'm scared that the first thing will run but the second one won't, causing a lot of problems. We can deal with this by just throwing a big error message for the user hopefully
.then(() => {
dispatch({type: 'JOIN_TABLE_ROWS_DESTROYED', payload: contact_id});
})
.catch(err => dispatch({type: 'ERROR_CAUGHT', payload: {err_message: err.response.data.message, err_code: err.response.request.status, err_value: err.response.request.statusText}}))
})
.catch(err => dispatch({type: 'ERROR_CAUGHT', payload: {err_message: err.response.data.message, err_code: err.response.request.status, err_value: err.response.request.statusText}}))
}
I'm using redux as well so that's why I have all of the dispatch and whatnot, but essentially I've split the deletions into two axios calls: one where I delete the contact and one where I delete the join tables.
Backend:
For the contact I have this:
export const destroy = (req, res) => {
// Here is when we want to remove an existing contact
Contact.deleteMe(req.body.id)
.then(() => res.json("Contact deleted"))
.catch((err) => res.status(500).json({message: "Something went wrong when trying to save delete this. Try and reload the page and try again "}))
}
And the associated deleteMe function:
static deleteMe(customer_id){
//Uses SQL to delete an individual customer element
return db.execute('DELETE FROM contacts WHERE id = ?', [customer_id]);
}
For the jointable, I have this:
export const destroy = (req, res) => {
// Here is when we want to remove an existing contact
JoinTable.deleteMe(req.body.id)
.then(() => res.json("Join tables deleted"))
.catch(err => res.status(500).json({message: "Something went wrong on our end. Try to reload the page and start again"}))
}
And the associated deleteMe function:
static deleteMe(customer_id){
//Uses SQL to delete an individual customer element
return db.execute('DELETE FROM workercontacts WHERE workerContacts.contact_id = ?', [customer_id]);
}
I'm using a MySQL database if that helps.
Hopefully this is enough information, but if you require more, I can definitely provide you with it.
Just use a single call and execute the DELETE commands in a transaction:
export const destroyContact = (contact_id) => (dispatch) => {
axios
.post(`http://localhost:3001/contacts/destroy`, { id: contact_id })
.then(() => {
dispatch({ type: 'CONTACT_DESTROYED', payload: contact_id });
dispatch({ type: 'JOIN_TABLE_ROWS_DESTROYED', payload: contact_id });
})
.catch((err) =>
dispatch({
type: 'ERROR_CAUGHT',
payload: {
err_message: err.response.data.message,
err_code: err.response.request.status,
err_value: err.response.request.statusText,
},
})
);
};
One the backend:
static async function deleteMe(customer_id) {
await db.execute('START TRANSACTION');
try {
await db.execute('DELETE FROM contacts WHERE id = ?', [customer_id]);
await db.execute('DELETE FROM workercontacts WHERE workerContacts.contact_id = ?', [customer_id]);
await db.execute('COMMIT');
} catch (err) {
await db.execute('ROLLBACK');
}
}
...
export const destroy = (req, res) => {
// Here is when we want to remove an existing contact
Contact.deleteMe(req.body.id)
.then(() => res.json("Contact deleted"))
.catch((err) => res.status(500).json({message: "Something went wrong when trying to save delete this. Try and reload the page and try again "}))
}

Catch/Display errors in client side without console logs

When a promise is called in the client side. What is the best way to catch/display error without console.log()? Example as follows
.then(result => {
})
.catch(err => {
console.log(err);
});
Maybe you are looking for:
console.error = () => { throw new Error(FEATURES_ERROR); };
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error

One response after few async functions

I have a web page with a form where a user can edit personal info, education, work history and etc.
And the user can add more than one degree, for example: bs, ms, phd. And a few job positions as well.
When the user push 'save' button I send all this data to my server. I send it all in one request. In the server I have a point to handle the request.
app.post(config.version + '/profile', (req, res, next) => {});
And there I do a few MySQL queries to insert/update/delete a data. I use mysql package from npm to do that.
new Promise((resolve, reject) => {
const userQuery = `INSERT INTO user ...;`;
const degreesQuery = 'INSERT INTO degree ...;';
const positionsQuery = 'UPDATE position SET ...;';
this.connection.query(userQuery, err => {});
this.connection.query(degreesQuery, err => {});
this.connection.query(positionsQuery, err => {});
resolve({});
})
In the end I do resolve({}) but I want to select updated profile and send it back (because in MySQL tables for degrees I add ids that helps me to not insert again duplicate data). So, my question is how to do resolve({}) only when all my async this.connection.querys finished?
My suggestion is to run all the queries in a Promise.all().
Example:
const queries = [
`INSERT INTO user ...;`;,
'INSERT INTO degree ...;',
'UPDATE position SET ...;'
];
Promise.all(queries.map((query) => {
return new Promise((resolve, reject) => {
this.connection.query(query, err => {
return err ? reject(err) : resolve();
});
});
})
.then(() => {
// continue
// get your updated data here with and send it as response
})
If your db library has support for Promise write it this way
Promise.all(queries.map((query) => {
return this.connection.query(query);
})
.then(() => {
// continue
// get your updated data here with and send it as response
})

how do I force PouchDB to really delete records?

I am creating a PouchDb like so :
var db = new PouchDB('my_db',
{ auto_compaction: true, revs_limit: 1, adapter: 'websql' });
Then I create and delete a number of records :
db.put({ _id: '1'});
db.put({ _id: '2'});
db.put({ _id: '3'});
db.get('1')
.then(function(doc) {
db.remove(doc)
});
db.get('2')
.then(function(doc) {
db.remove(doc)
});
db.get('3')
.then(function(doc) {
db.remove(doc)
});
From my reading of the documentation, this is the correct way to delete and remove records.
And this SO question and answer seems to suggest also that this is the way to do things.
However, if I use the Chrome inspector to look at my Web SQL DB, the records are still there :
I don't believe this is not a timing issue or anything like that, as I can refresh with just the delete code and then get a 404 not_found error
My application creates and keeps records in a local pouchDb until they have been synced to central server, at which time I want to clear them from the local database.
I'm creating lots of records and if I cannot clear them out then eventually I'm going to run out of space on the device (it is hybrid HTML5 mobile app).
Is it even possible to actually remove records from a local PouchDB?
If so, how do I do it?
If not, what is a good solution that I can easily swap in place of PouchDB?
(I'm really hoping it is possible because I've gone down this path of development, so if the answer to the first question is No, then I need a good answer to the third question)
As mentioned in the comments above, this is not yet possible but is being worked on (source 1 source 2). However, there is a work around which you might be able to use.
The workaround is to replicate the database locally to another PouchDB database and once the replication is complete, delete the original database. Deleted documents won't be replicated (source)
Here is a working demo:
(() => {
// DECLARATION
const dbName = 'testdb';
const tmpDBName = 'tmpdb';
const deleteFilter = (doc, req) => !doc._deleted;
const doc1 = { _id: 'd1' };
const doc2 = { _id: 'd2' };
// CREATION
// create database
const maindb = new PouchDB(dbName);
// insert two documents
maindb.post(doc1)
.then(() => maindb.post(doc2))
// query for one document
.then(() => maindb.get(doc1._id))
// delete this document
.then((doc) => { console.log(doc); return maindb.remove(doc) })
// query for the same document
.then(() => maindb.get(doc1._id))
.catch((err) => { console.log(err) });
// CLEANUP
// delete a database with tmpdb name
new PouchDB(tmpDBName).destroy()
// create a database with tmpdb name
.then(() => Promise.resolve(new PouchDB(tmpDBName)))
// replicate original database to tmpdb with filter
.then((tmpDB) => new Promise((resolve, reject) => {
maindb.replicate.to(tmpDB, { filter: deleteFilter })
.on('complete', () => { resolve(tmpDB) })
.on('denied', reject)
.on('error', reject)
}))
// destroy the original db
.then((tmpDB) => {
console.log(tmpDB.name);
return maindb.destroy().then(() => Promise.resolve(tmpDB))
})
// create the original db
.then((tmpDB) => new Promise((resolve, reject) => {
console.log(tmpDB.name);
try {
resolve({ db: new PouchDB(dbName), tmpDB: tmpDB })
} catch (e) {
reject(e)
}
}))
// replicate the tmpdb to original db
.then(({db, tmpDB}) => new Promise((resolve, reject) => {
tmpDB.replicate.to(db)
.on('complete', () => { resolve(tmpDB) })
.on('denied', reject)
.on('error', reject)
}))
// destroy the tmpdb
.then((tmpDB) => tmpDB.destroy())
.then(() => { console.log('Cleanup complete') })
.catch((err) => { console.log(err) });
})()
If you check the state of the database after executing this code, it'll contain only one document. Note that at times, I had to refresh the browser to be able to see the latest state of the database (a right click + Refresh IndexedDB wasn't enough).
If you want to cleanup the database while testing this, you can use this snippet:
['testdb', 'tmpdb'].forEach((d) => { new PouchDB(d).destroy() })