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

Side by Side Diff: components/rappor/log_uploader.cc

Issue 49753002: RAPPOR implementation (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: RapporMetrics StatsTest Created 6 years, 11 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 unified diff | Download patch
OLDNEW
(Empty)
1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "components/rappor/log_uploader.h"
6
7 #include "net/base/load_flags.h"
8 #include "url/gurl.h"
9
10 using base::TimeDelta;
11
12 namespace {
13
14 // The delay, in seconds, between uploading when there are queued logs to send.
15 const int kUnsentLogsIntervalSeconds = 3;
16
17 // When uploading metrics to the server fails, we progressively wait longer and
18 // longer before sending the next log. This backoff process helps reduce load
19 // on a server that is having issues.
20 // The following is the multiplier we use to expand that inter-log duration.
21 const double kBackoffMultiplier = 1.1;
22
23 // The maximum backoff multiplier.
24 const int kMaxBackoffMultiplier = 10;
25
26 // If an upload fails, and the transmission was over this byte count, then we
27 // will discard the log, and not try to retransmit it.
28 const size_t kUploadLogAvoidRetransmitSize = 50000;
29
30 // The maximum number of unsent logs we will keep.
31 // TODO(holte): Limit based on log size instead.
32 const size_t kMaxQueuedLogs = 10;
33
34 } // namespace
35
36 namespace rappor {
37
38 LogUploader::LogUploader(const std::string& server_url,
39 const std::string& mime_type,
40 net::URLRequestContextGetter* request_context)
41 : server_url_(server_url),
42 mime_type_(mime_type),
43 request_context_(request_context),
44 has_callback_pending_(false),
45 upload_interval_(TimeDelta::FromSeconds(kUnsentLogsIntervalSeconds)) {
46 }
47
48 LogUploader::~LogUploader() {}
49
50 void LogUploader::QueueLog(const std::string& log) {
51 queued_logs_.push(log);
52 if (!upload_timer_.IsRunning() && !has_callback_pending_)
53 StartScheduledUpload();
54 }
55
56 void LogUploader::ScheduleNextUpload() {
57 if (upload_timer_.IsRunning() || has_callback_pending_)
58 return;
59
60 upload_timer_.Start(
61 FROM_HERE, upload_interval_, this, &LogUploader::StartScheduledUpload);
62 }
63
64 void LogUploader::StartScheduledUpload() {
65 has_callback_pending_ = true;
66 current_fetch_.reset(
67 net::URLFetcher::Create(GURL(server_url_), net::URLFetcher::POST, this));
68 current_fetch_->SetRequestContext(request_context_.get());
69 current_fetch_->SetUploadData(mime_type_, queued_logs_.front());
70
71 // We already drop cookies server-side, but we might as well strip them out
72 // client-side as well.
73 current_fetch_->SetLoadFlags(net::LOAD_DO_NOT_SAVE_COOKIES |
74 net::LOAD_DO_NOT_SEND_COOKIES);
75 current_fetch_->Start();
76 }
77
78 void LogUploader::OnURLFetchComplete(const net::URLFetcher* source) {
79 // We're not allowed to re-use the existing |URLFetcher|s, so free them here.
80 // Note however that |source| is aliased to the fetcher, so we should be
81 // careful not to delete it too early.
82 DCHECK_EQ(current_fetch_.get(), source);
83 scoped_ptr<net::URLFetcher> s(current_fetch_.Pass());
84
85 int response_code = source->GetResponseCode();
86
87 bool upload_succeeded = response_code == 200;
88
89 // Provide boolean for error recovery (allow us to ignore response_code).
90 bool discard_log = false;
91 const size_t log_size = queued_logs_.front().length();
92 if (!upload_succeeded && log_size > kUploadLogAvoidRetransmitSize) {
93 discard_log = true;
94 } else if (queued_logs_.size() > kMaxQueuedLogs) {
95 discard_log = true;
96 } else if (response_code == 400) {
97 // Bad syntax. Retransmission won't work.
98 discard_log = true;
99 }
100
101 if (upload_succeeded || discard_log)
102 queued_logs_.pop();
103
104 // Error 400 indicates a problem with the log, not with the server, so
105 // don't consider that a sign that the server is in trouble.
106 bool server_is_healthy = upload_succeeded || response_code == 400;
107 UploadFinished(server_is_healthy, !queued_logs_.empty());
108 }
109
110 void LogUploader::UploadFinished(bool server_is_healthy,
111 bool more_logs_remaining) {
112 DCHECK(has_callback_pending_);
113 has_callback_pending_ = false;
114 // If the server is having issues, back off. Otherwise, reset to default.
115 if (!server_is_healthy)
116 BackOffUploadInterval();
117 else
118 upload_interval_ = TimeDelta::FromSeconds(kUnsentLogsIntervalSeconds);
119
120 if (more_logs_remaining)
121 ScheduleNextUpload();
122 }
123
124 void LogUploader::BackOffUploadInterval() {
125 DCHECK_GT(kBackoffMultiplier, 1.0);
126 upload_interval_ = TimeDelta::FromMicroseconds(static_cast<int64>(
127 kBackoffMultiplier * upload_interval_.InMicroseconds()));
128
129 TimeDelta max_interval = kMaxBackoffMultiplier *
130 TimeDelta::FromSeconds(kUnsentLogsIntervalSeconds);
131 if (upload_interval_ > max_interval || upload_interval_.InSeconds() < 0)
132 upload_interval_ = max_interval;
133 }
134
135 } // namespace rappor
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698