better transaction handling with rollback on exception

This commit is contained in:
azivner 2017-10-29 18:50:28 -04:00
parent afadd6ec06
commit de3d1b3e39
8 changed files with 145 additions and 150 deletions

View File

@ -44,8 +44,7 @@ router.put('/:noteId', async (req, res, next) => {
const history = await sql.getSingleResult("select id from notes_history where note_id = ? and date_modified_from >= ?", [noteId, historyCutoff]); const history = await sql.getSingleResult("select id from notes_history where note_id = ? and date_modified_from >= ?", [noteId, historyCutoff]);
await sql.beginTransaction(); await sql.doInTransaction(async () => {
if (history) { if (history) {
await sql.execute("update notes_history set note_title = ?, note_text = ?, encryption = ?, date_modified_to = ? where id = ?", [ await sql.execute("update notes_history set note_title = ?, note_text = ?, encryption = ?, date_modified_to = ? where id = ?", [
note['detail']['note_title'], note['detail']['note_title'],
@ -97,20 +96,18 @@ router.put('/:noteId', async (req, res, next) => {
await sql.remove("links", noteId); await sql.remove("links", noteId);
for (const link in note['links']) for (const link in note['links']) {
await sql.insert("links", link); await sql.insert("links", link);
}
await sql.commit(); });
res.send({}); res.send({});
}); });
router.delete('/:noteId', async (req, res, next) => { router.delete('/:noteId', async (req, res, next) => {
await sql.beginTransaction(); await sql.doInTransaction(async () => {
await deleteNote(req.params.noteId, req); await deleteNote(req.params.noteId, req);
});
await sql.commit();
res.send({}); res.send({});
}); });
@ -165,8 +162,7 @@ router.post('/:parentNoteId/children', async (req, res, next) => {
throw new ('Unknown target: ' + note['target']); throw new ('Unknown target: ' + note['target']);
} }
await sql.beginTransaction(); await sql.doInTransaction(async () => {
await sql.addAudit(audit_category.CREATE_NOTE, req, noteId); await sql.addAudit(audit_category.CREATE_NOTE, req, noteId);
const now = utils.nowTimestamp(); const now = utils.nowTimestamp();
@ -189,8 +185,7 @@ router.post('/:parentNoteId/children', async (req, res, next) => {
'date_modified': utils.nowTimestamp(), 'date_modified': utils.nowTimestamp(),
'is_deleted': 0 'is_deleted': 0
}); });
});
await sql.commit();
res.send({ res.send({
'note_id': noteId 'note_id': noteId

View File

@ -22,14 +22,12 @@ router.put('/:noteId/moveTo/:parentId', auth.checkApiAuth, async (req, res, next
const now = utils.nowTimestamp(); const now = utils.nowTimestamp();
await sql.beginTransaction(); await sql.doInTransaction(async () => {
await sql.execute("update notes_tree set note_pid = ?, note_pos = ?, date_modified = ? where note_id = ?", await sql.execute("update notes_tree set note_pid = ?, note_pos = ?, date_modified = ? where note_id = ?",
[parentId, newNotePos, now, noteId]); [parentId, newNotePos, now, noteId]);
await sql.addAudit(audit_category.CHANGE_PARENT, req, noteId, null, parentId); await sql.addAudit(audit_category.CHANGE_PARENT, req, noteId, null, parentId);
});
await sql.commit();
res.send({}); res.send({});
}); });
@ -43,16 +41,14 @@ router.put('/:noteId/moveBefore/:beforeNoteId', async (req, res, next) => {
if (beforeNote) { if (beforeNote) {
const now = utils.nowTimestamp(); const now = utils.nowTimestamp();
await sql.beginTransaction(); await sql.doInTransaction(async () => {
await sql.execute("update notes_tree set note_pos = note_pos + 1, date_modified = ? where note_id = ?", [now, beforeNoteId]); await sql.execute("update notes_tree set note_pos = note_pos + 1, date_modified = ? where note_id = ?", [now, beforeNoteId]);
await sql.execute("update notes_tree set note_pid = ?, note_pos = ?, date_modified = ? where note_id = ?", await sql.execute("update notes_tree set note_pid = ?, note_pos = ?, date_modified = ? where note_id = ?",
[beforeNote['note_pid'], beforeNote['note_pos'], now, noteId]); [beforeNote['note_pid'], beforeNote['note_pos'], now, noteId]);
await sql.addAudit(audit_category.CHANGE_POSITION, req, noteId); await sql.addAudit(audit_category.CHANGE_POSITION, req, noteId);
});
await sql.commit();
} }
res.send({}); res.send({});
@ -67,8 +63,7 @@ router.put('/:noteId/moveAfter/:afterNoteId', async (req, res, next) => {
if (afterNote) { if (afterNote) {
const now = utils.nowTimestamp(); const now = utils.nowTimestamp();
await sql.beginTransaction(); await sql.doInTransaction(async () => {
await sql.execute("update notes_tree set note_pos = note_pos + 1, date_modified = ? where note_pid = ? and note_pos > ? and is_deleted = 0", await sql.execute("update notes_tree set note_pos = note_pos + 1, date_modified = ? where note_pid = ? and note_pos > ? and is_deleted = 0",
[now, afterNote['note_pid'], afterNote['note_pos']]); [now, afterNote['note_pid'], afterNote['note_pos']]);
@ -76,8 +71,7 @@ router.put('/:noteId/moveAfter/:afterNoteId', async (req, res, next) => {
[afterNote['note_pid'], afterNote['note_pos'] + 1, now, noteId]); [afterNote['note_pid'], afterNote['note_pos'] + 1, now, noteId]);
await sql.addAudit(audit_category.CHANGE_POSITION, req, noteId); await sql.addAudit(audit_category.CHANGE_POSITION, req, noteId);
});
await sql.commit();
} }
res.send({}); res.send({});
@ -88,9 +82,11 @@ router.put('/:noteId/expanded/:expanded', async (req, res, next) => {
const expanded = req.params.expanded; const expanded = req.params.expanded;
const now = utils.nowTimestamp(); const now = utils.nowTimestamp();
await sql.doInTransaction(async () => {
await sql.execute("update notes_tree set is_expanded = ?, date_modified = ? where note_id = ?", [expanded, now, noteId]); await sql.execute("update notes_tree set is_expanded = ?, date_modified = ? where note_id = ?", [expanded, now, noteId]);
// no audit here, not really important await sql.addAudit(audit_category.CHANGE_EXPANDED, req, noteId, null, expanded);
});
res.send({}); res.send({});
}); });

View File

@ -27,13 +27,11 @@ router.post('/', async (req, res, next) => {
if (ALLOWED_OPTIONS.includes(body['name'])) { if (ALLOWED_OPTIONS.includes(body['name'])) {
const optionName = await sql.getOption(body['name']); const optionName = await sql.getOption(body['name']);
await sql.beginTransaction(); await sql.doInTransaction(async () => {
await sql.addAudit(audit_category.SETTINGS, req, null, optionName, body['value'], body['name']); await sql.addAudit(audit_category.SETTINGS, req, null, optionName, body['value'], body['name']);
await sql.setOption(body['name'], body['value']); await sql.setOption(body['name'], body['value']);
});
await sql.commit();
res.send({}); res.send({});
} }

View File

@ -4,6 +4,7 @@ module.exports = {
UPDATE_CONTENT: 'CONTENT', UPDATE_CONTENT: 'CONTENT',
UPDATE_TITLE: 'TITLE', UPDATE_TITLE: 'TITLE',
CHANGE_POSITION: 'POSITION', CHANGE_POSITION: 'POSITION',
CHANGE_EXPANDED: 'EXPANDED',
CREATE_NOTE: 'CREATE', CREATE_NOTE: 'CREATE',
DELETE_NOTE: 'DELETE', DELETE_NOTE: 'DELETE',
CHANGE_PARENT: 'PARENT', CHANGE_PARENT: 'PARENT',

View File

@ -55,15 +55,13 @@ async function changePassword(currentPassword, newPassword, req = null) {
const newEncryptedDataKey = encrypt(decryptedDataKey); const newEncryptedDataKey = encrypt(decryptedDataKey);
await sql.beginTransaction(); await sql.doInTransaction(async () => {
await sql.setOption('encrypted_data_key', newEncryptedDataKey); await sql.setOption('encrypted_data_key', newEncryptedDataKey);
await sql.setOption('password_verification_hash', newPasswordVerificationKey); await sql.setOption('password_verification_hash', newPasswordVerificationKey);
await sql.addAudit(audit_category.CHANGE_PASSWORD, req); await sql.addAudit(audit_category.CHANGE_PASSWORD, req);
});
await sql.commit();
return { return {
'success': true, 'success': true,

View File

@ -42,13 +42,11 @@ async function migrate() {
try { try {
log.info("Attempting migration to version " + mig.dbVersion + " with script: " + migrationSql); log.info("Attempting migration to version " + mig.dbVersion + " with script: " + migrationSql);
await sql.beginTransaction(); await sql.doInTransaction(async () => {
await sql.executeScript(migrationSql); await sql.executeScript(migrationSql);
await sql.setOption("db_version", mig.dbVersion); await sql.setOption("db_version", mig.dbVersion);
});
await sql.commit();
log.info("Migration to version " + mig.dbVersion + " has been successful."); log.info("Migration to version " + mig.dbVersion + " has been successful.");

View File

@ -109,6 +109,21 @@ async function deleteRecentAudits(category, req, noteId) {
[category, browserId, noteId, deleteCutoff]) [category, browserId, noteId, deleteCutoff])
} }
async function doInTransaction(func) {
try {
await beginTransaction();
await func();
await commit();
}
catch (e) {
log.error("Error executing transaction, executing rollback. Inner exception: " + e.stack);
await rollback();
}
}
module.exports = { module.exports = {
insert, insert,
getSingleResult, getSingleResult,
@ -118,10 +133,8 @@ module.exports = {
executeScript, executeScript,
getOption, getOption,
setOption, setOption,
beginTransaction,
commit,
rollback,
addAudit, addAudit,
deleteRecentAudits, deleteRecentAudits,
remove remove,
doInTransaction
}; };

View File

@ -34,8 +34,7 @@ async function pullSync(cookieJar, syncLog) {
} }
try { try {
await sql.beginTransaction(); await sql.doInTransaction(async () => {
await putChanged(resp, syncLog); await putChanged(resp, syncLog);
for (const noteId of resp.notes) { for (const noteId of resp.notes) {
@ -63,12 +62,9 @@ async function pullSync(cookieJar, syncLog) {
} }
await sql.setOption('last_synced_pull', resp.syncTimestamp); await sql.setOption('last_synced_pull', resp.syncTimestamp);
});
await sql.commit();
} }
catch (e) { catch (e) {
await sql.rollback();
throw e; throw e;
} }
} }