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.
5 #include "components/domain_reliability/context.h"
10 #include "base/json/json_writer.h"
11 #include "base/logging.h"
12 #include "base/metrics/histogram.h"
13 #include "base/metrics/sparse_histogram.h"
14 #include "base/values.h"
15 #include "components/domain_reliability/beacon.h"
16 #include "components/domain_reliability/dispatcher.h"
17 #include "components/domain_reliability/uploader.h"
18 #include "components/domain_reliability/util.h"
19 #include "net/base/net_errors.h"
20 #include "net/url_request/url_request_context_getter.h"
22 using base::DictionaryValue
;
23 using base::ListValue
;
26 namespace domain_reliability
{
29 typedef std::deque
<DomainReliabilityBeacon
> BeaconDeque
;
30 typedef BeaconDeque::iterator BeaconIterator
;
31 typedef BeaconDeque::const_iterator BeaconConstIterator
;
34 class DomainReliabilityContext::ResourceState
{
36 ResourceState(DomainReliabilityContext
* context
,
37 const DomainReliabilityConfig::Resource
* config
)
40 successful_requests(0),
42 uploading_beacons_size(0),
43 uploading_successful_requests(0),
44 uploading_failed_requests(0) {}
47 // Serializes the resource state into a Value to be included in an upload.
48 // If there is nothing to report (no beacons and all request counters are 0),
49 // returns a scoped_ptr to NULL instead so the resource can be omitted.
50 scoped_ptr
<base::Value
> ToValue(base::TimeTicks upload_time
) const {
51 if (beacons
.empty() && successful_requests
== 0 && failed_requests
== 0)
52 return scoped_ptr
<base::Value
>();
54 ListValue
* beacons_value
= new ListValue();
55 for (BeaconConstIterator it
= beacons
.begin(); it
!= beacons
.end(); ++it
)
56 beacons_value
->Append(it
->ToValue(upload_time
));
58 DictionaryValue
* resource_value
= new DictionaryValue();
59 resource_value
->SetString("resource_name", config
->name
);
60 resource_value
->SetInteger("successful_requests", successful_requests
);
61 resource_value
->SetInteger("failed_requests", failed_requests
);
62 resource_value
->Set("beacons", beacons_value
);
64 return scoped_ptr
<Value
>(resource_value
);
67 // Remembers the current state of the resource data when an upload starts.
69 uploading_beacons_size
= beacons
.size();
70 uploading_successful_requests
= successful_requests
;
71 uploading_failed_requests
= failed_requests
;
74 // Uses the state remembered by |MarkUpload| to remove successfully uploaded
75 // data but keep beacons and request counts added after the upload started.
77 BeaconIterator begin
= beacons
.begin();
78 BeaconIterator end
= begin
+ uploading_beacons_size
;
79 beacons
.erase(begin
, end
);
80 successful_requests
-= uploading_successful_requests
;
81 failed_requests
-= uploading_failed_requests
;
84 // Gets the start time of the oldest beacon, if there are any. Returns true
85 // and sets |oldest_start_out| if so; otherwise, returns false.
86 bool GetOldestBeaconStart(base::TimeTicks
* oldest_start_out
) const {
89 *oldest_start_out
= beacons
[0].start_time
;
93 // Removes the oldest beacon. DCHECKs if there isn't one.
94 void RemoveOldestBeacon() {
95 DCHECK(!beacons
.empty());
96 beacons
.erase(beacons
.begin());
97 // If that just removed a beacon counted in uploading_beacons_size,
100 if (uploading_beacons_size
> 0)
101 --uploading_beacons_size
;
104 DomainReliabilityContext
* context
;
105 const DomainReliabilityConfig::Resource
* config
;
107 std::deque
<DomainReliabilityBeacon
> beacons
;
108 uint32 successful_requests
;
109 uint32 failed_requests
;
111 // State saved during uploads; if an upload succeeds, these are used to
112 // remove uploaded data from the beacon list and request counters.
113 size_t uploading_beacons_size
;
114 uint32 uploading_successful_requests
;
115 uint32 uploading_failed_requests
;
118 DISALLOW_COPY_AND_ASSIGN(ResourceState
);
122 const size_t DomainReliabilityContext::kMaxQueuedBeacons
= 150;
124 DomainReliabilityContext::DomainReliabilityContext(
126 const DomainReliabilityScheduler::Params
& scheduler_params
,
127 const std::string
& upload_reporter_string
,
128 DomainReliabilityDispatcher
* dispatcher
,
129 DomainReliabilityUploader
* uploader
,
130 scoped_ptr
<const DomainReliabilityConfig
> config
)
131 : config_(config
.Pass()),
133 upload_reporter_string_(upload_reporter_string
),
135 config_
->collectors
.size(),
137 base::Bind(&DomainReliabilityContext::ScheduleUpload
,
138 base::Unretained(this))),
139 dispatcher_(dispatcher
),
142 uploading_beacon_count_(0),
143 weak_factory_(this) {
144 InitializeResourceStates();
147 DomainReliabilityContext::~DomainReliabilityContext() {}
149 void DomainReliabilityContext::OnBeacon(const GURL
& url
,
150 const DomainReliabilityBeacon
& beacon
) {
151 size_t index
= config_
->GetResourceIndexForUrl(url
);
152 if (index
== DomainReliabilityConfig::kInvalidResourceIndex
)
154 DCHECK_GT(states_
.size(), index
);
156 bool success
= (beacon
.status
== "ok");
158 ResourceState
* state
= states_
[index
];
160 ++state
->successful_requests
;
162 ++state
->failed_requests
;
164 bool reported
= false;
165 bool evicted
= false;
166 if (state
->config
->DecideIfShouldReportRequest(success
)) {
167 state
->beacons
.push_back(beacon
);
169 if (beacon_count_
> kMaxQueuedBeacons
) {
170 RemoveOldestBeacon();
173 scheduler_
.OnBeaconAdded();
175 UMA_HISTOGRAM_SPARSE_SLOWLY("DomainReliability.ReportedBeaconError",
176 -beacon
.chrome_error
);
177 // TODO(ttuttle): Histogram HTTP response code?
180 UMA_HISTOGRAM_BOOLEAN("DomainReliability.BeaconReported", reported
);
181 UMA_HISTOGRAM_BOOLEAN("DomainReliability.OnBeaconDidEvict", evicted
);
184 void DomainReliabilityContext::ClearBeacons() {
185 ResourceStateVector::iterator it
;
186 for (it
= states_
.begin(); it
!= states_
.end(); ++it
) {
187 ResourceState
* state
= *it
;
188 state
->beacons
.clear();
189 state
->successful_requests
= 0;
190 state
->failed_requests
= 0;
191 state
->uploading_beacons_size
= 0;
192 state
->uploading_successful_requests
= 0;
193 state
->uploading_failed_requests
= 0;
196 uploading_beacon_count_
= 0;
199 scoped_ptr
<base::Value
> DomainReliabilityContext::GetWebUIData() const {
200 base::DictionaryValue
* context_value
= new base::DictionaryValue();
202 context_value
->SetString("domain", config().domain
);
203 context_value
->SetInteger("beacon_count", static_cast<int>(beacon_count_
));
204 context_value
->SetInteger("uploading_beacon_count",
205 static_cast<int>(uploading_beacon_count_
));
206 context_value
->Set("scheduler", scheduler_
.GetWebUIData());
208 return scoped_ptr
<base::Value
>(context_value
);
211 void DomainReliabilityContext::GetQueuedDataForTesting(
212 size_t resource_index
,
213 std::vector
<DomainReliabilityBeacon
>* beacons_out
,
214 uint32
* successful_requests_out
,
215 uint32
* failed_requests_out
) const {
216 DCHECK_NE(DomainReliabilityConfig::kInvalidResourceIndex
, resource_index
);
217 DCHECK_GT(states_
.size(), resource_index
);
218 const ResourceState
& state
= *states_
[resource_index
];
220 beacons_out
->assign(state
.beacons
.begin(), state
.beacons
.end());
221 if (successful_requests_out
)
222 *successful_requests_out
= state
.successful_requests
;
223 if (failed_requests_out
)
224 *failed_requests_out
= state
.failed_requests
;
227 void DomainReliabilityContext::InitializeResourceStates() {
228 ScopedVector
<DomainReliabilityConfig::Resource
>::const_iterator it
;
229 for (it
= config_
->resources
.begin(); it
!= config_
->resources
.end(); ++it
)
230 states_
.push_back(new ResourceState(this, *it
));
233 void DomainReliabilityContext::ScheduleUpload(
234 base::TimeDelta min_delay
,
235 base::TimeDelta max_delay
) {
236 dispatcher_
->ScheduleTask(
238 &DomainReliabilityContext::StartUpload
,
239 weak_factory_
.GetWeakPtr()),
244 void DomainReliabilityContext::StartUpload() {
247 DCHECK(upload_time_
.is_null());
248 upload_time_
= time_
->NowTicks();
249 std::string report_json
;
250 scoped_ptr
<const Value
> report_value(CreateReport(upload_time_
));
251 base::JSONWriter::Write(report_value
.get(), &report_json
);
252 report_value
.reset();
254 size_t collector_index
= scheduler_
.OnUploadStart();
256 uploader_
->UploadReport(
258 config_
->collectors
[collector_index
]->upload_url
,
260 &DomainReliabilityContext::OnUploadComplete
,
261 weak_factory_
.GetWeakPtr()));
263 UMA_HISTOGRAM_BOOLEAN("DomainReliability.UploadFailover",
264 collector_index
> 0);
265 if (!last_upload_time_
.is_null()) {
266 UMA_HISTOGRAM_LONG_TIMES("DomainReliability.UploadInterval",
267 upload_time_
- last_upload_time_
);
271 void DomainReliabilityContext::OnUploadComplete(bool success
) {
274 scheduler_
.OnUploadComplete(success
);
275 UMA_HISTOGRAM_BOOLEAN("DomainReliability.UploadSuccess", success
);
276 DCHECK(!upload_time_
.is_null());
277 UMA_HISTOGRAM_MEDIUM_TIMES("DomainReliability.UploadDuration",
278 time_
->NowTicks() - upload_time_
);
279 last_upload_time_
= upload_time_
;
280 upload_time_
= base::TimeTicks();
283 scoped_ptr
<const Value
> DomainReliabilityContext::CreateReport(
284 base::TimeTicks upload_time
) const {
285 ListValue
* resources_value
= new ListValue();
286 for (ResourceStateIterator it
= states_
.begin(); it
!= states_
.end(); ++it
) {
287 scoped_ptr
<Value
> resource_report
= (*it
)->ToValue(upload_time
);
289 resources_value
->Append(resource_report
.release());
292 DictionaryValue
* report_value
= new DictionaryValue();
293 report_value
->SetString("config_version", config().version
);
294 report_value
->SetString("reporter", upload_reporter_string_
);
295 report_value
->Set("resource_reports", resources_value
);
297 return scoped_ptr
<const Value
>(report_value
);
300 void DomainReliabilityContext::MarkUpload() {
301 for (ResourceStateIterator it
= states_
.begin(); it
!= states_
.end(); ++it
)
303 uploading_beacon_count_
= beacon_count_
;
306 void DomainReliabilityContext::CommitUpload() {
307 for (ResourceStateIterator it
= states_
.begin(); it
!= states_
.end(); ++it
)
308 (*it
)->CommitUpload();
309 beacon_count_
-= uploading_beacon_count_
;
312 void DomainReliabilityContext::RemoveOldestBeacon() {
313 DCHECK_LT(0u, beacon_count_
);
315 base::TimeTicks min_time
;
316 ResourceState
* min_resource
= NULL
;
317 for (ResourceStateIterator it
= states_
.begin(); it
!= states_
.end(); ++it
) {
318 base::TimeTicks oldest
;
319 if ((*it
)->GetOldestBeaconStart(&oldest
)) {
320 if (!min_resource
|| oldest
< min_time
) {
326 DCHECK(min_resource
);
328 VLOG(1) << "Beacon queue for " << config().domain
<< " full; "
329 << "removing oldest beacon from " << min_resource
->config
->name
;
331 min_resource
->RemoveOldestBeacon();
333 // If that just removed a beacon counted in uploading_beacon_count_, decrement
335 if (uploading_beacon_count_
> 0)
336 --uploading_beacon_count_
;
339 } // namespace domain_reliability