Skip to content

Commit

Permalink
fix(uploads): fix auto retry after timeout (#1785)
Browse files Browse the repository at this point in the history
* fix(uploads): Add pause state for all parts on error

* fix(uploads): new functions, remove console logs, new unit tests
  • Loading branch information
danubevictoria authored Dec 11, 2019
1 parent cb23f52 commit c3c601d
Show file tree
Hide file tree
Showing 4 changed files with 314 additions and 74 deletions.
58 changes: 53 additions & 5 deletions src/api/uploads/MultiputPart.js
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,10 @@ class MultiputPart extends BaseMultiput {

fileSize: number;

isPaused: boolean; // For resumable uploads. When an error happens, all parts for an upload get paused. This
// is not a separate state because a paused upload transitions to the DIGEST_READY state immediately
// so MultiputUpload can upload the part again.

/**
* [constructor]
*
Expand Down Expand Up @@ -115,6 +119,7 @@ class MultiputPart extends BaseMultiput {
if (this.rangeEnd > fileSize - 1) {
this.rangeEnd = fileSize - 1;
}
this.isPaused = false;

this.onSuccess = onSuccess || noop;
this.onError = onError || noop;
Expand Down Expand Up @@ -148,7 +153,7 @@ class MultiputPart extends BaseMultiput {
* @return {void}
*/
upload = (): void => {
if (this.isDestroyed()) {
if (this.isDestroyedOrPaused()) {
return;
}

Expand Down Expand Up @@ -197,7 +202,7 @@ class MultiputPart extends BaseMultiput {
* @return {void}
*/
uploadSuccessHandler = ({ data }: { data: Object }) => {
if (this.isDestroyed()) {
if (this.isDestroyedOrPaused()) {
return;
}

Expand All @@ -219,7 +224,7 @@ class MultiputPart extends BaseMultiput {
* @return {void}
*/
uploadProgressHandler = (event: ProgressEvent) => {
if (this.isDestroyed()) {
if (this.isDestroyedOrPaused()) {
return;
}

Expand All @@ -237,7 +242,8 @@ class MultiputPart extends BaseMultiput {
* @return {void}
*/
uploadErrorHandler = async (error: Error) => {
if (this.isDestroyed()) {
if (this.isDestroyedOrPaused()) {
// Ignore abort() error by checking this.isPaused
return;
}

Expand Down Expand Up @@ -296,7 +302,7 @@ class MultiputPart extends BaseMultiput {
* @return {Promise}
*/
retryUpload = async (): Promise<any> => {
if (this.isDestroyed()) {
if (this.isDestroyedOrPaused()) {
return;
}

Expand Down Expand Up @@ -339,6 +345,48 @@ class MultiputPart extends BaseMultiput {
this.destroy();
}

/**
* Pauses upload for this Part.
*
* @return {void}
*/
pause(): void {
this.isPaused = true;
this.state = PART_STATE_DIGEST_READY;
this.xhr.abort(); // This calls the error handler.
}

/**
* Unpauses upload for this Part.
*
* @return {void}
*/
unpause(): void {
this.isPaused = false;
this.state = PART_STATE_UPLOADING;
this.retryUpload();
}

/**
* Resets upload for this Part.
*
* @return {void}
*/
reset(): void {
this.numUploadRetriesPerformed = 0;
this.timing = {};
this.uploadedBytes = 0;
}

/**
* Checks if this Part is destroyed or paused
*
* @return {boolean}
*/
isDestroyedOrPaused(): boolean {
return this.isDestroyed() || this.isPaused;
}

/**
* List specified parts
*
Expand Down
39 changes: 22 additions & 17 deletions src/api/uploads/MultiputUpload.js
Original file line number Diff line number Diff line change
Expand Up @@ -488,22 +488,6 @@ class MultiputUpload extends BaseMultiput {
logEvent: session_endpoints.log_event,
};

// Reset uploading process for parts that were in progress when the upload failed
let nextUploadIndex = this.firstUnuploadedPartIndex;
while (this.numPartsUploading > 0) {
const part = this.parts[nextUploadIndex];
if (part && part.state === PART_STATE_UPLOADING) {
part.state = PART_STATE_DIGEST_READY;
part.numUploadRetriesPerformed = 0;
part.timing = {};
part.uploadedBytes = 0;

this.numPartsUploading -= 1;
this.numPartsDigestReady += 1;
}
nextUploadIndex += 1;
}

this.processNextParts();
}

Expand Down Expand Up @@ -655,6 +639,23 @@ class MultiputUpload extends BaseMultiput {
*/
partUploadErrorHandler = (error: Error, eventInfo: string): void => {
this.sessionErrorHandler(error, LOG_EVENT_TYPE_PART_UPLOAD_RETRIES_EXCEEDED, eventInfo);
// Pause the rest of the parts.
// can't cancel parts because cancel destroys the part and parts are only created in createSession call
if (this.isResumableUploadsEnabled) {
// Reset uploading process for parts that were in progress when the upload failed
let nextUploadIndex = this.firstUnuploadedPartIndex;
while (this.numPartsUploading > 0) {
const part = this.parts[nextUploadIndex];
if (part && part.state === PART_STATE_UPLOADING) {
part.reset();
part.pause();

this.numPartsUploading -= 1;
this.numPartsDigestReady += 1;
}
nextUploadIndex += 1;
}
}
};

/**
Expand Down Expand Up @@ -1056,7 +1057,11 @@ class MultiputUpload extends BaseMultiput {
// can get called on retries
this.numPartsDigestReady -= 1;
this.numPartsUploading += 1;
part.upload();
if (part.isPaused) {
part.unpause();
} else {
part.upload();
}
break;
}
}
Expand Down
Loading

0 comments on commit c3c601d

Please sign in to comment.