mirror of
				https://github.com/zadam/trilium.git
				synced 2025-10-31 02:16:05 +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", |   "name": "trilium", | ||||||
|   "version": "0.0.1", |   "version": "0.2.2", | ||||||
|   "lockfileVersion": 1, |   "lockfileVersion": 1, | ||||||
|   "requires": true, |   "requires": true, | ||||||
|   "dependencies": { |   "dependencies": { | ||||||
| @@ -333,6 +333,11 @@ | |||||||
|       "resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.0.tgz", |       "resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.0.tgz", | ||||||
|       "integrity": "sha512-jp/uFnooOiO+L211eZOoSyzpOITMXx1rBITauYykG3BRYPu8h0UcxsPNB04RR5vo4Tyz3+ay17tR6JVf9qzYWg==" |       "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": { |     "asynckit": { | ||||||
|       "version": "0.4.0", |       "version": "0.4.0", | ||||||
|       "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", |       "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": { |     "electron-to-chromium": { | ||||||
|       "version": "1.3.27", |       "version": "1.3.27", | ||||||
|       "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.27.tgz", |       "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.27.tgz", | ||||||
| @@ -3535,11 +3531,6 @@ | |||||||
|         "es5-ext": "0.10.35" |         "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": { |     "execa": { | ||||||
|       "version": "0.7.0", |       "version": "0.7.0", | ||||||
|       "resolved": "https://registry.npmjs.org/execa/-/execa-0.7.0.tgz", |       "resolved": "https://registry.npmjs.org/execa/-/execa-0.7.0.tgz", | ||||||
|   | |||||||
| @@ -2,6 +2,11 @@ | |||||||
|   "name": "trilium", |   "name": "trilium", | ||||||
|   "description": "Trilium Notes", |   "description": "Trilium Notes", | ||||||
|   "version": "0.2.2", |   "version": "0.2.2", | ||||||
|  |   "license": "AGPL-3.0-only", | ||||||
|  |   "repository": { | ||||||
|  |     "type": "git", | ||||||
|  |     "url": "https://github.com/zadam/trilium.git" | ||||||
|  |   }, | ||||||
|   "scripts": { |   "scripts": { | ||||||
|     "start": "node ./bin/www", |     "start": "node ./bin/www", | ||||||
|     "test-electron": "xo", |     "test-electron": "xo", | ||||||
| @@ -14,6 +19,7 @@ | |||||||
|     "publish-forge": "electron-forge publish" |     "publish-forge": "electron-forge publish" | ||||||
|   }, |   }, | ||||||
|   "dependencies": { |   "dependencies": { | ||||||
|  |     "async-mutex": "^0.1.3", | ||||||
|     "body-parser": "~1.18.2", |     "body-parser": "~1.18.2", | ||||||
|     "cookie-parser": "~1.4.3", |     "cookie-parser": "~1.4.3", | ||||||
|     "debug": "~3.1.0", |     "debug": "~3.1.0", | ||||||
|   | |||||||
| @@ -190,7 +190,7 @@ async function validateParentChild(res, parentNoteId, childNoteId, noteTreeId = | |||||||
|     if (existing && (noteTreeId === null || existing.note_tree_id !== noteTreeId)) { |     if (existing && (noteTreeId === null || existing.note_tree_id !== noteTreeId)) { | ||||||
|         res.send({ |         res.send({ | ||||||
|             success: false, |             success: false, | ||||||
|             message: 'This note already exists in target parent note.' |             message: 'This note already exists in the target.' | ||||||
|         }); |         }); | ||||||
|  |  | ||||||
|         return false; |         return false; | ||||||
|   | |||||||
| @@ -6,6 +6,7 @@ const fs = require('fs-extra'); | |||||||
| const dataDir = require('./data_dir'); | const dataDir = require('./data_dir'); | ||||||
| const log = require('./log'); | const log = require('./log'); | ||||||
| const sql = require('./sql'); | const sql = require('./sql'); | ||||||
|  | const sync_mutex = require('./sync_mutex'); | ||||||
|  |  | ||||||
| async function regularBackup() { | async function regularBackup() { | ||||||
|     const now = new Date(); |     const now = new Date(); | ||||||
| @@ -21,6 +22,10 @@ async function regularBackup() { | |||||||
| } | } | ||||||
|  |  | ||||||
| async function backupNow() { | async function backupNow() { | ||||||
|  |     // we don't want to backup DB in the middle of sync with potentially inconsistent DB state | ||||||
|  |     const releaseMutex = await sync_mutex.acquire(); | ||||||
|  |  | ||||||
|  |     try { | ||||||
|         const now = utils.nowDate(); |         const now = utils.nowDate(); | ||||||
|  |  | ||||||
|         const backupFile = dataDir.BACKUP_DIR + "/" + "backup-" + utils.getDateTimeForFile() + ".db"; |         const backupFile = dataDir.BACKUP_DIR + "/" + "backup-" + utils.getDateTimeForFile() + ".db"; | ||||||
| @@ -32,6 +37,10 @@ async function backupNow() { | |||||||
|         await sql.doInTransaction(async () => { |         await sql.doInTransaction(async () => { | ||||||
|             await options.setOption('last_backup_date', now); |             await options.setOption('last_backup_date', now); | ||||||
|         }); |         }); | ||||||
|  |     } | ||||||
|  |     finally { | ||||||
|  |         releaseMutex(); | ||||||
|  |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| async function cleanupOldBackups() { | async function cleanupOldBackups() { | ||||||
|   | |||||||
| @@ -3,6 +3,7 @@ | |||||||
| const sql = require('./sql'); | const sql = require('./sql'); | ||||||
| const log = require('./log'); | const log = require('./log'); | ||||||
| const messaging = require('./messaging'); | const messaging = require('./messaging'); | ||||||
|  | const sync_mutex = require('./sync_mutex'); | ||||||
|  |  | ||||||
| async function runCheck(query, errorText, errorList) { | async function runCheck(query, errorText, errorList) { | ||||||
|     const result = await sql.getFirstColumn(query); |     const result = await sql.getFirstColumn(query); | ||||||
| @@ -80,11 +81,9 @@ async function runSyncRowChecks(table, key, errorList) { | |||||||
|         `Missing ${table} records for existing sync rows`, errorList); |         `Missing ${table} records for existing sync rows`, errorList); | ||||||
| } | } | ||||||
|  |  | ||||||
| async function runChecks() { | async function runAllChecks() { | ||||||
|     const errorList = []; |     const errorList = []; | ||||||
|  |  | ||||||
|     const startTime = new Date(); |  | ||||||
|  |  | ||||||
|     await runCheck(` |     await runCheck(` | ||||||
|           SELECT  |           SELECT  | ||||||
|             note_id  |             note_id  | ||||||
| @@ -139,7 +138,7 @@ async function runChecks() { | |||||||
|           WHERE |           WHERE | ||||||
|             (SELECT COUNT(*) FROM notes_tree WHERE notes.note_id = notes_tree.note_id AND notes_tree.is_deleted = 0) = 0 |             (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 |             AND notes.is_deleted = 0 | ||||||
|     `, ); |     `,); | ||||||
|  |  | ||||||
|     await runCheck(` |     await runCheck(` | ||||||
|           SELECT  |           SELECT  | ||||||
| @@ -186,7 +185,24 @@ async function runChecks() { | |||||||
|         await checkTreeCycles(errorList); |         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) { |     if (errorList.length > 0) { | ||||||
|         log.info(`Consistency checks failed (took ${elapsedTimeMs}ms) with these errors: ` + JSON.stringify(errorList)); |         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 app_info = require('./app_info'); | ||||||
| const messaging = require('./messaging'); | const messaging = require('./messaging'); | ||||||
| const sync_setup = require('./sync_setup'); | const sync_setup = require('./sync_setup'); | ||||||
|  | const sync_mutex = require('./sync_mutex'); | ||||||
|  |  | ||||||
| let syncInProgress = false; |  | ||||||
| let proxyToggle = true; | let proxyToggle = true; | ||||||
| let syncServerCertificate = null; | let syncServerCertificate = null; | ||||||
|  |  | ||||||
| async function sync() { | async function sync() { | ||||||
|     if (syncInProgress) { |     const releaseMutex = await sync_mutex.acquire(); | ||||||
|         log.info("Sync already in progress"); |  | ||||||
|  |  | ||||||
|         return { |  | ||||||
|             success: false, |  | ||||||
|             message: "Sync already in progress" |  | ||||||
|         }; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     syncInProgress = true; |  | ||||||
|  |  | ||||||
|     try { |     try { | ||||||
|         if (!await sql.isDbUpToDate()) { |         if (!await sql.isDbUpToDate()) { | ||||||
| @@ -74,7 +65,7 @@ async function sync() { | |||||||
|         } |         } | ||||||
|     } |     } | ||||||
|     finally { |     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