fix: expect merge conflicts in notebooks

This commit is contained in:
thecodrr
2022-07-13 12:21:00 +05:00
parent 333ffe6ebb
commit 4e34f33f1d
3 changed files with 60 additions and 12 deletions

View File

@@ -259,6 +259,46 @@ test.skip(
60 * 1000 60 * 1000
); );
test.skip(
"issue: new topics on 2 devices are not properly synced",
async () => {
const deviceA = await initializeDevice("deviceA");
const deviceB = await initializeDevice("deviceB");
const id = await deviceA.notebooks.add({ title: "Notebook 1" });
await syncAndWait(deviceA, deviceB, false);
expect(deviceB.notebooks.notebook(id)).toBeDefined();
await deviceA.notebooks.notebook(id).topics.add("Topic 1");
// to create a conflict
await delay(1500);
await deviceB.notebooks.notebook(id).topics.add("Topic 2");
expect(deviceA.notebooks.notebook(id).topics.has("Topic 1")).toBeTruthy();
expect(deviceB.notebooks.notebook(id).topics.has("Topic 2")).toBeTruthy();
await syncAndWait(deviceA, deviceB, false);
await delay(1000);
await syncAndWait(deviceB, deviceB, false);
expect(deviceA.notebooks.notebook(id).topics.has("Topic 1")).toBeTruthy();
expect(deviceB.notebooks.notebook(id).topics.has("Topic 1")).toBeTruthy();
expect(deviceA.notebooks.notebook(id).topics.has("Topic 2")).toBeTruthy();
expect(deviceB.notebooks.notebook(id).topics.has("Topic 2")).toBeTruthy();
await cleanup(deviceA, deviceB);
},
60 * 1000
);
/** /**
* *
* @param {string} id * @param {string} id
@@ -274,11 +314,11 @@ async function initializeDevice(id, capabilities = []) {
const device = new Database(new NodeStorageInterface(), EventSource, FS); const device = new Database(new NodeStorageInterface(), EventSource, FS);
device.host({ device.host({
API_HOST: "http://192.168.43.221:5264", API_HOST: "http://192.168.10.29:5264",
AUTH_HOST: "http://192.168.43.221:8264", AUTH_HOST: "http://192.168.10.29:8264",
SSE_HOST: "http://192.168.43.221:7264", SSE_HOST: "http://192.168.10.29:7264",
ISSUES_HOST: "http://192.168.43.221:2624", ISSUES_HOST: "http://192.168.10.29:2624",
SUBSCRIPTIONS_HOST: "http://192.168.43.221:9264", SUBSCRIPTIONS_HOST: "http://192.168.10.29:9264",
}); });
await device.init(id); await device.init(id);

View File

@@ -20,10 +20,13 @@ class Merger {
set: (item) => this._db.notes.merge(item), set: (item) => this._db.notes.merge(item),
}, },
notebook: { notebook: {
threshold: 1000,
get: (id) => this._db.notebooks.notebook(id), get: (id) => this._db.notebooks.notebook(id),
set: (item) => this._db.notebooks.merge(item), set: (item) => this._db.notebooks.merge(item),
conflict: (_local, remote) => this._db.notebooks.merge(remote),
}, },
content: { content: {
threshold: process.env.NODE_ENV === "test" ? 6 * 1000 : 60 * 1000,
get: (id) => this._db.content.raw(id), get: (id) => this._db.content.raw(id),
set: (item) => this._db.content.add(item), set: (item) => this._db.content.add(item),
conflict: async (local, remote) => { conflict: async (local, remote) => {
@@ -138,7 +141,13 @@ class Merger {
} }
} }
async _mergeItemWithConflicts(remoteItem, get, add, markAsConflicted) { async _mergeItemWithConflicts(
remoteItem,
get,
add,
markAsConflicted,
threshold
) {
remoteItem = await this._deserialize(remoteItem); remoteItem = await this._deserialize(remoteItem);
let localItem = await get(remoteItem.id); let localItem = await get(remoteItem.id);
@@ -149,15 +158,13 @@ class Merger {
const isModified = localItem.dateModified > this._lastSynced; const isModified = localItem.dateModified > this._lastSynced;
if (isModified && !isResolved) { if (isModified && !isResolved) {
// If time difference between local item's edits & remote item's edits // If time difference between local item's edits & remote item's edits
// is less than 1 minute, we shouldn't trigger a merge conflict; instead // is less than threshold, we shouldn't trigger a merge conflict; instead
// we will keep the most recently changed item. // we will keep the most recently changed item.
const timeDiff = const timeDiff =
Math.max(remoteItem.dateModified, localItem.dateModified) - Math.max(remoteItem.dateModified, localItem.dateModified) -
Math.min(remoteItem.dateModified, localItem.dateModified); Math.min(remoteItem.dateModified, localItem.dateModified);
const ONE_MINUTE = if (timeDiff < threshold) {
process.env.NODE_ENV === "test" ? 6 * 1000 : 60 * 1000;
if (timeDiff < ONE_MINUTE) {
if (remoteItem.dateModified > localItem.dateModified) { if (remoteItem.dateModified > localItem.dateModified) {
await add(remoteItem); await add(remoteItem);
} }
@@ -188,7 +195,8 @@ class Merger {
item, item,
definition.get, definition.get,
definition.set, definition.set,
definition.conflict definition.conflict,
definition.threshold
); );
} else if (definition.get && definition.set) { } else if (definition.get && definition.set) {
await this._mergeItem(item, definition.get, definition.set); await this._mergeItem(item, definition.get, definition.set);

View File

@@ -47,7 +47,7 @@ export default class Notebooks extends Collection {
isChanged = true; isChanged = true;
} }
} }
if (isChanged) remoteNotebook.dateModified = Date.now(); // we update the dateEdited so it can be synced back remoteNotebook.remote = !isChanged;
} }
return await this._collection.addItem(remoteNotebook); return await this._collection.addItem(remoteNotebook);
} }