mirror of
				https://github.com/zadam/trilium.git
				synced 2025-10-29 02:28:57 +01:00 
			
		
		
		
	better transaction handling with rollback on exception
This commit is contained in:
		
							parent
							
								
									afadd6ec06
								
							
						
					
					
						commit
						de3d1b3e39
					
				| @ -44,73 +44,70 @@ 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]); | ||||
| 
 | ||||
|     await sql.beginTransaction(); | ||||
|     await sql.doInTransaction(async () => { | ||||
|         if (history) { | ||||
|             await sql.execute("update notes_history set note_title = ?, note_text = ?, encryption = ?, date_modified_to = ? where id = ?", [ | ||||
|                 note['detail']['note_title'], | ||||
|                 note['detail']['note_text'], | ||||
|                 note['detail']['encryption'], | ||||
|                 now, | ||||
|                 history['id'] | ||||
|             ]); | ||||
|         } | ||||
|         else { | ||||
|             await sql.execute("insert into notes_history (note_id, note_title, note_text, encryption, date_modified_from, date_modified_to) values (?, ?, ?, ?, ?, ?)", [ | ||||
|                 noteId, | ||||
|                 note['detail']['note_title'], | ||||
|                 note['detail']['note_text'], | ||||
|                 note['detail']['encryption'], | ||||
|                 now, | ||||
|                 now | ||||
|             ]); | ||||
|         } | ||||
| 
 | ||||
|     if (history) { | ||||
|         await sql.execute("update notes_history set note_title = ?, note_text = ?, encryption = ?, date_modified_to = ? where id = ?", [ | ||||
|         if (note['detail']['note_title'] !== detail['note_title']) { | ||||
|             await sql.deleteRecentAudits(audit_category.UPDATE_TITLE, req, noteId); | ||||
|             await sql.addAudit(audit_category.UPDATE_TITLE, req, noteId); | ||||
|         } | ||||
| 
 | ||||
|         if (note['detail']['note_text'] !== detail['note_text']) { | ||||
|             await sql.deleteRecentAudits(audit_category.UPDATE_CONTENT, req, noteId); | ||||
|             await sql.addAudit(audit_category.UPDATE_CONTENT, req, noteId); | ||||
|         } | ||||
| 
 | ||||
|         if (note['detail']['encryption'] !== detail['encryption']) { | ||||
|             await sql.addAudit(audit_category.ENCRYPTION, req, noteId, detail['encryption'], note['detail']['encryption']); | ||||
|         } | ||||
| 
 | ||||
|         await sql.execute("update notes set note_title = ?, note_text = ?, encryption = ?, date_modified = ? where note_id = ?", [ | ||||
|             note['detail']['note_title'], | ||||
|             note['detail']['note_text'], | ||||
|             note['detail']['encryption'], | ||||
|             now, | ||||
|             history['id'] | ||||
|         ]); | ||||
|     } | ||||
|     else { | ||||
|         await sql.execute("insert into notes_history (note_id, note_title, note_text, encryption, date_modified_from, date_modified_to) values (?, ?, ?, ?, ?, ?)", [ | ||||
|             noteId, | ||||
|             note['detail']['note_title'], | ||||
|             note['detail']['note_text'], | ||||
|             note['detail']['encryption'], | ||||
|             now, | ||||
|             now | ||||
|         ]); | ||||
|     } | ||||
|             noteId]); | ||||
| 
 | ||||
|     if (note['detail']['note_title'] !== detail['note_title']) { | ||||
|         await sql.deleteRecentAudits(audit_category.UPDATE_TITLE, req, noteId); | ||||
|         await sql.addAudit(audit_category.UPDATE_TITLE, req, noteId); | ||||
|     } | ||||
|         await sql.remove("images", noteId); | ||||
| 
 | ||||
|     if (note['detail']['note_text'] !== detail['note_text']) { | ||||
|         await sql.deleteRecentAudits(audit_category.UPDATE_CONTENT, req, noteId); | ||||
|         await sql.addAudit(audit_category.UPDATE_CONTENT, req, noteId); | ||||
|     } | ||||
|         for (const img of note['images']) { | ||||
|             img['image_data'] = atob(img['image_data']); | ||||
| 
 | ||||
|     if (note['detail']['encryption'] !== detail['encryption']) { | ||||
|         await sql.addAudit(audit_category.ENCRYPTION, req, noteId, detail['encryption'], note['detail']['encryption']); | ||||
|     } | ||||
|             await sql.insert("images", img); | ||||
|         } | ||||
| 
 | ||||
|     await sql.execute("update notes set note_title = ?, note_text = ?, encryption = ?, date_modified = ? where note_id = ?", [ | ||||
|         note['detail']['note_title'], | ||||
|         note['detail']['note_text'], | ||||
|         note['detail']['encryption'], | ||||
|         now, | ||||
|         noteId]); | ||||
|         await sql.remove("links", noteId); | ||||
| 
 | ||||
|     await sql.remove("images", noteId); | ||||
| 
 | ||||
|     for (const img of note['images']) { | ||||
|         img['image_data'] = atob(img['image_data']); | ||||
| 
 | ||||
|         await sql.insert("images", img); | ||||
|     } | ||||
| 
 | ||||
|     await sql.remove("links", noteId); | ||||
| 
 | ||||
|     for (const link in note['links']) | ||||
|         await sql.insert("links", link); | ||||
| 
 | ||||
|     await sql.commit(); | ||||
|         for (const link in note['links']) { | ||||
|             await sql.insert("links", link); | ||||
|         } | ||||
|     }); | ||||
| 
 | ||||
|     res.send({}); | ||||
| }); | ||||
| 
 | ||||
| router.delete('/:noteId', async (req, res, next) => { | ||||
|     await sql.beginTransaction(); | ||||
| 
 | ||||
|     await deleteNote(req.params.noteId, req); | ||||
| 
 | ||||
|     await sql.commit(); | ||||
|     await sql.doInTransaction(async () => { | ||||
|         await deleteNote(req.params.noteId, req); | ||||
|     }); | ||||
| 
 | ||||
|     res.send({}); | ||||
| }); | ||||
| @ -165,33 +162,31 @@ router.post('/:parentNoteId/children', async (req, res, next) => { | ||||
|         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(); | ||||
|         await sql.insert("notes", { | ||||
|             'note_id': noteId, | ||||
|             'note_title': note['note_title'], | ||||
|             'note_text': '', | ||||
|             'note_clone_id': '', | ||||
|             'date_created': now, | ||||
|             'date_modified': now, | ||||
|             'encryption': note['encryption'] | ||||
|         }); | ||||
| 
 | ||||
|     await sql.insert("notes", { | ||||
|         'note_id': noteId, | ||||
|         'note_title': note['note_title'], | ||||
|         'note_text': '', | ||||
|         'note_clone_id': '', | ||||
|         'date_created': now, | ||||
|         'date_modified': now, | ||||
|         'encryption': note['encryption'] | ||||
|         await sql.insert("notes_tree", { | ||||
|             'note_id': noteId, | ||||
|             'note_pid': parentNoteId, | ||||
|             'note_pos': newNotePos, | ||||
|             'is_expanded': 0, | ||||
|             'date_modified': utils.nowTimestamp(), | ||||
|             'is_deleted': 0 | ||||
|         }); | ||||
|     }); | ||||
| 
 | ||||
|     await sql.insert("notes_tree", { | ||||
|         'note_id': noteId, | ||||
|         'note_pid': parentNoteId, | ||||
|         'note_pos': newNotePos, | ||||
|         'is_expanded': 0, | ||||
|         'date_modified': utils.nowTimestamp(), | ||||
|         'is_deleted': 0 | ||||
|     }); | ||||
| 
 | ||||
|     await sql.commit(); | ||||
| 
 | ||||
|     res.send({ | ||||
|         'note_id': noteId | ||||
|     }); | ||||
|  | ||||
| @ -22,14 +22,12 @@ router.put('/:noteId/moveTo/:parentId', auth.checkApiAuth, async (req, res, next | ||||
| 
 | ||||
|     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 = ?", | ||||
|             [parentId, newNotePos, now, noteId]); | ||||
| 
 | ||||
|     await sql.execute("update notes_tree set note_pid = ?, note_pos = ?, date_modified = ? where note_id = ?", | ||||
|         [parentId, newNotePos, now, noteId]); | ||||
| 
 | ||||
|     await sql.addAudit(audit_category.CHANGE_PARENT, req, noteId, null, parentId); | ||||
| 
 | ||||
|     await sql.commit(); | ||||
|         await sql.addAudit(audit_category.CHANGE_PARENT, req, noteId, null, parentId); | ||||
|     }); | ||||
| 
 | ||||
|     res.send({}); | ||||
| }); | ||||
| @ -43,16 +41,14 @@ router.put('/:noteId/moveBefore/:beforeNoteId', async (req, res, next) => { | ||||
|     if (beforeNote) { | ||||
|         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 = ?", | ||||
|                 [beforeNote['note_pid'], beforeNote['note_pos'], now, noteId]); | ||||
| 
 | ||||
|         await sql.execute("update notes_tree set note_pid = ?, note_pos = ?, date_modified = ? where note_id = ?", | ||||
|             [beforeNote['note_pid'], beforeNote['note_pos'], now, noteId]); | ||||
| 
 | ||||
|         await sql.addAudit(audit_category.CHANGE_POSITION, req, noteId); | ||||
| 
 | ||||
|         await sql.commit(); | ||||
|             await sql.addAudit(audit_category.CHANGE_POSITION, req, noteId); | ||||
|         }); | ||||
|     } | ||||
| 
 | ||||
|     res.send({}); | ||||
| @ -67,17 +63,15 @@ router.put('/:noteId/moveAfter/:afterNoteId', async (req, res, next) => { | ||||
|     if (afterNote) { | ||||
|         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", | ||||
|                 [now, afterNote['note_pid'], afterNote['note_pos']]); | ||||
| 
 | ||||
|         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']]); | ||||
|             await sql.execute("update notes_tree set note_pid = ?, note_pos = ?, date_modified = ? where note_id = ?", | ||||
|                 [afterNote['note_pid'], afterNote['note_pos'] + 1, now, noteId]); | ||||
| 
 | ||||
|         await sql.execute("update notes_tree set note_pid = ?, note_pos = ?, date_modified = ? where note_id = ?", | ||||
|             [afterNote['note_pid'], afterNote['note_pos'] + 1, now, noteId]); | ||||
| 
 | ||||
|         await sql.addAudit(audit_category.CHANGE_POSITION, req, noteId); | ||||
| 
 | ||||
|         await sql.commit(); | ||||
|             await sql.addAudit(audit_category.CHANGE_POSITION, req, noteId); | ||||
|         }); | ||||
|     } | ||||
| 
 | ||||
|     res.send({}); | ||||
| @ -88,9 +82,11 @@ router.put('/:noteId/expanded/:expanded', async (req, res, next) => { | ||||
|     const expanded = req.params.expanded; | ||||
|     const now = utils.nowTimestamp(); | ||||
| 
 | ||||
|     await sql.execute("update notes_tree set is_expanded = ?, date_modified = ? where note_id = ?", [expanded, now, noteId]); | ||||
|     await sql.doInTransaction(async () => { | ||||
|         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({}); | ||||
| }); | ||||
|  | ||||
| @ -27,13 +27,11 @@ router.post('/', async (req, res, next) => { | ||||
|     if (ALLOWED_OPTIONS.includes(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.commit(); | ||||
|             await sql.setOption(body['name'], body['value']); | ||||
|         }); | ||||
| 
 | ||||
|         res.send({}); | ||||
|     } | ||||
|  | ||||
| @ -4,6 +4,7 @@ module.exports = { | ||||
|     UPDATE_CONTENT: 'CONTENT', | ||||
|     UPDATE_TITLE: 'TITLE', | ||||
|     CHANGE_POSITION: 'POSITION', | ||||
|     CHANGE_EXPANDED: 'EXPANDED', | ||||
|     CREATE_NOTE: 'CREATE', | ||||
|     DELETE_NOTE: 'DELETE', | ||||
|     CHANGE_PARENT: 'PARENT', | ||||
|  | ||||
| @ -55,15 +55,13 @@ async function changePassword(currentPassword, newPassword, req = null) { | ||||
| 
 | ||||
|     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.commit(); | ||||
|         await sql.addAudit(audit_category.CHANGE_PASSWORD, req); | ||||
|     }); | ||||
| 
 | ||||
|     return { | ||||
|         'success': true, | ||||
|  | ||||
| @ -42,13 +42,11 @@ async function migrate() { | ||||
|         try { | ||||
|             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.commit(); | ||||
|                 await sql.setOption("db_version", mig.dbVersion); | ||||
|             }); | ||||
| 
 | ||||
|             log.info("Migration to version " + mig.dbVersion + " has been successful."); | ||||
| 
 | ||||
|  | ||||
| @ -109,6 +109,21 @@ async function deleteRecentAudits(category, req, noteId) { | ||||
|             [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 = { | ||||
|     insert, | ||||
|     getSingleResult, | ||||
| @ -118,10 +133,8 @@ module.exports = { | ||||
|     executeScript, | ||||
|     getOption, | ||||
|     setOption, | ||||
|     beginTransaction, | ||||
|     commit, | ||||
|     rollback, | ||||
|     addAudit, | ||||
|     deleteRecentAudits, | ||||
|     remove | ||||
|     remove, | ||||
|     doInTransaction | ||||
| }; | ||||
| @ -34,41 +34,37 @@ async function pullSync(cookieJar, syncLog) { | ||||
|     } | ||||
| 
 | ||||
|     try { | ||||
|         await sql.beginTransaction(); | ||||
|         await sql.doInTransaction(async () => { | ||||
|             await putChanged(resp, syncLog); | ||||
| 
 | ||||
|         await putChanged(resp, syncLog); | ||||
|             for (const noteId of resp.notes) { | ||||
|                 let note; | ||||
| 
 | ||||
|         for (const noteId of resp.notes) { | ||||
|             let note; | ||||
|                 try { | ||||
|                     note = await rp({ | ||||
|                         uri: SYNC_SERVER + "/api/sync/note/" + noteId + "/" + lastSyncedPull, | ||||
|                         headers: { | ||||
|                             auth: 'sync' | ||||
|                         }, | ||||
|                         json: true, | ||||
|                         jar: cookieJar | ||||
|                     }); | ||||
|                 } | ||||
|                 catch (e) { | ||||
|                     throw new Error("Can't pull note " + noteId + ", inner exception: " + e.stack); | ||||
|                 } | ||||
| 
 | ||||
|             try { | ||||
|                 note = await rp({ | ||||
|                     uri: SYNC_SERVER + "/api/sync/note/" + noteId + "/" + lastSyncedPull, | ||||
|                     headers: { | ||||
|                         auth: 'sync' | ||||
|                     }, | ||||
|                     json: true, | ||||
|                     jar: cookieJar | ||||
|                 }); | ||||
|             } | ||||
|             catch (e) { | ||||
|                 throw new Error("Can't pull note " + noteId + ", inner exception: " + e.stack); | ||||
|                 await putNote(note, syncLog); | ||||
|             } | ||||
| 
 | ||||
|             await putNote(note, syncLog); | ||||
|         } | ||||
|             if (resp.notes.length > 0) { | ||||
|                 await sql.addAudit(audit_category.SYNC); | ||||
|             } | ||||
| 
 | ||||
|         if (resp.notes.length > 0) { | ||||
|             await sql.addAudit(audit_category.SYNC); | ||||
|         } | ||||
| 
 | ||||
|         await sql.setOption('last_synced_pull', resp.syncTimestamp); | ||||
| 
 | ||||
|         await sql.commit(); | ||||
|             await sql.setOption('last_synced_pull', resp.syncTimestamp); | ||||
|         }); | ||||
|     } | ||||
|     catch (e) { | ||||
|         await sql.rollback(); | ||||
| 
 | ||||
|         throw e; | ||||
|     } | ||||
| } | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user
	 azivner
						azivner