mirror of
				https://github.com/zadam/trilium.git
				synced 2025-10-31 10:26:08 +01:00 
			
		
		
		
	added sync mutex for consistency checks and backup
This commit is contained in:
		
							
								
								
									
										21
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										21
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							| @@ -1,6 +1,6 @@ | ||||
| { | ||||
|   "name": "trilium", | ||||
|   "version": "0.0.1", | ||||
|   "version": "0.2.2", | ||||
|   "lockfileVersion": 1, | ||||
|   "requires": true, | ||||
|   "dependencies": { | ||||
| @@ -333,6 +333,11 @@ | ||||
|       "resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.0.tgz", | ||||
|       "integrity": "sha512-jp/uFnooOiO+L211eZOoSyzpOITMXx1rBITauYykG3BRYPu8h0UcxsPNB04RR5vo4Tyz3+ay17tR6JVf9qzYWg==" | ||||
|     }, | ||||
|     "async-mutex": { | ||||
|       "version": "0.1.3", | ||||
|       "resolved": "https://registry.npmjs.org/async-mutex/-/async-mutex-0.1.3.tgz", | ||||
|       "integrity": "sha1-Cq0hEjaXlas/F+M3RFVtLs9UdWY=" | ||||
|     }, | ||||
|     "asynckit": { | ||||
|       "version": "0.4.0", | ||||
|       "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", | ||||
| @@ -2909,15 +2914,6 @@ | ||||
|         } | ||||
|       } | ||||
|     }, | ||||
|     "electron-search-text": { | ||||
|       "version": "0.3.0", | ||||
|       "resolved": "https://registry.npmjs.org/electron-search-text/-/electron-search-text-0.3.0.tgz", | ||||
|       "integrity": "sha1-7/B6qelfmzeDqTGXNiyL43iKPT0=", | ||||
|       "requires": { | ||||
|         "eventemitter2": "2.2.2", | ||||
|         "lodash": "4.17.4" | ||||
|       } | ||||
|     }, | ||||
|     "electron-to-chromium": { | ||||
|       "version": "1.3.27", | ||||
|       "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.27.tgz", | ||||
| @@ -3535,11 +3531,6 @@ | ||||
|         "es5-ext": "0.10.35" | ||||
|       } | ||||
|     }, | ||||
|     "eventemitter2": { | ||||
|       "version": "2.2.2", | ||||
|       "resolved": "https://registry.npmjs.org/eventemitter2/-/eventemitter2-2.2.2.tgz", | ||||
|       "integrity": "sha1-QH6nHCAgzVdTggOrfnpr3Pt2ktU=" | ||||
|     }, | ||||
|     "execa": { | ||||
|       "version": "0.7.0", | ||||
|       "resolved": "https://registry.npmjs.org/execa/-/execa-0.7.0.tgz", | ||||
|   | ||||
| @@ -2,6 +2,11 @@ | ||||
|   "name": "trilium", | ||||
|   "description": "Trilium Notes", | ||||
|   "version": "0.2.2", | ||||
|   "license": "AGPL-3.0-only", | ||||
|   "repository": { | ||||
|     "type": "git", | ||||
|     "url": "https://github.com/zadam/trilium.git" | ||||
|   }, | ||||
|   "scripts": { | ||||
|     "start": "node ./bin/www", | ||||
|     "test-electron": "xo", | ||||
| @@ -14,6 +19,7 @@ | ||||
|     "publish-forge": "electron-forge publish" | ||||
|   }, | ||||
|   "dependencies": { | ||||
|     "async-mutex": "^0.1.3", | ||||
|     "body-parser": "~1.18.2", | ||||
|     "cookie-parser": "~1.4.3", | ||||
|     "debug": "~3.1.0", | ||||
|   | ||||
| @@ -190,7 +190,7 @@ async function validateParentChild(res, parentNoteId, childNoteId, noteTreeId = | ||||
|     if (existing && (noteTreeId === null || existing.note_tree_id !== noteTreeId)) { | ||||
|         res.send({ | ||||
|             success: false, | ||||
|             message: 'This note already exists in target parent note.' | ||||
|             message: 'This note already exists in the target.' | ||||
|         }); | ||||
|  | ||||
|         return false; | ||||
|   | ||||
| @@ -6,6 +6,7 @@ const fs = require('fs-extra'); | ||||
| const dataDir = require('./data_dir'); | ||||
| const log = require('./log'); | ||||
| const sql = require('./sql'); | ||||
| const sync_mutex = require('./sync_mutex'); | ||||
|  | ||||
| async function regularBackup() { | ||||
|     const now = new Date(); | ||||
| @@ -21,17 +22,25 @@ async function regularBackup() { | ||||
| } | ||||
|  | ||||
| async function backupNow() { | ||||
|     const now = utils.nowDate(); | ||||
|     // we don't want to backup DB in the middle of sync with potentially inconsistent DB state | ||||
|     const releaseMutex = await sync_mutex.acquire(); | ||||
|  | ||||
|     const backupFile = dataDir.BACKUP_DIR + "/" + "backup-" + utils.getDateTimeForFile() + ".db"; | ||||
|     try { | ||||
|         const now = utils.nowDate(); | ||||
|  | ||||
|     fs.copySync(dataDir.DOCUMENT_PATH, backupFile); | ||||
|         const backupFile = dataDir.BACKUP_DIR + "/" + "backup-" + utils.getDateTimeForFile() + ".db"; | ||||
|  | ||||
|     log.info("Created backup at " + backupFile); | ||||
|         fs.copySync(dataDir.DOCUMENT_PATH, backupFile); | ||||
|  | ||||
|     await sql.doInTransaction(async () => { | ||||
|         await options.setOption('last_backup_date', now); | ||||
|     }); | ||||
|         log.info("Created backup at " + backupFile); | ||||
|  | ||||
|         await sql.doInTransaction(async () => { | ||||
|             await options.setOption('last_backup_date', now); | ||||
|         }); | ||||
|     } | ||||
|     finally { | ||||
|         releaseMutex(); | ||||
|     } | ||||
| } | ||||
|  | ||||
| async function cleanupOldBackups() { | ||||
|   | ||||
| @@ -3,6 +3,7 @@ | ||||
| const sql = require('./sql'); | ||||
| const log = require('./log'); | ||||
| const messaging = require('./messaging'); | ||||
| const sync_mutex = require('./sync_mutex'); | ||||
|  | ||||
| async function runCheck(query, errorText, errorList) { | ||||
|     const result = await sql.getFirstColumn(query); | ||||
| @@ -80,11 +81,9 @@ async function runSyncRowChecks(table, key, errorList) { | ||||
|         `Missing ${table} records for existing sync rows`, errorList); | ||||
| } | ||||
|  | ||||
| async function runChecks() { | ||||
| async function runAllChecks() { | ||||
|     const errorList = []; | ||||
|  | ||||
|     const startTime = new Date(); | ||||
|  | ||||
|     await runCheck(` | ||||
|           SELECT  | ||||
|             note_id  | ||||
| @@ -139,7 +138,7 @@ async function runChecks() { | ||||
|           WHERE | ||||
|             (SELECT COUNT(*) FROM notes_tree WHERE notes.note_id = notes_tree.note_id AND notes_tree.is_deleted = 0) = 0 | ||||
|             AND notes.is_deleted = 0 | ||||
|     `, ); | ||||
|     `,); | ||||
|  | ||||
|     await runCheck(` | ||||
|           SELECT  | ||||
| @@ -186,7 +185,24 @@ async function runChecks() { | ||||
|         await checkTreeCycles(errorList); | ||||
|     } | ||||
|  | ||||
|     const elapsedTimeMs = new Date().getTime() - startTime.getTime(); | ||||
|     return errorList; | ||||
| } | ||||
|  | ||||
| async function runChecks() { | ||||
|     let errorList; | ||||
|     let elapsedTimeMs; | ||||
|     const releaseMutex = await sync_mutex.acquire(); | ||||
|  | ||||
|     try { | ||||
|         const startTime = new Date(); | ||||
|  | ||||
|         errorList = await runAllChecks(); | ||||
|  | ||||
|         elapsedTimeMs = new Date().getTime() - startTime.getTime(); | ||||
|     } | ||||
|     finally { | ||||
|         releaseMutex(); | ||||
|     } | ||||
|  | ||||
|     if (errorList.length > 0) { | ||||
|         log.info(`Consistency checks failed (took ${elapsedTimeMs}ms) with these errors: ` + JSON.stringify(errorList)); | ||||
|   | ||||
| @@ -14,22 +14,13 @@ const fs = require('fs'); | ||||
| const app_info = require('./app_info'); | ||||
| const messaging = require('./messaging'); | ||||
| const sync_setup = require('./sync_setup'); | ||||
| const sync_mutex = require('./sync_mutex'); | ||||
|  | ||||
| let syncInProgress = false; | ||||
| let proxyToggle = true; | ||||
| let syncServerCertificate = null; | ||||
|  | ||||
| async function sync() { | ||||
|     if (syncInProgress) { | ||||
|         log.info("Sync already in progress"); | ||||
|  | ||||
|         return { | ||||
|             success: false, | ||||
|             message: "Sync already in progress" | ||||
|         }; | ||||
|     } | ||||
|  | ||||
|     syncInProgress = true; | ||||
|     const releaseMutex = await sync_mutex.acquire(); | ||||
|  | ||||
|     try { | ||||
|         if (!await sql.isDbUpToDate()) { | ||||
| @@ -74,7 +65,7 @@ async function sync() { | ||||
|         } | ||||
|     } | ||||
|     finally { | ||||
|         syncInProgress = false; | ||||
|         releaseMutex(); | ||||
|     } | ||||
| } | ||||
|  | ||||
|   | ||||
							
								
								
									
										8
									
								
								services/sync_mutex.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								services/sync_mutex.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,8 @@ | ||||
| /** | ||||
|  * Sync makes process can make data intermittently inconsistent. Processes which require strong data consistency | ||||
|  * (like consistency checks) can use this mutex to make sure sync isn't currently running. | ||||
|  */ | ||||
|  | ||||
| const Mutex = require('async-mutex').Mutex; | ||||
|  | ||||
| module.exports = new Mutex(); | ||||
		Reference in New Issue
	
	Block a user