Remove redundant error handling in Z.Sync.Data.Engine::_uploadObjects()

makeRequest() now retries 5xx errors automatically, so it's not
necessary higher up.
This commit is contained in:
Dan Stillman 2016-05-02 01:33:58 -04:00
parent 4cc6408105
commit a0c7cf9bee

View file

@ -835,141 +835,116 @@ Zotero.Sync.Data.Engine.prototype._uploadObjects = Zotero.Promise.coroutine(func
let results;
let numSuccessful = 0;
try {
({ libraryVersion, results } = yield this.apiClient.uploadObjects(
this.library.libraryType,
this.libraryTypeID,
"POST",
libraryVersion,
objectType,
batch
));
Zotero.debug("===");
Zotero.debug(results);
// Mark successful and unchanged objects as synced with new version,
// and save uploaded JSON to cache
let ids = [];
let toSave = [];
let toCache = [];
for (let state of ['successful', 'unchanged']) {
for (let index in results[state]) {
let current = results[state][index];
// 'successful' includes objects, not keys
let key = state == 'successful' ? current.key : current;
if (key != batch[index].key) {
throw new Error("Key mismatch (" + key + " != " + batch[index].key + ")");
}
let obj = yield objectsClass.getByLibraryAndKeyAsync(
this.libraryID, key, { noCache: true }
)
ids.push(obj.id);
if (state == 'successful') {
// Update local object with saved data if necessary
yield obj.loadAllData();
obj.fromJSON(current.data);
toSave.push(obj);
toCache.push(current);
}
else {
// This won't reflect the actual version of the item on the server, but
// it will guarantee that the item won't be redownloaded unnecessarily
// in the case of a full sync, because the version will be higher than
// whatever version is on the server.
batch[index].version = libraryVersion
toCache.push(batch[index]);
}
numSuccessful++;
// Remove from batch to mark as successful
delete batch[index];
}
}
yield Zotero.Sync.Data.Local.saveCacheObjects(
objectType, this.libraryID, toCache
);
yield Zotero.DB.executeTransaction(function* () {
for (let i = 0; i < toSave.length; i++) {
yield toSave[i].save();
}
this.library.libraryVersion = libraryVersion;
yield this.library.save();
objectsClass.updateVersion(ids, libraryVersion);
objectsClass.updateSynced(ids, true);
}.bind(this));
// Handle failed objects
for (let index in results.failed) {
let { code, message, data } = results.failed[index];
let e = new Error(message);
e.name = "ZoteroObjectUploadError";
e.code = code;
if (data) {
e.data = data;
}
Zotero.logError("Error for " + objectType + " " + batch[index].key + " in "
+ this.library.name + ":\n\n" + e);
({ libraryVersion, results } = yield this.apiClient.uploadObjects(
this.library.libraryType,
this.libraryTypeID,
"POST",
libraryVersion,
objectType,
batch
));
Zotero.debug("===");
Zotero.debug(results);
// Mark successful and unchanged objects as synced with new version,
// and save uploaded JSON to cache
let ids = [];
let toSave = [];
let toCache = [];
for (let state of ['successful', 'unchanged']) {
for (let index in results[state]) {
let current = results[state][index];
// 'successful' includes objects, not keys
let key = state == 'successful' ? current.key : current;
// This shouldn't happen, because the upload request includes a library
// version and should prevent an outdated upload before the object version is
// checked. If it does, we need to do a full sync.
if (e.code == 412) {
return this.UPLOAD_RESULT_OBJECT_CONFLICT;
if (key != batch[index].key) {
throw new Error("Key mismatch (" + key + " != " + batch[index].key + ")");
}
if (this.onError) {
this.onError(e);
let obj = yield objectsClass.getByLibraryAndKeyAsync(
this.libraryID, key, { noCache: true }
)
ids.push(obj.id);
if (state == 'successful') {
// Update local object with saved data if necessary
yield obj.loadAllData();
obj.fromJSON(current.data);
toSave.push(obj);
toCache.push(current);
}
if (this.stopOnError) {
throw new Error(e);
else {
// This won't reflect the actual version of the item on the server, but
// it will guarantee that the item won't be redownloaded unnecessarily
// in the case of a full sync, because the version will be higher than
// whatever version is on the server.
batch[index].version = libraryVersion
toCache.push(batch[index]);
}
batch[index].tries++;
// Mark 400 errors as permanently failed
if (e.code >= 400 && e.code < 500) {
batch[index].failed = true;
}
// 500 errors should stay in queue and be retried
numSuccessful++;
// Remove from batch to mark as successful
delete batch[index];
}
// Add failed objects back to end of queue
var numFailed = 0;
for (let o of batch) {
if (o !== undefined) {
queue.push(o);
// TODO: Clear JSON?
numFailed++;
}
}
Zotero.debug("Failed: " + numFailed, 2);
}
catch (e) {
if (e instanceof Zotero.HTTP.UnexpectedStatusException) {
if (e.status == 412) {
throw e;
}
// On 5xx, delay and retry
if (e.status >= 500 && e.status <= 600) {
if (!failureDelayGenerator) {
// Keep trying for up to an hour
failureDelayGenerator = Zotero.Utilities.Internal.delayGenerator(
Zotero.Sync.Data.failureDelayIntervals, 60 * 60 * 1000
);
}
let keepGoing = yield failureDelayGenerator.next();
if (!keepGoing) {
Zotero.logError("Failed too many times");
throw e;
}
continue;
}
yield Zotero.Sync.Data.Local.saveCacheObjects(
objectType, this.libraryID, toCache
);
yield Zotero.DB.executeTransaction(function* () {
for (let i = 0; i < toSave.length; i++) {
yield toSave[i].save();
}
throw e;
this.library.libraryVersion = libraryVersion;
yield this.library.save();
objectsClass.updateVersion(ids, libraryVersion);
objectsClass.updateSynced(ids, true);
}.bind(this));
// Handle failed objects
for (let index in results.failed) {
let { code, message, data } = results.failed[index];
let e = new Error(message);
e.name = "ZoteroObjectUploadError";
e.code = code;
if (data) {
e.data = data;
}
Zotero.logError("Error for " + objectType + " " + batch[index].key + " in "
+ this.library.name + ":\n\n" + e);
// This shouldn't happen, because the upload request includes a library
// version and should prevent an outdated upload before the object version is
// checked. If it does, we need to do a full sync.
if (e.code == 412) {
return this.UPLOAD_RESULT_OBJECT_CONFLICT;
}
if (this.onError) {
this.onError(e);
}
if (this.stopOnError) {
throw new Error(e);
}
batch[index].tries++;
// Mark 400 errors as permanently failed
if (e.code >= 400 && e.code < 500) {
batch[index].failed = true;
}
// 500 errors should stay in queue and be retried
}
// Add failed objects back to end of queue
var numFailed = 0;
for (let o of batch) {
if (o !== undefined) {
queue.push(o);
// TODO: Clear JSON?
numFailed++;
}
}
Zotero.debug("Failed: " + numFailed, 2);
// If we didn't make any progress, bail
if (!numSuccessful) {
throw new Error("Made no progress during upload -- stopping");