added basic CLS support with re-entrant transactions

This commit is contained in:
azivner
2018-03-28 23:41:22 -04:00
parent b10b0048f3
commit 0ec909fd7a
13 changed files with 115 additions and 24 deletions

View File

@@ -7,6 +7,7 @@ const dataDir = require('./data_dir');
const log = require('./log');
const sql = require('./sql');
const sync_mutex = require('./sync_mutex');
const cls = require('./cls');
async function regularBackup() {
const now = new Date();
@@ -64,10 +65,10 @@ if (!fs.existsSync(dataDir.BACKUP_DIR)) {
}
sql.dbReady.then(() => {
setInterval(regularBackup, 60 * 60 * 1000);
setInterval(cls.wrap(regularBackup), 60 * 60 * 1000);
// kickoff backup immediately
setTimeout(regularBackup, 1000);
setTimeout(cls.wrap(regularBackup), 1000);
});
module.exports = {

16
src/services/cls.js Normal file
View File

@@ -0,0 +1,16 @@
const clsHooked = require('cls-hooked');
const namespace = clsHooked.createNamespace("trilium");
async function init(callback) {
return await namespace.runAndReturn(callback);
}
function wrap(callback) {
return async () => await init(callback);
}
module.exports = {
init,
wrap,
namespace
};

View File

@@ -5,6 +5,7 @@ const log = require('./log');
const messaging = require('./messaging');
const sync_mutex = require('./sync_mutex');
const utils = require('./utils');
const cls = require('./cls');
async function runCheck(query, errorText, errorList) {
utils.assertArguments(query, errorText, errorList);
@@ -268,10 +269,10 @@ async function runChecks() {
}
sql.dbReady.then(() => {
setInterval(runChecks, 60 * 60 * 1000);
setInterval(cls.wrap(runChecks), 60 * 60 * 1000);
// kickoff backup immediately
setTimeout(runChecks, 10000);
setTimeout(cls.wrap(runChecks), 10000);
});
module.exports = {

View File

@@ -262,16 +262,16 @@ async function loadFile(noteId, newNote, dataKey) {
await protected_session.decryptNote(dataKey, oldNote);
}
newNote.detail.content = oldNote.content;
newNote.content = oldNote.content;
}
async function updateNote(noteId, newNote, dataKey, sourceId) {
if (newNote.detail.type === 'file') {
if (newNote.type === 'file') {
await loadFile(noteId, newNote, dataKey);
}
if (newNote.detail.isProtected) {
await protected_session.encryptNote(dataKey, newNote.detail);
if (newNote.isProtected) {
await protected_session.encryptNote(dataKey, newNote);
}
const labelsMap = await labels.getNoteLabelMap(noteId);
@@ -287,7 +287,7 @@ async function updateNote(noteId, newNote, dataKey, sourceId) {
"SELECT noteRevisionId FROM note_revisions WHERE noteId = ? AND dateModifiedTo >= ?", [noteId, revisionCutoff]);
await sql.doInTransaction(async () => {
const msSinceDateCreated = now.getTime() - utils.parseDateTime(newNote.detail.dateCreated).getTime();
const msSinceDateCreated = now.getTime() - utils.parseDateTime(newNote.dateCreated).getTime();
if (labelsMap.disable_versioning !== 'true'
&& !existingnoteRevisionId
@@ -296,14 +296,14 @@ async function updateNote(noteId, newNote, dataKey, sourceId) {
await saveNoteRevision(noteId, dataKey, sourceId, nowStr);
}
await saveNoteImages(noteId, newNote.detail.content, sourceId);
await saveNoteImages(noteId, newNote.content, sourceId);
await protectNoteRevisions(noteId, dataKey, newNote.detail.isProtected);
await protectNoteRevisions(noteId, dataKey, newNote.isProtected);
await sql.execute("UPDATE notes SET title = ?, content = ?, isProtected = ?, dateModified = ? WHERE noteId = ?", [
newNote.detail.title,
newNote.detail.content,
newNote.detail.isProtected,
newNote.title,
newNote.content,
newNote.isProtected,
nowStr,
noteId]);

View File

@@ -1,5 +1,6 @@
const script = require('./script');
const Repository = require('./repository');
const cls = require('./cls');
const repo = new Repository();
@@ -20,8 +21,8 @@ async function runNotesWithLabel(runAttrValue) {
}
}
setTimeout(() => runNotesWithLabel('backend_startup'), 10 * 1000);
setTimeout(cls.wrap(() => runNotesWithLabel('backend_startup')), 10 * 1000);
setInterval(() => runNotesWithLabel('hourly'), 3600 * 1000);
setInterval(cls.wrap(() => runNotesWithLabel('hourly')), 3600 * 1000);
setInterval(() => runNotesWithLabel('daily'), 24 * 3600 * 1000);
setInterval(cls.wrap(() => runNotesWithLabel('daily'), 24 * 3600 * 1000));

View File

@@ -1,6 +1,7 @@
const utils = require('./utils');
const log = require('./log');
const sql = require('./sql');
const cls = require('./cls');
async function saveSourceId(sourceId) {
await sql.doInTransaction(async () => {
@@ -41,7 +42,7 @@ function isLocalSourceId(srcId) {
const currentSourceId = createSourceId();
// this will also refresh source IDs
sql.dbReady.then(() => saveSourceId(currentSourceId));
sql.dbReady.then(cls.wrap(() => saveSourceId(currentSourceId)));
function getCurrentSourceId() {
return currentSourceId;

View File

@@ -6,6 +6,7 @@ const fs = require('fs');
const sqlite = require('sqlite');
const app_info = require('./app_info');
const resource_dir = require('./resource_dir');
const cls = require('./cls');
async function createConnection() {
return await sqlite.open(dataDir.DOCUMENT_PATH, {Promise});
@@ -15,7 +16,7 @@ const dbConnected = createConnection();
let dbReadyResolve = null;
const dbReady = new Promise((resolve, reject) => {
dbConnected.then(async db => {
dbConnected.then(cls.wrap(async db => {
await execute("PRAGMA foreign_keys = ON");
dbReadyResolve = () => {
@@ -65,7 +66,7 @@ const dbReady = new Promise((resolve, reject) => {
resolve(db);
}
})
}))
.catch(e => {
console.log("Error connecting to DB.", e);
process.exit(1);
@@ -191,6 +192,15 @@ let transactionActive = false;
let transactionPromise = null;
async function doInTransaction(func) {
if (cls.namespace.get('isInTransaction')) {
console.log("Transaction already active");
return await func();
}
else {
console.log("Starting new transaction");
}
while (transactionActive) {
await transactionPromise;
}
@@ -201,6 +211,8 @@ async function doInTransaction(func) {
transactionActive = true;
transactionPromise = new Promise(async (resolve, reject) => {
try {
cls.namespace.set('isInTransaction', true);
await beginTransaction();
ret = await func();
@@ -219,6 +231,9 @@ async function doInTransaction(func) {
reject(e);
}
finally {
cls.namespace.set('isInTransaction', false);
}
});
if (transactionActive) {

View File

@@ -15,6 +15,7 @@ const app_info = require('./app_info');
const messaging = require('./messaging');
const sync_setup = require('./sync_setup');
const sync_mutex = require('./sync_mutex');
const cls = require('./cls');
let proxyToggle = true;
let syncServerCertificate = null;
@@ -347,10 +348,10 @@ sql.dbReady.then(() => {
syncServerCertificate = fs.readFileSync(sync_setup.SYNC_CERT_PATH);
}
setInterval(sync, 60000);
setInterval(cls.wrap(sync), 60000);
// kickoff initial sync immediately
setTimeout(sync, 1000);
setTimeout(cls.wrap(sync), 1000);
}
else {
log.info("Sync server not configured, sync timer not running.")