From 88088c68db1215bde94b92c6c91e6d7f258b5e28 Mon Sep 17 00:00:00 2001 From: Dan Stillman Date: Sat, 1 Jul 2017 06:26:22 -0400 Subject: [PATCH] Add Sync.Storage.Local.updateSyncStates() This speeds up updating of sync states, particularly after resetting file sync history. --- .../zotero/xpcom/storage/storageLocal.js | 47 +++++++++++++++++-- test/tests/storageLocalTest.js | 22 +++++++++ 2 files changed, 65 insertions(+), 4 deletions(-) diff --git a/chrome/content/zotero/xpcom/storage/storageLocal.js b/chrome/content/zotero/xpcom/storage/storageLocal.js index bc8ba5010..f61d538f8 100644 --- a/chrome/content/zotero/xpcom/storage/storageLocal.js +++ b/chrome/content/zotero/xpcom/storage/storageLocal.js @@ -258,16 +258,26 @@ Zotero.Sync.Storage.Local = { //Zotero.debug("Memory usage: " + memmgr.resident); var changed = false; - for (let i = 0; i < items.length; i++) { - let item = items[i]; + var statesToSet = {}; + for (let item of items) { // TODO: Catch error? let state = yield this._checkForUpdatedFile(item, attachmentData[item.id]); if (state !== false) { - item.attachmentSyncState = state; - yield item.saveTx({ skipAll: true }); + if (!statesToSet[state]) { + statesToSet[state] = []; + } + statesToSet[state].push(item); changed = true; } } + // Update sync states in bulk + if (changed) { + yield Zotero.DB.executeTransaction(function* () { + for (let state in statesToSet) { + yield this.updateSyncStates(statesToSet[state], parseInt(state)); + } + }.bind(this)); + } if (!items.length) { Zotero.debug("No synced files have changed locally"); @@ -499,6 +509,35 @@ Zotero.Sync.Storage.Local = { }, + /** + * @param {Zotero.Item[]} items + * @param {String|Integer} syncState + * @return {Promise} + */ + updateSyncStates: function (items, syncState) { + if (syncState === undefined) { + throw new Error("Sync state not specified"); + } + if (typeof syncState == 'string') { + syncState = this["SYNC_STATE_" + syncState.toUpperCase()]; + } + return Zotero.Utilities.Internal.forEachChunkAsync( + items, + 1000, + async function (chunk) { + chunk.forEach((item) => { + item._attachmentSyncState = syncState; + }); + return Zotero.DB.queryAsync( + "UPDATE itemAttachments SET syncState=? WHERE itemID IN " + + "(" + chunk.map(item => item.id).join(', ') + ")", + syncState + ); + } + ); + }, + + /** * Mark all stored files for upload checking * diff --git a/test/tests/storageLocalTest.js b/test/tests/storageLocalTest.js index 1afdc55f3..904c536fb 100644 --- a/test/tests/storageLocalTest.js +++ b/test/tests/storageLocalTest.js @@ -93,6 +93,28 @@ describe("Zotero.Sync.Storage.Local", function () { }) }) + describe("#updateSyncStates()", function () { + it("should update attachment sync states to 'to_upload'", function* () { + var attachment1 = yield importFileAttachment('test.png'); + attachment1.attachmentSyncState = 'in_sync'; + yield attachment1.saveTx(); + var attachment2 = yield importFileAttachment('test.png'); + attachment2.attachmentSyncState = 'in_sync'; + yield attachment2.saveTx(); + + var local = Zotero.Sync.Storage.Local; + yield local.updateSyncStates([attachment1, attachment2], 'to_upload'); + + for (let attachment of [attachment1, attachment2]) { + assert.strictEqual(attachment.attachmentSyncState, local.SYNC_STATE_TO_UPLOAD); + let state = yield Zotero.DB.valueQueryAsync( + "SELECT syncState FROM itemAttachments WHERE itemID=?", attachment.id + ); + assert.strictEqual(state, local.SYNC_STATE_TO_UPLOAD); + } + }); + }); + describe("#resetAllSyncStates()", function () { it("should reset attachment sync states to 'to_upload'", function* () { var attachment = yield importFileAttachment('test.png');