1
0
mirror of https://github.com/laurent22/joplin.git synced 2024-12-24 10:27:10 +02:00

Core: Fixed potential out-of-sync issue if user cancels while in the middle of delta step

This commit is contained in:
Laurent Cozic 2017-11-21 18:17:50 +00:00
parent 585ccc2b8b
commit c5214b6c44
2 changed files with 41 additions and 6 deletions

View File

@ -606,5 +606,25 @@ describe('Synchronizer', function() {
done(); done();
}); });
it('items should be downloaded again when user cancels in the middle of delta operation', async (done) => {
let folder1 = await Folder.save({ title: "folder1" });
let note1 = await Note.save({ title: "un", is_todo: 1, parent_id: folder1.id });
await synchronizer().start();
await switchClient(2);
synchronizer().debugFlags_ = ['cancelDeltaLoop2'];
let context = await synchronizer().start();
let notes = await Note.all();
expect(notes.length).toBe(0);
synchronizer().debugFlags_ = [];
await synchronizer().start({ context: context });
notes = await Note.all();
expect(notes.length).toBe(1);
done();
});
}); });

View File

@ -22,6 +22,10 @@ class Synchronizer {
this.appType_ = appType; this.appType_ = appType;
this.cancelling_ = false; this.cancelling_ = false;
// Debug flags are used to test certain hard-to-test conditions
// such as cancelling in the middle of a loop.
this.debugFlags_ = [];
this.onProgress_ = function(s) {}; this.onProgress_ = function(s) {};
this.progressReport_ = {}; this.progressReport_ = {};
@ -354,10 +358,11 @@ class Synchronizer {
let context = null; let context = null;
let newDeltaContext = null; let newDeltaContext = null;
let localFoldersToDelete = []; let localFoldersToDelete = [];
let hasCancelled = false;
if (lastContext.delta) context = lastContext.delta; if (lastContext.delta) context = lastContext.delta;
while (true) { while (true) {
if (this.cancelling()) break; if (this.cancelling() || hasCancelled) break;
let listResult = await this.api().delta('', { let listResult = await this.api().delta('', {
context: context, context: context,
@ -372,7 +377,10 @@ class Synchronizer {
let remotes = listResult.items; let remotes = listResult.items;
for (let i = 0; i < remotes.length; i++) { for (let i = 0; i < remotes.length; i++) {
if (this.cancelling()) break; if (this.cancelling() || this.debugFlags_.indexOf('cancelDeltaLoop2') >= 0) {
hasCancelled = true;
break;
}
let remote = remotes[i]; let remote = remotes[i];
if (!BaseItem.isSystemPath(remote.path)) continue; // The delta API might return things like the .sync, .resource or the root folder if (!BaseItem.isSystemPath(remote.path)) continue; // The delta API might return things like the .sync, .resource or the root folder
@ -443,11 +451,18 @@ class Synchronizer {
} }
} }
if (!listResult.hasMore) { // If user has cancelled, don't record the new context (2) so that synchronisation
newDeltaContext = listResult.context; // can start again from the previous context (1) next time. It is ok if some items
break; // have been synced between (1) and (2) because the loop above will handle the same
// items being synced twice as an update. If the local and remote items are indentical
// the update will simply be skipped.
if (!hasCancelled) {
if (!listResult.hasMore) {
newDeltaContext = listResult.context;
break;
}
context = listResult.context;
} }
context = listResult.context;
} }
outputContext.delta = newDeltaContext ? newDeltaContext : lastContext.delta; outputContext.delta = newDeltaContext ? newDeltaContext : lastContext.delta;