added sync mutex for consistency checks and backup

This commit is contained in:
azivner 2018-01-04 21:37:36 -05:00
parent a6a687c4a6
commit 663bd1a8fe
7 changed files with 61 additions and 40 deletions

21
package-lock.json generated
View file

@ -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",

View file

@ -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",

View file

@ -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;

View file

@ -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() {

View file

@ -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));

View file

@ -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
View 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();