diff --git a/src/public/app/menus/tree_context_menu.js b/src/public/app/menus/tree_context_menu.js
index a83c45cb1..c0daf7f4f 100644
--- a/src/public/app/menus/tree_context_menu.js
+++ b/src/public/app/menus/tree_context_menu.js
@@ -67,7 +67,6 @@ export default class TreeContextMenu {
{ title: "Advanced", uiIcon: "bx bx-empty", enabled: true, items: [
{ title: 'Expand subtree ', command: "expandSubtree", uiIcon: "bx bx-expand", enabled: noSelectedNotes },
{ title: 'Collapse subtree ', command: "collapseSubtree", uiIcon: "bx bx-collapse", enabled: noSelectedNotes },
- { title: "Force note sync", command: "forceNoteSync", uiIcon: "bx bx-refresh", enabled: noSelectedNotes },
{ title: 'Sort by ... ', command: "sortChildNotes", uiIcon: "bx bx-empty", enabled: noSelectedNotes && notSearch },
{ title: 'Recent changes in subtree', command: "recentChangesInSubtree", uiIcon: "bx bx-history", enabled: noSelectedNotes },
{ title: 'Convert to attachment', command: "convertNoteToAttachment", uiIcon: "bx bx-empty", enabled: isNotRoot && !isHoisted }
diff --git a/src/public/app/services/sync.js b/src/public/app/services/sync.js
index 1f1f8cc17..6c4f2fc67 100644
--- a/src/public/app/services/sync.js
+++ b/src/public/app/services/sync.js
@@ -18,13 +18,6 @@ async function syncNow(ignoreNotConfigured = false) {
}
}
-async function forceNoteSync(noteId) {
- await server.post(`sync/force-note-sync/${noteId}`);
-
- toastService.showMessage("Note added to sync queue.");
-}
-
export default {
- syncNow,
- forceNoteSync
+ syncNow
};
diff --git a/src/public/app/widgets/note_tree.js b/src/public/app/widgets/note_tree.js
index 750ff46fc..bbbcc9905 100644
--- a/src/public/app/widgets/note_tree.js
+++ b/src/public/app/widgets/note_tree.js
@@ -1564,10 +1564,6 @@ export default class NoteTreeWidget extends NoteContextAwareWidget {
this.triggerCommand("showImportDialog", {noteId: node.data.noteId});
}
- forceNoteSyncCommand({node}) {
- syncService.forceNoteSync(node.data.noteId);
- }
-
editNoteTitleCommand({node}) {
appContext.triggerCommand('focusOnTitle');
}
diff --git a/src/routes/api/login.js b/src/routes/api/login.js
index 6256be1fd..42b78d951 100644
--- a/src/routes/api/login.js
+++ b/src/routes/api/login.js
@@ -3,7 +3,7 @@
const options = require('../../services/options');
const utils = require('../../services/utils');
const dateUtils = require('../../services/date_utils');
-const instanceId = require('../../services/member_id');
+const instanceId = require('../../services/instance_id');
const passwordEncryptionService = require('../../services/encryption/password_encryption');
const protectedSessionService = require('../../services/protected_session');
const appInfo = require('../../services/app_info');
diff --git a/src/routes/api/sync.js b/src/routes/api/sync.js
index 139f668b0..36d2b137a 100644
--- a/src/routes/api/sync.js
+++ b/src/routes/api/sync.js
@@ -9,10 +9,8 @@ const optionService = require('../../services/options');
const contentHashService = require('../../services/content_hash');
const log = require('../../services/log');
const syncOptions = require('../../services/sync_options');
-const dateUtils = require('../../services/date_utils');
const utils = require('../../services/utils');
const ws = require('../../services/ws');
-const becca = require("../../becca/becca");
async function testSync() {
try {
@@ -84,54 +82,14 @@ function forceFullSync() {
syncService.sync();
}
-function forceNoteSync(req) {
- const noteId = req.params.noteId;
- const note = becca.getNote(noteId);
-
- const now = dateUtils.utcNowDateTime();
-
- sql.execute(`UPDATE notes SET utcDateModified = ? WHERE noteId = ?`, [now, noteId]);
- entityChangesService.moveEntityChangeToTop('notes', noteId);
-
- sql.execute(`UPDATE blobs SET utcDateModified = ? WHERE blobId = ?`, [now, note.blobId]);
- entityChangesService.moveEntityChangeToTop('blobs', note.blobId);
-
- for (const branchId of sql.getColumn("SELECT branchId FROM branches WHERE noteId = ?", [noteId])) {
- sql.execute(`UPDATE branches SET utcDateModified = ? WHERE branchId = ?`, [now, branchId]);
-
- entityChangesService.moveEntityChangeToTop('branches', branchId);
- }
-
- for (const attributeId of sql.getColumn("SELECT attributeId FROM attributes WHERE noteId = ?", [noteId])) {
- sql.execute(`UPDATE attributes SET utcDateModified = ? WHERE attributeId = ?`, [now, attributeId]);
-
- entityChangesService.moveEntityChangeToTop('attributes', attributeId);
- }
-
- for (const revisionId of sql.getColumn("SELECT revisionId FROM revisions WHERE noteId = ?", [noteId])) {
- sql.execute(`UPDATE revisions SET utcDateModified = ? WHERE revisionId = ?`, [now, revisionId]);
- entityChangesService.moveEntityChangeToTop('revisions', revisionId);
- }
-
- for (const attachmentId of sql.getColumn("SELECT attachmentId FROM attachments WHERE noteId = ?", [noteId])) {
- sql.execute(`UPDATE attachments SET utcDateModified = ? WHERE attachmentId = ?`, [now, attachmentId]);
- entityChangesService.moveEntityChangeToTop('attachments', attachmentId);
- }
-
- log.info(`Forcing note sync for ${noteId}`);
-
- // not awaiting for the job to finish (will probably take a long time)
- syncService.sync();
-}
-
function getChanged(req) {
const startTime = Date.now();
let lastEntityChangeId = parseInt(req.query.lastEntityChangeId);
- const clientinstanceId = req.query.instanceId;
+ const clientInstanceId = req.query.instanceId;
let filteredEntityChanges = [];
- while (filteredEntityChanges.length === 0) {
+ do {
const entityChanges = sql.getRows(`
SELECT *
FROM entity_changes
@@ -144,20 +102,22 @@ function getChanged(req) {
break;
}
- filteredEntityChanges = entityChanges.filter(ec => ec.instanceId !== clientinstanceId);
+ filteredEntityChanges = entityChanges.filter(ec => ec.instanceId !== clientInstanceId);
if (filteredEntityChanges.length === 0) {
lastEntityChangeId = entityChanges[entityChanges.length - 1].id;
}
- }
+ } while (filteredEntityChanges.length === 0);
const entityChangeRecords = syncService.getEntityChangeRecords(filteredEntityChanges);
if (entityChangeRecords.length > 0) {
lastEntityChangeId = entityChangeRecords[entityChangeRecords.length - 1].entityChange.id;
+
+ log.info(`Returning ${entityChangeRecords.length} entity changes in ${Date.now() - startTime}ms`);
}
- const ret = {
+ return {
entityChanges: entityChangeRecords,
lastEntityChangeId,
outstandingPullCount: sql.getValue(`
@@ -165,14 +125,8 @@ function getChanged(req) {
FROM entity_changes
WHERE isSynced = 1
AND instanceId != ?
- AND id > ?`, [clientinstanceId, lastEntityChangeId])
+ AND id > ?`, [clientInstanceId, lastEntityChangeId])
};
-
- if (ret.entityChanges.length > 0) {
- log.info(`Returning ${ret.entityChanges.length} entity changes in ${Date.now() - startTime}ms`);
- }
-
- return ret;
}
const partialRequests = {};
@@ -194,12 +148,12 @@ function update(req) {
}
if (!partialRequests[requestId]) {
- throw new Error(`Partial request ${requestId}, index ${pageIndex} of ${pageCount} of pages does not have expected record.`);
+ throw new Error(`Partial request ${requestId}, page ${pageIndex + 1} of ${pageCount} of pages does not have expected record.`);
}
partialRequests[requestId].payload += req.body;
- log.info(`Receiving partial request ${requestId}, page index ${pageIndex} out of ${pageCount} pages.`);
+ log.info(`Receiving a partial request ${requestId}, page ${pageIndex + 1} out of ${pageCount} pages.`);
if (pageIndex !== pageCount - 1) {
return;
@@ -212,9 +166,11 @@ function update(req) {
const {entities, instanceId} = body;
- for (const {entityChange, entity} of entities) {
- syncUpdateService.updateEntity(entityChange, entity, instanceId);
- }
+ sql.transactional(() => {
+ for (const {entityChange, entity} of entities) {
+ syncUpdateService.updateEntity(entityChange, entity, instanceId);
+ }
+ });
}
setInterval(() => {
@@ -241,8 +197,7 @@ function queueSector(req) {
}
function checkEntityChanges() {
- const consistencyChecks = require("../../services/consistency_checks");
- consistencyChecks.runEntityChangesChecks();
+ require("../../services/consistency_checks").runEntityChangesChecks();
}
module.exports = {
@@ -251,7 +206,6 @@ module.exports = {
syncNow,
fillEntityChanges,
forceFullSync,
- forceNoteSync,
getChanged,
update,
getStats,
diff --git a/src/routes/routes.js b/src/routes/routes.js
index 1c40c4fd2..a7f540d09 100644
--- a/src/routes/routes.js
+++ b/src/routes/routes.js
@@ -216,7 +216,6 @@ function register(app) {
apiRoute(PST, '/api/sync/now', syncApiRoute.syncNow);
apiRoute(PST, '/api/sync/fill-entity-changes', syncApiRoute.fillEntityChanges);
apiRoute(PST, '/api/sync/force-full-sync', syncApiRoute.forceFullSync);
- apiRoute(PST, '/api/sync/force-note-sync/:noteId', syncApiRoute.forceNoteSync);
route(GET, '/api/sync/check', [auth.checkApiAuth], syncApiRoute.checkSync, apiResultHandler);
route(GET, '/api/sync/changed', [auth.checkApiAuth], syncApiRoute.getChanged, apiResultHandler);
route(PUT, '/api/sync/update', [auth.checkApiAuth], syncApiRoute.update, apiResultHandler);
diff --git a/src/services/consistency_checks.js b/src/services/consistency_checks.js
index 19903c951..512f16325 100644
--- a/src/services/consistency_checks.js
+++ b/src/services/consistency_checks.js
@@ -597,14 +597,10 @@ class ConsistencyChecks {
runEntityChangeChecks(entityName, key) {
this.findAndFixIssues(`
- SELECT
- ${key} as entityId
- FROM
- ${entityName}
- LEFT JOIN entity_changes ON entity_changes.entityName = '${entityName}'
- AND entity_changes.entityId = ${key}
- WHERE
- entity_changes.id IS NULL`,
+ SELECT ${key} as entityId
+ FROM ${entityName}
+ LEFT JOIN entity_changes ec ON ec.entityName = '${entityName}' AND ec.entityId = ${entityName}.${key}
+ WHERE ec.id IS NULL`,
({entityId}) => {
const entityRow = sql.getRow(`SELECT * FROM ${entityName} WHERE ${key} = ?`, [entityId]);
@@ -613,7 +609,7 @@ class ConsistencyChecks {
entityName,
entityId,
hash: utils.randomString(10), // doesn't matter, will force sync, but that's OK
- isErased: !!entityRow.isErased,
+ isErased: false,
utcDateChanged: entityRow.utcDateModified || entityRow.utcDateCreated,
isSynced: entityName !== 'options' || entityRow.isSynced
});
@@ -625,15 +621,13 @@ class ConsistencyChecks {
});
this.findAndFixIssues(`
- SELECT
- id, entityId
- FROM
- entity_changes
- LEFT JOIN ${entityName} ON entityId = ${key}
+ SELECT id, entityId
+ FROM entity_changes
+ LEFT JOIN ${entityName} ON entityId = ${entityName}.${key}
WHERE
entity_changes.isErased = 0
AND entity_changes.entityName = '${entityName}'
- AND ${key} IS NULL`,
+ AND ${entityName}.${key} IS NULL`,
({id, entityId}) => {
if (this.autoFix) {
sql.execute("DELETE FROM entity_changes WHERE entityName = ? AND entityId = ?", [entityName, entityId]);
@@ -645,11 +639,9 @@ class ConsistencyChecks {
});
this.findAndFixIssues(`
- SELECT
- id, entityId
- FROM
- entity_changes
- JOIN ${entityName} ON entityId = ${key}
+ SELECT id, entityId
+ FROM entity_changes
+ JOIN ${entityName} ON entityId = ${entityName}.${key}
WHERE
entity_changes.isErased = 1
AND entity_changes.entityName = '${entityName}'`,
diff --git a/src/services/content_hash.js b/src/services/content_hash.js
index 8eacc18b1..e7c4a5d6f 100644
--- a/src/services/content_hash.js
+++ b/src/services/content_hash.js
@@ -14,7 +14,8 @@ function getEntityHashes() {
const hashRows = sql.getRawRows(`
SELECT entityName,
entityId,
- hash
+ hash,
+ isErased
FROM entity_changes
WHERE isSynced = 1
AND entityName != 'note_reordering'`);
@@ -25,12 +26,17 @@ function getEntityHashes() {
const hashMap = {};
- for (const [entityName, entityId, hash] of hashRows) {
+ for (const [entityName, entityId, hash, isErased] of hashRows) {
const entityHashMap = hashMap[entityName] = hashMap[entityName] || {};
const sector = entityId[0];
- entityHashMap[sector] = (entityHashMap[sector] || "") + hash
+ if (entityName === 'revisions' && sector === '5') {
+ console.log(entityId, hash, isErased);
+ }
+
+ // if the entity is erased, its hash is not updated, so it has to be added extra
+ entityHashMap[sector] = (entityHashMap[sector] || "") + hash + isErased;
}
for (const entityHashMap of Object.values(hashMap)) {
diff --git a/src/services/entity_changes.js b/src/services/entity_changes.js
index 5b98ad9d9..17c01f588 100644
--- a/src/services/entity_changes.js
+++ b/src/services/entity_changes.js
@@ -3,7 +3,7 @@ const dateUtils = require('./date_utils');
const log = require('./log');
const cls = require('./cls');
const utils = require('./utils');
-const instanceId = require('./member_id');
+const instanceId = require('./instance_id');
const becca = require("../becca/becca");
const blobService = require("../services/blob");
@@ -62,8 +62,6 @@ function moveEntityChangeToTop(entityName, entityId) {
}
function addEntityChangesForSector(entityName, sector) {
- const startTime = Date.now();
-
const entityChanges = sql.getRows(`SELECT * FROM entity_changes WHERE entityName = ? AND SUBSTR(entityId, 1, 1) = ?`, [entityName, sector]);
sql.transactional(() => {
@@ -72,7 +70,7 @@ function addEntityChangesForSector(entityName, sector) {
}
});
- log.info(`Added sector ${sector} of '${entityName}' (${entityChanges.length} entities) to sync queue in ${Date.now() - startTime}ms.`);
+ log.info(`Added sector ${sector} of '${entityName}' (${entityChanges.length} entities) to the sync queue.`);
}
function cleanupEntityChangesForMissingEntities(entityName, entityPrimaryKey) {
@@ -103,39 +101,34 @@ function fillEntityChanges(entityName, entityPrimaryKey, condition = '') {
createdCount++;
- let hash;
- let utcDateChanged;
- let isSynced;
+ const ec = {
+ entityName,
+ entityId,
+ isErased: false
+ };
if (entityName === 'blobs') {
const blob = sql.getRow("SELECT blobId, content, utcDateModified FROM blobs WHERE blobId = ?", [entityId]);
- hash = blobService.calculateContentHash(blob);
- utcDateChanged = blob.utcDateModified;
- isSynced = true; // blobs are always synced
+ ec.hash = blobService.calculateContentHash(blob);
+ ec.utcDateChanged = blob.utcDateModified;
+ ec.isSynced = true; // blobs are always synced
} else {
const entity = becca.getEntity(entityName, entityId);
if (entity) {
- hash = entity?.generateHash() || "|deleted";
- utcDateChanged = entity?.getUtcDateChanged() || dateUtils.utcNowDateTime();
- isSynced = entityName !== 'options' || !!entity?.isSynced;
+ ec.hash = entity.generateHash() || "|deleted";
+ ec.utcDateChanged = entity.getUtcDateChanged() || dateUtils.utcNowDateTime();
+ ec.isSynced = entityName !== 'options' || !!entity.isSynced;
} else {
// entity might be null (not present in becca) when it's deleted
// FIXME: hacky, not sure if it might cause some problems
- hash = "deleted";
- utcDateChanged = dateUtils.utcNowDateTime();
- isSynced = true; // deletable (the ones with isDeleted) entities are synced
+ ec.hash = "deleted";
+ ec.utcDateChanged = dateUtils.utcNowDateTime();
+ ec.isSynced = true; // deletable (the ones with isDeleted) entities are synced
}
}
- addEntityChange({
- entityName,
- entityId,
- hash: hash,
- isErased: false,
- utcDateChanged: utcDateChanged,
- isSynced: isSynced
- });
+ addEntityChange(ec);
}
if (createdCount > 0) {
diff --git a/src/services/erase.js b/src/services/erase.js
index ee0b21d23..790a314e5 100644
--- a/src/services/erase.js
+++ b/src/services/erase.js
@@ -37,6 +37,7 @@ function eraseNotes(noteIdsToErase) {
function setEntityChangesAsErased(entityChanges) {
for (const ec of entityChanges) {
ec.isErased = true;
+ ec.utcDateChanged = dateUtils.utcNowDateTime();
entityChangesService.addEntityChange(ec);
}
diff --git a/src/services/member_id.js b/src/services/instance_id.js
similarity index 100%
rename from src/services/member_id.js
rename to src/services/instance_id.js
diff --git a/src/services/revisions.js b/src/services/revisions.js
index b0764eb13..0a61643be 100644
--- a/src/services/revisions.js
+++ b/src/services/revisions.js
@@ -3,6 +3,7 @@
const log = require('./log');
const sql = require('./sql');
const protectedSessionService = require("./protected_session");
+const dateUtils = require("./date_utils");
/**
* @param {BNote} note
@@ -40,7 +41,7 @@ function eraseRevisions(revisionIdsToErase) {
log.info(`Removing note revisions: ${JSON.stringify(revisionIdsToErase)}`);
sql.executeMany(`DELETE FROM revisions WHERE revisionId IN (???)`, revisionIdsToErase);
- sql.executeMany(`UPDATE entity_changes SET isErased = 1 WHERE entityName = 'revisions' AND entityId IN (???)`, revisionIdsToErase);
+ sql.executeMany(`UPDATE entity_changes SET isErased = 1, utcDateChanged = '${dateUtils.utcNowDateTime()}' WHERE entityName = 'revisions' AND entityId IN (???)`, revisionIdsToErase);
}
module.exports = {
diff --git a/src/services/sync.js b/src/services/sync.js
index c2dd9b0f1..cc1b75d5a 100644
--- a/src/services/sync.js
+++ b/src/services/sync.js
@@ -4,7 +4,7 @@ const log = require('./log');
const sql = require('./sql');
const optionService = require('./options');
const utils = require('./utils');
-const instanceId = require('./member_id');
+const instanceId = require('./instance_id');
const dateUtils = require('./date_utils');
const syncUpdateService = require('./sync_update');
const contentHashService = require('./content_hash');
@@ -54,6 +54,7 @@ async function sync() {
});
}
catch (e) {
+ // we're dynamically switching whether we're using proxy or not based on whether we encountered error with the current method
proxyToggle = !proxyToggle;
if (e.message?.includes('ECONNREFUSED') ||
@@ -107,7 +108,7 @@ async function doLogin() {
});
if (resp.instanceId === instanceId) {
- throw new Error(`Sync server has member ID '${resp.instanceId}' which is also local. This usually happens when the sync client is (mis)configured to sync with itself (URL points back to client) instead of the correct sync server.`);
+ throw new Error(`Sync server has instance ID '${resp.instanceId}' which is also local. This usually happens when the sync client is (mis)configured to sync with itself (URL points back to client) instead of the correct sync server.`);
}
syncContext.instanceId = resp.instanceId;
@@ -253,7 +254,7 @@ async function checkContentHash(syncContext) {
const failedChecks = contentHashService.checkContentHashes(resp.entityHashes);
if (failedChecks.length > 0) {
- // before requeuing sectors, make sure the entity changes are correct
+ // before re-queuing sectors, make sure the entity changes are correct
const consistencyChecks = require("./consistency_checks");
consistencyChecks.runEntityChangesChecks();
@@ -350,7 +351,8 @@ function getEntityChangeRecords(entityChanges) {
length += JSON.stringify(record).length;
- if (length > 1000000) {
+ if (length > 1_000_000) {
+ // each sync request/response should have at most ~1 MB.
break;
}
}
diff --git a/src/services/sync_update.js b/src/services/sync_update.js
index 152c1b772..86b2c5b58 100644
--- a/src/services/sync_update.js
+++ b/src/services/sync_update.js
@@ -4,98 +4,83 @@ const entityChangesService = require('./entity_changes');
const eventService = require('./events');
const entityConstructor = require("../becca/entity_constructor");
-function updateEntity(entityChange, entityRow, instanceId) {
- // can be undefined for options with isSynced=false
- if (!entityRow) {
- if (entityChange.isSynced) {
- if (entityChange.isErased) {
- eraseEntity(entityChange, instanceId);
- }
- else {
- log.info(`Encountered synced non-erased entity change without entity: ${JSON.stringify(entityChange)}`);
- }
- }
- else if (entityChange.entityName !== 'options') {
- log.info(`Encountered unsynced non-option entity change without entity: ${JSON.stringify(entityChange)}`);
- }
-
- return;
+function updateEntity(remoteEC, remoteEntityRow, instanceId) {
+ if (!remoteEntityRow && remoteEC.entityName === 'options') {
+ return; // can be undefined for options with isSynced=false
}
- const updated = entityChange.entityName === 'note_reordering'
- ? updateNoteReordering(entityChange, entityRow, instanceId)
- : updateNormalEntity(entityChange, entityRow, instanceId);
+ const updated = remoteEC.entityName === 'note_reordering'
+ ? updateNoteReordering(remoteEC, remoteEntityRow, instanceId)
+ : updateNormalEntity(remoteEC, remoteEntityRow, instanceId);
if (updated) {
- if (entityRow.isDeleted) {
+ if (remoteEntityRow?.isDeleted) {
eventService.emit(eventService.ENTITY_DELETE_SYNCED, {
- entityName: entityChange.entityName,
- entityId: entityChange.entityId
+ entityName: remoteEC.entityName,
+ entityId: remoteEC.entityId
});
}
- else if (!entityChange.isErased) {
+ else if (!remoteEC.isErased) {
eventService.emit(eventService.ENTITY_CHANGE_SYNCED, {
- entityName: entityChange.entityName,
- entityRow
+ entityName: remoteEC.entityName,
+ entityRow: remoteEntityRow
});
}
}
}
-function updateNormalEntity(remoteEntityChange, remoteEntityRow, instanceId) {
- const localEntityChange = sql.getRow(`
- SELECT utcDateChanged, hash, isErased
- FROM entity_changes
- WHERE entityName = ? AND entityId = ?`, [remoteEntityChange.entityName, remoteEntityChange.entityId]);
+function updateNormalEntity(remoteEC, remoteEntityRow, instanceId) {
+ const localEC = sql.getRow(`SELECT * FROM entity_changes WHERE entityName = ? AND entityId = ?`, [remoteEC.entityName, remoteEC.entityId]);
- if (localEntityChange && !localEntityChange.isErased && remoteEntityChange.isErased) {
- sql.transactional(() => {
- const primaryKey = entityConstructor.getEntityFromEntityName(remoteEntityChange.entityName).primaryKeyName;
-
- sql.execute(`DELETE FROM ${remoteEntityChange.entityName} WHERE ${primaryKey} = ?`, remoteEntityChange.entityId);
-
- entityChangesService.addEntityChangeWithInstanceId(remoteEntityChange, instanceId);
- });
+ if (!localEC?.isErased && remoteEC.isErased) {
+ eraseEntity(remoteEC, instanceId);
return true;
+ } else if (localEC?.isErased && !remoteEC.isErased) {
+ // on this side, we can't unerase the entity, so force the entity to be erased on the other side.
+ entityChangesService.addEntityChangeWithInstanceId(localEC, null);
+
+ return false;
}
- if (!localEntityChange
- || localEntityChange.utcDateChanged < remoteEntityChange.utcDateChanged
- || localEntityChange.hash !== remoteEntityChange.hash // sync error, we should still update
+ if (!localEC
+ || localEC.utcDateChanged < remoteEC.utcDateChanged
+ || (localEC.utcDateChanged === remoteEC.utcDateChanged && localEC.hash !== remoteEC.hash) // sync error, we should still update
) {
- if (remoteEntityChange.entityName === 'blobs') {
+ if (remoteEC.entityName === 'blobs' && remoteEntityRow.content !== null) {
// we always use a Buffer object which is different from normal saving - there we use a simple string type for
// "string notes". The problem is that in general, it's not possible to detect whether a blob content
// is string note or note (syncs can arrive out of order)
- remoteEntityRow.content = remoteEntityRow.content === null ? null : Buffer.from(remoteEntityRow.content, 'base64');
+ remoteEntityRow.content = Buffer.from(remoteEntityRow.content, 'base64');
- if (remoteEntityRow.content?.byteLength === 0) {
+ if (remoteEntityRow.content.byteLength === 0) {
// there seems to be a bug which causes empty buffer to be stored as NULL which is then picked up as inconsistency
+ // (possibly not a problem anymore with the newer better-sqlite3)
remoteEntityRow.content = "";
}
}
- sql.transactional(() => {
- sql.replace(remoteEntityChange.entityName, remoteEntityRow);
+ sql.replace(remoteEC.entityName, remoteEntityRow);
- entityChangesService.addEntityChangeWithInstanceId(remoteEntityChange, instanceId);
- });
+ entityChangesService.addEntityChangeWithInstanceId(remoteEC, instanceId);
return true;
+ } else if (localEC.hash !== remoteEC.hash && localEC.utcDateChanged > remoteEC.utcDateChanged) {
+ // the change on our side is newer than on the other side, so the other side should update
+ entityChangesService.addEntityChangeWithInstanceId(localEC, null);
+
+ return false;
}
return false;
}
-function updateNoteReordering(entityChange, entity, instanceId) {
- sql.transactional(() => {
- for (const key in entity) {
- sql.execute("UPDATE branches SET notePosition = ? WHERE branchId = ?", [entity[key], key]);
- }
+function updateNoteReordering(remoteEC, remoteEntityRow, instanceId) {
+ for (const key in remoteEntityRow) {
+ sql.execute("UPDATE branches SET notePosition = ? WHERE branchId = ?", [remoteEntityRow[key], key]);
+ }
- entityChangesService.addEntityChangeWithInstanceId(entityChange, instanceId);
- });
+ entityChangesService.addEntityChangeWithInstanceId(remoteEC, instanceId);
return true;
}
@@ -109,19 +94,17 @@ function eraseEntity(entityChange, instanceId) {
"attributes",
"revisions",
"attachments",
- "blobs",
+ "blobs"
];
if (!entityNames.includes(entityName)) {
- log.error(`Cannot erase entity '${entityName}', id '${entityId}'`);
+ log.error(`Cannot erase entity '${entityName}', id '${entityId}'.`);
return;
}
- const keyName = entityConstructor.getEntityFromEntityName(entityName).primaryKeyName;
+ const primaryKeyName = entityConstructor.getEntityFromEntityName(entityName).primaryKeyName;
- sql.execute(`DELETE FROM ${entityName} WHERE ${keyName} = ?`, [entityId]);
-
- eventService.emit(eventService.ENTITY_DELETE_SYNCED, { entityName, entityId });
+ sql.execute(`DELETE FROM ${entityName} WHERE ${primaryKeyName} = ?`, [entityId]);
entityChangesService.addEntityChangeWithInstanceId(entityChange, instanceId);
}