Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(1368)

Unified Diff: chrome/browser/google_apis/drive_uploader.cc

Issue 12209035: Implement retry flow on DriveUploader. (Closed) Base URL: http://git.chromium.org/chromium/src.git@b148632_wapi_get_upload_status_operation_impl
Patch Set: Created 7 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: chrome/browser/google_apis/drive_uploader.cc
diff --git a/chrome/browser/google_apis/drive_uploader.cc b/chrome/browser/google_apis/drive_uploader.cc
index 9be960f48138b0a7623d3bd2e3bedf337c25b9b2..7d9c89e5cc212cf215076fb123c97edc67363ab5 100644
--- a/chrome/browser/google_apis/drive_uploader.cc
+++ b/chrome/browser/google_apis/drive_uploader.cc
@@ -9,7 +9,9 @@
#include "base/bind.h"
#include "base/callback.h"
#include "base/message_loop.h"
+#include "base/rand_util.h"
#include "base/string_number_conversions.h"
+#include "base/time.h"
#include "chrome/browser/google_apis/drive_service_interface.h"
#include "chrome/browser/google_apis/drive_upload_mode.h"
#include "chrome/browser/google_apis/gdata_wapi_parser.h"
@@ -36,6 +38,29 @@ int64 OpenFileStreamAndGetSizeOnBlockingPool(net::FileStream* file_stream,
return file_stream->Available();
}
+base::TimeDelta GetWaitDuration(int trial_count) {
+ // The waiting duration for the exponential back is
+ // 1st trial: 1sec + rand millisecs
+ // 2nd trial: 2secs + rand millisecs
+ // 3rd trial: 4secs + rand millisecs
+ // 4th trial: 8secs + rand millisecs
+ // :
+ // nth trial: 2^(n-1) + rand millisecs
+ // Please see also
+ // https://developers.google.com/drive/manage-uploads#exp-backoff
+ // for more details.
satorux1 2013/02/07 05:48:32 I thought we had similar code elsewhere. zork@ wha
Zachary Kuznia 2013/02/07 06:26:36 This should call into the scheduler instead. That
hidehiko 2013/02/07 06:53:26 So, all retrying logic will be in DriveScheduler s
+ DCHECK_GT(trial_count, 0);
+
+ // Note: The range for RandInt is inclusive.
+ return base::TimeDelta::FromSeconds(1 << (trial_count - 1))
+ + base::TimeDelta::FromMilliseconds(base::RandInt(0, 9));
+}
+
+// Returns true if the status_code is 5xx SERVER ERROR, otherwise false.
+bool IsHttp5xxServerError(int status_code) {
+ return status_code >= 500 && status_code < 600;
+}
+
} // namespace
namespace google_apis {
@@ -61,9 +86,10 @@ struct DriveUploader::UploadFileInfo {
etag(etag),
completion_callback(callback),
content_length(0),
- next_send_position(0),
file_stream(new net::FileStream(NULL)),
buf(new net::IOBuffer(kUploadChunkSize)),
+ buf_position(0),
+ buf_size(0),
blocking_task_runner(task_runner),
power_save_blocker(content::PowerSaveBlocker::Create(
content::PowerSaveBlocker::kPowerSaveBlockPreventAppSuspension,
@@ -76,8 +102,8 @@ struct DriveUploader::UploadFileInfo {
// Bytes left to upload.
int64 SizeRemaining() const {
- DCHECK(content_length >= next_send_position);
- return content_length - next_send_position;
+ DCHECK(content_length >= buf_position + buf_size);
+ return content_length - (buf_position + buf_size);
}
// Useful for printf debugging.
@@ -120,9 +146,6 @@ struct DriveUploader::UploadFileInfo {
// Header content-Length.
int64 content_length;
- // The start position of the contents to be sent as the next upload chunk.
- int64 next_send_position;
-
// For opening and reading from physical file.
//
// File operations are posted to |blocking_task_runner|, while the ownership
@@ -135,6 +158,16 @@ struct DriveUploader::UploadFileInfo {
// Holds current content to be uploaded.
const scoped_refptr<net::IOBuffer> buf;
+ // The start position of the |buf| in the file.
+ int64 buf_position;
+
+ // The size of available data in |buf|.
+ int64 buf_size;
+
+ // The number of trials for the current buffer data.
+ // This is used to calculate exponential backoff waiting duration.
+ int trial_count;
+
// Runner for net::FileStream tasks.
const scoped_refptr<base::SequencedTaskRunner> blocking_task_runner;
@@ -329,10 +362,27 @@ void DriveUploader::ReadCompletionCallback(
return;
}
- int64 start_position = upload_file_info->next_send_position;
- upload_file_info->next_send_position += bytes_read;
- int64 end_position = upload_file_info->next_send_position;
+ // Update the buffer's position.
+ // First, move the start position of the buffer by previous buffer size,
+ // and then store the current buffer size.
+ upload_file_info->buf_position += upload_file_info->buf_size;
+ upload_file_info->buf_size = bytes_read;
+
+ // Try to send all the data in the buffer.
+ int64 start_position = upload_file_info->buf_position;
+ ResumeUpload(upload_file_info.Pass(), start_position);
+}
+void DriveUploader::ResumeUpload(scoped_ptr<UploadFileInfo> upload_file_info,
+ int64 start_position) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ int64 end_position =
+ upload_file_info->buf_position + upload_file_info->buf_size;
+ DCHECK_GE(start_position, upload_file_info->buf_position);
+ DCHECK_LE(start_position, end_position);
+ int64 buffer_offset = start_position - upload_file_info->buf_position;
+
+ // Try to send all the data in the buffer.
UploadFileInfo* info_ptr = upload_file_info.get();
drive_service_->ResumeUpload(
ResumeUploadParams(info_ptr->upload_mode,
@@ -341,6 +391,7 @@ void DriveUploader::ReadCompletionCallback(
info_ptr->content_length,
info_ptr->content_type,
info_ptr->buf,
+ buffer_offset,
info_ptr->upload_location,
info_ptr->drive_path),
base::Bind(&DriveUploader::OnUploadRangeResponseReceived,
@@ -374,19 +425,30 @@ void DriveUploader::OnUploadRangeResponseReceived(
return;
}
+ if (IsHttp5xxServerError(response.code)) {
+ // The upload is interrupted. So retry with after waiting for a short
+ // period.
+ ResumeInterruptedUpload(upload_file_info.Pass());
+ return;
+ }
+
// If code is 308 (RESUME_INCOMPLETE) and range_received is what has been
// previously uploaded (i.e. = upload_file_info->end_position), proceed to
// upload the next chunk.
if (response.code != HTTP_RESUME_INCOMPLETE ||
response.start_position_received != 0 ||
- response.end_position_received != upload_file_info->next_send_position) {
+ response.end_position_received < upload_file_info->buf_position ||
+ response.end_position_received >
+ upload_file_info->buf_position + upload_file_info->buf_size) {
// TODO(achuith): Handle error cases, e.g.
// - when previously uploaded data wasn't received by Google Docs server,
// i.e. when end_position_received < upload_file_info->end_position
LOG(ERROR) << "UploadNextChunk http code=" << response.code
<< ", start_position_received=" << response.start_position_received
<< ", end_position_received=" << response.end_position_received
- << ", expected end range=" << upload_file_info->next_send_position;
+ << ", sending buffer = [" << upload_file_info->buf_position
+ << ":" << upload_file_info->buf_position + upload_file_info->buf_size
+ << ")";
UploadFailed(upload_file_info.Pass(),
response.code == HTTP_FORBIDDEN ?
DRIVE_UPLOAD_ERROR_NO_SPACE : DRIVE_UPLOAD_ERROR_ABORT);
@@ -398,7 +460,56 @@ void DriveUploader::OnUploadRangeResponseReceived(
<< " for [" << upload_file_info->title << "]";
// Continue uploading.
- UploadNextChunk(upload_file_info.Pass());
+ if (response.end_position_received <
+ upload_file_info->buf_position + upload_file_info->buf_size) {
+ // There is the remaining data in the buffer, so resend it.
+ // PostTask is necessary here because we have to finish the callback
+ // before calling ResumeUpload due to the implementation of
+ // OperationRegistry. (http://crbug.com/134814)
+ MessageLoop::current()->PostTask(
+ FROM_HERE,
+ base::Bind(&DriveUploader::ResumeUpload,
+ weak_ptr_factory_.GetWeakPtr(),
+ base::Passed(&upload_file_info),
+ response.end_position_received));
+ } else {
+ // The whole data in the current buffer is successfully sent.
+ // Send the next chunk.
+ UploadNextChunk(upload_file_info.Pass());
+ }
+}
+
+void DriveUploader::ResumeInterruptedUpload(
+ scoped_ptr<UploadFileInfo> upload_file_info) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+
+ // Calculate the waiting duration based on the number of trial.
+ // See GetWaitDuration for more details.
+ ++upload_file_info->trial_count;
+ base::TimeDelta wait_duration =
+ GetWaitDuration(upload_file_info->trial_count);
+
+ MessageLoop::current()->PostDelayedTask(
+ FROM_HERE,
+ base::Bind(&DriveUploader::ResumeInterruptedUploadAfterWait,
+ weak_ptr_factory_.GetWeakPtr(),
+ base::Passed(&upload_file_info)),
+ wait_duration);
+}
+
+void DriveUploader::ResumeInterruptedUploadAfterWait(
+ scoped_ptr<UploadFileInfo> upload_file_info) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+
+ UploadFileInfo* info_ptr = upload_file_info.get();
+ drive_service_->GetUploadStatus(
+ info_ptr->upload_mode,
+ info_ptr->drive_path,
+ info_ptr->upload_location,
+ info_ptr->content_length,
+ base::Bind(&DriveUploader::OnUploadRangeResponseReceived,
+ weak_ptr_factory_.GetWeakPtr(),
+ base::Passed(&upload_file_info)));
}
void DriveUploader::UploadFailed(scoped_ptr<UploadFileInfo> upload_file_info,

Powered by Google App Engine
This is Rietveld 408576698