How to repeat SQL insertion until successful with pg-promise? - sql

In my program I insert some data into a table and get back it's id and I need to ensure I enter that id into another table with a unique randomly generated string. But, in case the insertion fails for attempting to insert an already-existing random string, how could I repeat the insertion until it is successful?
I'm using pg-promise to talk to postgreSQL. I can run program like this that inserts the data into both tables given the random string doesn't already exists:
db.none(
`
WITH insert_post AS
(
INSERT INTO table_one(text) VALUES('abcd123')
RETURNING id
)
INSERT INTO table_two(id, randstr)
VALUES((SELECT id FROM insert_post), '${randStrFn()}')
`
)
.then(() => console.log("Success"))
.catch(err => console.log(err));
I'm unsure if there is any easy SQL/JS/pg-promise based solution that I could make use of.

I would encourage the author of the question to seek a pure-SQL solution to his problem, as in terms of performance it would be significantly more efficient than anything else.
But since the question was about how to re-run queries with pg-promise, I will provide an example, in addition to one already published, except without acquiring and releasing the connection for every attempt, plus proper data integrity.
db.tx(t => {
// BEGIN;
return t.one('INSERT INTO table_one(text) VALUES($1) RETURNING id', 'abcd123', a => +a.id)
.then(id => {
var f = attempts => t.none('INSERT INTO table_two(id, randstr) VALUES($1, randStrFn())', id)
.catch(error => {
if (--attempts) {
return f(attempts); // try again
}
throw error; // give up
});
return f(3); // try up to 3 times
});
})
.then(data => {
// COMMIT;
// success, data = null
})
.catch(error => {
// ROLLBACK;
});
Since you are trying to re-run a dependent query, you should not let the first query remain successful, if all your attempts with the second query fail, you should roll all the changes back, i.e. use a transaction - method tx, as shown in the code.
This is why we split your WITH query inside the transaction, to ensure such an integrity.
UPDATE
Below is a better version of it though. Because errors inside the transaction need to be isolated, in order to avoid breaking the transaction stack, each attempt should be inside its own SAVEPOINT, which means using another transaction level:
db.tx(t => {
// BEGIN;
return t.one('INSERT INTO table_one(name) VALUES($1) RETURNING id', 'abcd123', a => +a.id)
.then(id => {
var f = attempts => t.tx(sp => {
// SAVEPOINT level_1;
return sp.none('INSERT INTO table_two(id, randstr) VALUES($1, randStrFn())', id);
})
.catch(error => {
// ROLLBACK TO SAVEPOINT level_1;
if (--attempts) {
return f(attempts); // try again
}
throw error; // give up
});
return f(3); // try up to 3 times
});
})
.then(data => {
// 1) RELEASE SAVEPOINT level_1;
// 2) COMMIT;
})
.catch(error => {
// ROLLBACK;
});
I would also suggest using pg-monitor, so you can see and understand what is happening underneath, and what queries are being in fact executed.
P.S. I'm the author of pg-promise.

The easiest way is to put it into a method then re-call that in the catch:
const insertPost = (post, numRetries) => {
return
db.none(
`
WITH insert_post AS
(
INSERT INTO table_one(text) VALUES('abcd123')
RETURNING id
)
INSERT INTO table_two(id, randstr)
VALUES((SELECT id FROM insert_post), '${randStrFn()}')
`
)
.then(() => console.log("Success"))
.catch(err => {
console.log(err)
if (numRetries < 3) {
return self.insertPost(post, numRetries + 1);
}
throw err;
});
}

Related

Best Practice for writing nested Express.js Queries?

I'm new to using Express.js. I'm working on my first endpoint which is to create a user. To accomplish this, it first has to be checked whether the username or email address already exists. After some research on how to do this, here's the code I've come up with:
// Check whether the user_name or email already exists
pool.query('SELECT CASE WHEN EXISTS (SELECT * FROM users WHERE user_name = $1 OR email = $2) THEN CAST(1 AS BIT) ELSE CAST(0 AS BIT) END', [values.user_name, values.email], (error, results) => {
if (error) {
throw error
}
if (results.rows[0].case === '1') {
console.log('User exists so send message to this effect back to client');
} else {
console.log('User does not exist so we can call INSERT query');
pool.query('INSERT INTO users (first_name, last_name, user_name, email, password, last_password, password_salt) VALUES ($1, $2, $3, $4, $5, $6, $7) RETURNING *', [values.first_name, values.last_name, values.user_name, values.email, values.password, values.password, 'tmp_salt'], (error, results) => {
if (error) {
console.error('Something went wrong');
throw error;
}
console.log('Results from INSERT:', results);
res.status(201).send(`User added with user_id: ${results.rows[0].user_id}`);
});
}
});
It's obviously not finished yet but I'm curious if this nested approach I've used is the best way to do it? In other words, first I'm checking for the existence of user_name and/or email and only if both don't exist am I performing the INSERT.
What do you think?
There are really two different questions there:
Is checking then inserting the right approach?
Is nesting the right approach?
Is checking then inserting the right approach?
Not usually, no, it leaves you open to a race condition:
Client A sends joe#example.com / funkypassword
Client B sends joe#example.com / somethingelse
The main thread picks up the task for Client A's request and starts the asynchronous check to see if the user exists
While waiting for that asynchronous result, the main thread picks up Client B's request and starts the asynchronous check to see if the user exists
Both checks come back clear (no user exists)
The main thread inserts one of those sets of details (Client A's or Client B's)
The main thread tries to insert the other one of those sets of details (Client B's or Client A's)
At this point, if the database is set up correctly, you'll get an insertion error (primary or unique key violation).
Instead, you ensure the DB is set up that way and expect to get an error if the user already exists, and don't do the check at all. Just do the insert, and look at the error if you get one to see if it's a primary or unique key constraint violation.
Is nesting the right approach?
This particular task may not need multiple DB operations (see above), but many others will. So is nesting the right way to handle that?
It's certainly been the main way to handle it for a long time, but it has the issue of quickly becoming "callback hell" where you have lots of nested operations, each in its own callback to a previous operation, etc., etc. It can get very hard to manage. But you can do it that way if you like, and many have done for some time.
The more modern alternative is to use promises and async/await. In an async function, you can make the function wait for an asynchronous process to complete before continuing with its logic. That way, your logic isn't buried in callbacks.
Suppose you have to do two or three database operations, where whether it's two or three depends on the first operation's result, and where information from earlier calls is needed by later ones. With nesting, you might do something like this:
pool.query("DO THE FIRST THING", (error1, results1) => {
if (error1) {
/*...handle/report error...*/
return;
}
const thirdThing = (results2) => {
pool.query("DO THE THIRD THING with results1 and results2 (which may be null)", (error3, results3) => {
if (error3) {
/*...handle/report error...*/
return;
}
/*...finish your work using `results1`, `results2`, and `results3`...*/
});
};
if (/*results1 says we should do the second thing*/) {
pool.query("DO THE SECOND THING with results1", (error2, results2) => {
if (error2) {
/*...handle/report error...*/
return;
}
thirdThing(results2);
});
} else {
thirdThing(null);
}
});
You might isolate the three operations as functions, but while that's good for reuse if it's relevant and possibly for debugging, it doesn't help much with the callback hell:
function firstThing(callback) {
pool.query("DO THE FIRST THING", callback);
}
function secondThing(paramA, callback) {
pool.query("DO THE SECOND THING with paramA", callback);
}
function thirdThing(paramA, paramB, callback) {
pool.query("DO THE THIRD THING with paramA and paramB (which may be null)", callback);
}
// ...and then where your code was:
const done = (error, results1, results2, results3) => {
if (error) {
/*...handle/report error...*/
} else {
/*...do your final work here using `results1`, `results2` (which may be `null`), and `results3`...*/
}
});
firstThing((error1, results1) => {
if (error1) {
done(error1);
} else if (/*results1 says we should do the second thing*/) {
secondThing(results1, (error2, results2) => {
if (error2) {
done(error2);
} else {
thirdThing(results1, results2, (error3, results3) => {
done(error3, results1, results2, results3);
});
}
});
} else {
thirdThing(results1, null, (error3, results3) => {
done(error3, results1, null, results3);
});
}
});
But suppose we had a poolQuery function that put a promise wrapper around pool.query. Here's how that could look:
async function firstThing() {
return await poolQuery("DO THE FIRST THING");
}
async function secondThing(paramA) {
return await poolQuery("DO THE SECOND THING with paramA");
}
async function thirdThing(paramA, paramB) {
return await poolQuery("DO THE THIRD THING with paramA and paramB (which may be null)");
}
// ...and then where your code was, making it an `async` function:
try {
const results1 = await firstThing();
const results2 = (/*results1 says we should do the second thing*/)
? await secondThing(results2)
: null;
const results3 = await thirdThing(results1, results2);
/*...do your final work here using `results1`, `results2` (which may be `null`), and `results3`...*/
} catch (error) {
/*...handle/report error...*/
}
Or if you aren't going to reuse those queries, then:
try {
const results1 = await poolQuery("DO THE FIRST THING");
const results2 = (/*results1 says we should do the second thing*/)
? await poolQuery("DO THE SECOND THING with results1")
: null;
const results3 = await poolQuery("DO THE THIRD THING with results1 and results2 (which may be null)");
/*...do your final work here using `results1`, `results2` (which may be `null`), and `results3`...*/
} catch (error) {
/*...handle/report error...*/
}
How simple and clear is that? :-) It gets even more powerful when you have loops and such involved.

Issue with loading sqlite entries in reverse in expo sqlite

I am working on a feature in an app where users can log journal entries, and I need to implement lazy loading. However, I need to load the entires in reverse, since I need to display the most recent entries first. I am not able to do this, even with the COUNT(*) aggregate function because I do not want to use GROUP BY. Here is my code:
export const lazyLoadEntriesByGoalId = (goalId, amountLastLoaded) => {
//load journal entries
const transactionPromise = new Promise((resolve, reject) => {
database.transaction((tx) => {
tx.executeSql(
`SELECT * FROM journal
WHERE goalId = ?
AND id < ?
ORDER BY id DESC
LIMIT 5;`,
[goalId, amountLastLoaded],
(_, result) => {
resolve(result.rows._array);
},
(_, err) => {
reject(err);
}
);
});
});
return transactionPromise;
};
The amountLastLoaded is for seeing how many entries are already loaded. I am considering using "COUNT(*) - ?", but expo-sqlite throws an error if I do that. What can I do?

Sqlite very slow on for loop delete

I have this local db that I'm playing with and it pulls a list of users, does something with each and then deletes the records. The delete is VERY slow:
db.all("select id, username from users", (err, rows) => {
rows.forEach((row) => {
// do stuff with row
db.run("delete from users where id = ?", row.id, (err) => {
if (err) {
throw err;
}
});
});
});
It is a simple db: CREATE TABLE IF NOT EXISTS users(id INTEGER PRIMARY KEY, username text NOT NULL)
Deleting a record takes even 20 seconds on a list of 100k records. What am I doing wrong here and how can I speed this up?
Deleting a record takes even 20 seconds on a list of 100k records. What am I doing wrong here and how can I speed this up?
db.all will fetch all the rows at once. This is slow, consumes a lot of memory, and all rows must be fetched before any processing starts.
Instead, use db.each. This will fetch a row and act on it immediately.
There's also no need to use where in (?). For a single value use where = ?. This may or may not affect performance.
db.each(
"select id, username from users", (err, row) => {
// do stuff with row
db.run("delete from users where id = ?", row.id, (err) => {
if (err) {
throw err;
}
}
}
)

Fetching data as reaction to observable array change in MobX

Suppose we have an observable main object array, and observable data about that array (e.g. suppose we have selectedReports and reportParameters) . Now suppose we emit action to either add report to the array or remove report from that array. How do we run an action to fetch the data for reportParameters, as reaction?
Thus far, my attempt, which isn't working, looks like this:
// report parameters stuff
async fetchAllReportParameters() {
reaction(
() => this.selectedReports,
async (reports) => {
// reset the report parameters
this.reportParameters = {}
// fetch the parameters for all the reports
await reports
.forEach((report) => {
this.fetchReportParameters(report.Id)
})
}
)
}
/**
* fetches report parameters for a reportId
* #param {number} reportId
*/
fetchReportParameters = (reportId) => {
this.reportParameters[reportId] = []
const onSuccess = (reportParameters) => {
this.reportParameters[reportId] = reportParameters
}
this.api.GetReportParameters(reportId)
.then(onSuccess, this.fetchReportParametersError)
}
fetchReportParametersError = (error) => {
// TODO: output some error here
}
Are you ever actually calling fetchAllReportParameters? If you don't, the reaction will never be created. You may instead like to create the reaction from the constructor, assuming you always want it to be run. One example:
class SomeStore {
constructor() {
this.disposeReportsReaction = reaction(
() => this.selectedReports.slice(),
reports => {
// ...
}
)
}
}
Call storeInstanceName.disposeReaction() whenever you're done with the reaction.
Notice that I've used .slice() here. This is because if you simply pass the array reference, the reaction will never be called. See reaction docs: you have to actually use the value in some way.
You also need to tweak the async code a bit. This:
async (reports) => {
await reports.forEach((report) => {
// ...
})
}
won't do what you hope, because forEach returns undefined. Even if you shift the async keyword to the forEach callback, all the API requests will be sent in quick succession. Consider using something like this instead, depending on whether you want to wait for the preceding request before sending the next one:
try {
for (const report of reports) {
await this.fetchReportParameters(report.id)
}
} catch (e) {
// handle error
}
This isn't always the right answer: sometimes it's fine to send a bunch of requests in quick succession (perhaps especially if it's a small batch, and/or in the context of HTTP/2). If that's ok with you, you could use:
reports => {
// ...
reports.forEach(report => this.fetchReportParameters(report.id))
}

SERIALIZABLE AND ATOMIC TRANSACTIONS RethinkDB

I want to know if performing an update in this way will guarantees me an ATOMIC and SERIALIZABLE transaction.
I need to insert some data in a table Y before performing the update
in a table X and no one canĀ“t do anything in Table X until i release
it.
r.table('TableX', { readMode: 'majority' }).get(IdUser).update(function () {
r.table('TableY').insert({
SomeData: 'Some data will be inserted in Table Y',
SomeData2: 'Some data 2 will be inserted in Table Y',
}).run(conn, (err, results) => {
console.log(results)
})
return { SomeData3: 'Some Data will be updated in TableX' }
}, { nonAtomic: false }).run(conn, function (err, result) {
if (!err && result.replaced > 0)
resolve('OK');
else
reject(new Error(err));
})