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/dispatcher.h"
16 #include "components/domain_reliability/uploader.h"
17 #include "components/domain_reliability/util.h"
18 #include "net/base/net_errors.h"
19 #include "net/url_request/url_request_context_getter.h"
21 using base::DictionaryValue
;
22 using base::ListValue
;
25 namespace domain_reliability
{
28 typedef std::deque
<DomainReliabilityBeacon
> BeaconDeque
;
29 typedef BeaconDeque::iterator BeaconIterator
;
30 typedef BeaconDeque::const_iterator BeaconConstIterator
;
33 class DomainReliabilityContext::ResourceState
{
35 ResourceState(DomainReliabilityContext
* context
,
36 const DomainReliabilityConfig::Resource
* config
)
39 successful_requests(0),
41 uploading_successful_requests(0),
42 uploading_failed_requests(0) {}
45 // Serializes the resource state into a Value to be included in an upload.
46 // If there is nothing to report (no beacons and all request counters are 0),
47 // returns a scoped_ptr to NULL instead so the resource can be omitted.
48 scoped_ptr
<base::Value
> ToValue(base::TimeTicks upload_time
) const {
49 if (successful_requests
== 0 && failed_requests
== 0)
50 return scoped_ptr
<base::Value
>();
52 DictionaryValue
* resource_value
= new DictionaryValue();
53 resource_value
->SetString("name", config
->name
);
54 resource_value
->SetInteger("successful_requests", successful_requests
);
55 resource_value
->SetInteger("failed_requests", failed_requests
);
57 return scoped_ptr
<Value
>(resource_value
);
60 // Remembers the current state of the resource data when an upload starts.
62 DCHECK_EQ(0u, uploading_successful_requests
);
63 DCHECK_EQ(0u, uploading_failed_requests
);
64 uploading_successful_requests
= successful_requests
;
65 uploading_failed_requests
= failed_requests
;
68 // Uses the state remembered by |MarkUpload| to remove successfully uploaded
69 // data but keep beacons and request counts added after the upload started.
71 successful_requests
-= uploading_successful_requests
;
72 failed_requests
-= uploading_failed_requests
;
73 uploading_successful_requests
= 0;
74 uploading_failed_requests
= 0;
77 void RollbackUpload() {
78 uploading_successful_requests
= 0;
79 uploading_failed_requests
= 0;
82 DomainReliabilityContext
* context
;
83 const DomainReliabilityConfig::Resource
* config
;
85 uint32 successful_requests
;
86 uint32 failed_requests
;
88 // State saved during uploads; if an upload succeeds, these are used to
89 // remove uploaded data from the beacon list and request counters.
90 uint32 uploading_successful_requests
;
91 uint32 uploading_failed_requests
;
94 DISALLOW_COPY_AND_ASSIGN(ResourceState
);
98 const size_t DomainReliabilityContext::kMaxQueuedBeacons
= 150;
100 DomainReliabilityContext::DomainReliabilityContext(
102 const DomainReliabilityScheduler::Params
& scheduler_params
,
103 const std::string
& upload_reporter_string
,
104 const base::TimeTicks
* last_network_change_time
,
105 DomainReliabilityDispatcher
* dispatcher
,
106 DomainReliabilityUploader
* uploader
,
107 scoped_ptr
<const DomainReliabilityConfig
> config
)
108 : config_(config
.Pass()),
110 upload_reporter_string_(upload_reporter_string
),
112 config_
->collectors
.size(),
114 base::Bind(&DomainReliabilityContext::ScheduleUpload
,
115 base::Unretained(this))),
116 dispatcher_(dispatcher
),
118 uploading_beacons_size_(0),
119 last_network_change_time_(last_network_change_time
),
120 weak_factory_(this) {
121 InitializeResourceStates();
124 DomainReliabilityContext::~DomainReliabilityContext() {}
126 void DomainReliabilityContext::OnBeacon(const GURL
& url
,
127 const DomainReliabilityBeacon
& beacon
) {
128 size_t index
= config_
->GetResourceIndexForUrl(url
);
129 if (index
== DomainReliabilityConfig::kInvalidResourceIndex
)
131 DCHECK_GT(states_
.size(), index
);
133 bool success
= (beacon
.status
== "ok");
135 ResourceState
* state
= states_
[index
];
137 ++state
->successful_requests
;
139 ++state
->failed_requests
;
141 bool reported
= false;
142 bool evicted
= false;
143 if (state
->config
->DecideIfShouldReportRequest(success
)) {
144 beacons_
.push_back(beacon
);
145 beacons_
.back().resource
= state
->config
->name
;
146 if (beacons_
.size() > kMaxQueuedBeacons
) {
147 RemoveOldestBeacon();
150 scheduler_
.OnBeaconAdded();
152 UMA_HISTOGRAM_SPARSE_SLOWLY("DomainReliability.ReportedBeaconError",
153 -beacon
.chrome_error
);
154 // TODO(ttuttle): Histogram HTTP response code?
157 UMA_HISTOGRAM_BOOLEAN("DomainReliability.BeaconReported", reported
);
158 UMA_HISTOGRAM_BOOLEAN("DomainReliability.OnBeaconDidEvict", evicted
);
161 void DomainReliabilityContext::ClearBeacons() {
162 ResourceStateVector::iterator it
;
163 for (it
= states_
.begin(); it
!= states_
.end(); ++it
) {
164 ResourceState
* state
= *it
;
165 state
->successful_requests
= 0;
166 state
->failed_requests
= 0;
167 state
->uploading_successful_requests
= 0;
168 state
->uploading_failed_requests
= 0;
171 uploading_beacons_size_
= 0;
174 scoped_ptr
<base::Value
> DomainReliabilityContext::GetWebUIData() const {
175 base::DictionaryValue
* context_value
= new base::DictionaryValue();
177 context_value
->SetString("domain", config().domain
);
178 context_value
->SetInteger("beacon_count", static_cast<int>(beacons_
.size()));
179 context_value
->SetInteger("uploading_beacon_count",
180 static_cast<int>(uploading_beacons_size_
));
181 context_value
->Set("scheduler", scheduler_
.GetWebUIData());
183 return scoped_ptr
<base::Value
>(context_value
);
186 void DomainReliabilityContext::GetQueuedBeaconsForTesting(
187 std::vector
<DomainReliabilityBeacon
>* beacons_out
) const {
188 beacons_out
->assign(beacons_
.begin(), beacons_
.end());
191 void DomainReliabilityContext::GetRequestCountsForTesting(
192 size_t resource_index
,
193 uint32_t* successful_requests_out
,
194 uint32_t* failed_requests_out
) const {
195 DCHECK_NE(DomainReliabilityConfig::kInvalidResourceIndex
, resource_index
);
196 DCHECK_GT(states_
.size(), resource_index
);
198 const ResourceState
& state
= *states_
[resource_index
];
199 *successful_requests_out
= state
.successful_requests
;
200 *failed_requests_out
= state
.failed_requests
;
203 void DomainReliabilityContext::InitializeResourceStates() {
204 ScopedVector
<DomainReliabilityConfig::Resource
>::const_iterator it
;
205 for (it
= config_
->resources
.begin(); it
!= config_
->resources
.end(); ++it
)
206 states_
.push_back(new ResourceState(this, *it
));
209 void DomainReliabilityContext::ScheduleUpload(
210 base::TimeDelta min_delay
,
211 base::TimeDelta max_delay
) {
212 dispatcher_
->ScheduleTask(
214 &DomainReliabilityContext::StartUpload
,
215 weak_factory_
.GetWeakPtr()),
220 void DomainReliabilityContext::StartUpload() {
223 DCHECK(upload_time_
.is_null());
224 upload_time_
= time_
->NowTicks();
225 std::string report_json
;
226 scoped_ptr
<const Value
> report_value(CreateReport(upload_time_
));
227 base::JSONWriter::Write(report_value
.get(), &report_json
);
228 report_value
.reset();
230 size_t collector_index
= scheduler_
.OnUploadStart();
232 uploader_
->UploadReport(
234 config_
->collectors
[collector_index
]->upload_url
,
236 &DomainReliabilityContext::OnUploadComplete
,
237 weak_factory_
.GetWeakPtr()));
239 UMA_HISTOGRAM_SPARSE_SLOWLY("DomainReliability.UploadCollectorIndex",
240 static_cast<int>(collector_index
));
241 if (!last_upload_time_
.is_null()) {
242 UMA_HISTOGRAM_LONG_TIMES("DomainReliability.UploadInterval",
243 upload_time_
- last_upload_time_
);
247 void DomainReliabilityContext::OnUploadComplete(
248 const DomainReliabilityUploader::UploadResult
& result
) {
249 if (result
.is_success())
253 base::TimeTicks first_beacon_time
= scheduler_
.first_beacon_time();
254 scheduler_
.OnUploadComplete(result
);
255 UMA_HISTOGRAM_BOOLEAN("DomainReliability.UploadSuccess",
256 result
.is_success());
257 base::TimeTicks now
= time_
->NowTicks();
258 UMA_HISTOGRAM_LONG_TIMES("DomainReliability.UploadLatency",
259 now
- first_beacon_time
);
260 DCHECK(!upload_time_
.is_null());
261 UMA_HISTOGRAM_MEDIUM_TIMES("DomainReliability.UploadDuration",
263 UMA_HISTOGRAM_LONG_TIMES("DomainReliability.UploadCollectorRetryDelay",
264 scheduler_
.last_collector_retry_delay());
265 last_upload_time_
= upload_time_
;
266 upload_time_
= base::TimeTicks();
269 scoped_ptr
<const Value
> DomainReliabilityContext::CreateReport(
270 base::TimeTicks upload_time
) const {
271 scoped_ptr
<ListValue
> beacons_value(new ListValue());
272 for (BeaconConstIterator it
= beacons_
.begin(); it
!= beacons_
.end(); ++it
)
273 beacons_value
->Append(it
->ToValue(upload_time
, *last_network_change_time_
));
275 scoped_ptr
<ListValue
> resources_value(new ListValue());
276 for (ResourceStateIterator it
= states_
.begin(); it
!= states_
.end(); ++it
) {
277 scoped_ptr
<Value
> resource_report
= (*it
)->ToValue(upload_time
);
279 resources_value
->Append(resource_report
.release());
282 DictionaryValue
* report_value
= new DictionaryValue();
283 if (!config().version
.empty())
284 report_value
->SetString("config_version", config().version
);
285 report_value
->SetString("reporter", upload_reporter_string_
);
286 report_value
->Set("entries", beacons_value
.release());
287 if (!resources_value
->empty())
288 report_value
->Set("resources", resources_value
.release());
290 return scoped_ptr
<const Value
>(report_value
);
293 void DomainReliabilityContext::MarkUpload() {
294 for (ResourceStateIterator it
= states_
.begin(); it
!= states_
.end(); ++it
)
296 DCHECK_EQ(0u, uploading_beacons_size_
);
297 uploading_beacons_size_
= beacons_
.size();
298 DCHECK_NE(0u, uploading_beacons_size_
);
301 void DomainReliabilityContext::CommitUpload() {
302 for (ResourceStateIterator it
= states_
.begin(); it
!= states_
.end(); ++it
)
303 (*it
)->CommitUpload();
304 BeaconIterator begin
= beacons_
.begin();
305 BeaconIterator end
= begin
+ uploading_beacons_size_
;
306 beacons_
.erase(begin
, end
);
307 DCHECK_NE(0u, uploading_beacons_size_
);
308 uploading_beacons_size_
= 0;
311 void DomainReliabilityContext::RollbackUpload() {
312 for (ResourceStateIterator it
= states_
.begin(); it
!= states_
.end(); ++it
)
313 (*it
)->RollbackUpload();
314 DCHECK_NE(0u, uploading_beacons_size_
);
315 uploading_beacons_size_
= 0;
318 void DomainReliabilityContext::RemoveOldestBeacon() {
319 DCHECK(!beacons_
.empty());
321 VLOG(1) << "Beacon queue for " << config().domain
<< " full; "
322 << "removing oldest beacon";
324 beacons_
.pop_front();
326 // If that just removed a beacon counted in uploading_beacons_size_, decrement
328 if (uploading_beacons_size_
> 0)
329 --uploading_beacons_size_
;
332 } // namespace domain_reliability