diff --git a/package-lock.json b/package-lock.json index 9af268a06..c0ad36b65 100644 --- a/package-lock.json +++ b/package-lock.json @@ -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", diff --git a/package.json b/package.json index b9e41fb70..742edc82c 100644 --- a/package.json +++ b/package.json @@ -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", diff --git a/routes/api/notes_move.js b/routes/api/notes_move.js index 25a6484e0..21ee9327d 100644 --- a/routes/api/notes_move.js +++ b/routes/api/notes_move.js @@ -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; diff --git a/services/backup.js b/services/backup.js index eee583972..80269921d 100644 --- a/services/backup.js +++ b/services/backup.js @@ -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() { diff --git a/services/consistency_checks.js b/services/consistency_checks.js index cb4217c13..8e12d8e24 100644 --- a/services/consistency_checks.js +++ b/services/consistency_checks.js @@ -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)); diff --git a/services/sync.js b/services/sync.js index f3bcf394d..17f0298d4 100644 --- a/services/sync.js +++ b/services/sync.js @@ -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(); } } diff --git a/services/sync_mutex.js b/services/sync_mutex.js new file mode 100644 index 000000000..0fe21bd98 --- /dev/null +++ b/services/sync_mutex.js @@ -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(); \ No newline at end of file