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

Side by Side Diff: components/precache/core/precache_fetcher.cc

Issue 1961153003: Add pause/resume functionality to precache (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: test fix Created 4 years, 6 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
1 // Copyright 2013 The Chromium Authors. All rights reserved. 1 // Copyright 2013 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "components/precache/core/precache_fetcher.h" 5 #include "components/precache/core/precache_fetcher.h"
6 6
7 #include <algorithm> 7 #include <algorithm>
8 #include <limits> 8 #include <limits>
9 #include <string> 9 #include <string>
10 #include <utility> 10 #include <utility>
11 #include <vector> 11 #include <vector>
12 12
13 #include "base/bind.h" 13 #include "base/bind.h"
14 #include "base/bind_helpers.h"
14 #include "base/callback.h" 15 #include "base/callback.h"
15 #include "base/command_line.h" 16 #include "base/command_line.h"
16 #include "base/compiler_specific.h" 17 #include "base/compiler_specific.h"
17 #include "base/containers/hash_tables.h" 18 #include "base/containers/hash_tables.h"
19 #include "base/location.h"
18 #include "base/logging.h" 20 #include "base/logging.h"
19 #include "base/memory/ptr_util.h" 21 #include "base/memory/ptr_util.h"
22 #include "base/memory/ref_counted.h"
20 #include "base/metrics/histogram_macros.h" 23 #include "base/metrics/histogram_macros.h"
21 #include "components/precache/core/precache_switches.h" 24 #include "components/precache/core/precache_switches.h"
22 #include "components/precache/core/proto/precache.pb.h" 25 #include "components/precache/core/proto/precache.pb.h"
26 #include "components/precache/core/proto/unfinished_work.pb.h"
23 #include "net/base/completion_callback.h" 27 #include "net/base/completion_callback.h"
24 #include "net/base/escape.h" 28 #include "net/base/escape.h"
25 #include "net/base/io_buffer.h" 29 #include "net/base/io_buffer.h"
26 #include "net/base/load_flags.h" 30 #include "net/base/load_flags.h"
27 #include "net/base/net_errors.h" 31 #include "net/base/net_errors.h"
28 #include "net/http/http_response_headers.h" 32 #include "net/http/http_response_headers.h"
29 #include "net/url_request/url_fetcher_response_writer.h" 33 #include "net/url_request/url_fetcher_response_writer.h"
30 #include "net/url_request/url_request_context_getter.h" 34 #include "net/url_request/url_request_context_getter.h"
31 #include "net/url_request/url_request_status.h" 35 #include "net/url_request/url_request_status.h"
32 36
(...skipping 216 matching lines...) Expand 10 before | Expand all | Expand 10 after
249 // If any of: 253 // If any of:
250 // - The request was for a config or manifest. 254 // - The request was for a config or manifest.
251 // - The resource was a cache hit without validators. 255 // - The resource was a cache hit without validators.
252 // - The response came from the network. 256 // - The response came from the network.
253 // Then Fetcher is done with this URL and can return control to the caller. 257 // Then Fetcher is done with this URL and can return control to the caller.
254 response_bytes_ = source->GetReceivedResponseContentLength(); 258 response_bytes_ = source->GetReceivedResponseContentLength();
255 network_response_bytes_ = source->GetTotalReceivedBytes(); 259 network_response_bytes_ = source->GetTotalReceivedBytes();
256 callback_.Run(*this); 260 callback_.Run(*this);
257 } 261 }
258 262
263 // static
264 void PrecacheFetcher::RecordCompletionStatistics(
265 const PrecacheUnfinishedWork& unfinished_work,
266 size_t remaining_manifest_urls_to_fetch,
267 size_t remaining_resource_urls_to_fetch) {
268 // These may be unset in tests.
269 if (!unfinished_work.has_start_time())
270 return;
271 base::TimeDelta time_to_fetch =
272 base::Time::Now() -
273 base::Time::FromInternalValue(unfinished_work.start_time());
274 UMA_HISTOGRAM_CUSTOM_TIMES("Precache.Fetch.TimeToComplete", time_to_fetch,
275 base::TimeDelta::FromSeconds(1),
276 base::TimeDelta::FromHours(4), 50);
277
278 // Number of manifests for which we have downloaded all resources.
279 int manifests_completed =
280 unfinished_work.num_manifest_urls() - remaining_manifest_urls_to_fetch;
281
282 // If there are resource URLs left to fetch, the last manifest is not yet
283 // completed.
284 if (remaining_resource_urls_to_fetch > 0)
285 --manifests_completed;
286
287 DCHECK_GE(manifests_completed, 0);
288 int percent_completed = unfinished_work.num_manifest_urls() == 0
289 ? 0
290 : (static_cast<double>(manifests_completed) /
291 unfinished_work.num_manifest_urls() * 100);
292
293 UMA_HISTOGRAM_PERCENTAGE("Precache.Fetch.PercentCompleted",
294 percent_completed);
295 UMA_HISTOGRAM_CUSTOM_COUNTS("Precache.Fetch.ResponseBytes.Total",
296 unfinished_work.total_bytes(),
297 1, kMaxResponseBytes, 100);
298 UMA_HISTOGRAM_CUSTOM_COUNTS("Precache.Fetch.ResponseBytes.Network",
299 unfinished_work.network_bytes(),
300 1, kMaxResponseBytes,
301 100);
302 }
303
259 PrecacheFetcher::PrecacheFetcher( 304 PrecacheFetcher::PrecacheFetcher(
260 const std::vector<std::string>& starting_hosts,
261 net::URLRequestContextGetter* request_context, 305 net::URLRequestContextGetter* request_context,
262 const GURL& config_url, 306 const GURL& config_url,
263 const std::string& manifest_url_prefix, 307 const std::string& manifest_url_prefix,
308 std::unique_ptr<PrecacheUnfinishedWork> unfinished_work,
264 PrecacheFetcher::PrecacheDelegate* precache_delegate) 309 PrecacheFetcher::PrecacheDelegate* precache_delegate)
265 : starting_hosts_(starting_hosts), 310 : request_context_(request_context),
266 request_context_(request_context),
267 config_url_(config_url), 311 config_url_(config_url),
268 manifest_url_prefix_(manifest_url_prefix), 312 manifest_url_prefix_(manifest_url_prefix),
269 precache_delegate_(precache_delegate), 313 precache_delegate_(precache_delegate),
270 config_(new PrecacheConfigurationSettings),
271 total_response_bytes_(0),
272 network_response_bytes_(0),
273 num_manifest_urls_to_fetch_(0),
274 pool_(kMaxParallelFetches) { 314 pool_(kMaxParallelFetches) {
275 DCHECK(request_context_.get()); // Request context must be non-NULL. 315 DCHECK(request_context_.get()); // Request context must be non-NULL.
276 DCHECK(precache_delegate_); // Precache delegate must be non-NULL. 316 DCHECK(precache_delegate_); // Precache delegate must be non-NULL.
277 317
278 DCHECK_NE(GURL(), GetDefaultConfigURL()) 318 DCHECK_NE(GURL(), GetDefaultConfigURL())
279 << "Could not determine the precache config settings URL."; 319 << "Could not determine the precache config settings URL.";
280 DCHECK_NE(std::string(), GetDefaultManifestURLPrefix()) 320 DCHECK_NE(std::string(), GetDefaultManifestURLPrefix())
281 << "Could not determine the default precache manifest URL prefix."; 321 << "Could not determine the default precache manifest URL prefix.";
322 DCHECK(unfinished_work);
323
324 // Copy manifests and resources to member variables as a convenience.
325 // TODO(bengr): Consider accessing these directly from the proto.
326 for (const auto& manifest : unfinished_work->manifest()) {
327 if (manifest.has_url())
328 manifest_urls_to_fetch_.push_back(GURL(manifest.url()));
329 }
330 for (const auto& resource : unfinished_work->resource()) {
331 if (resource.has_url())
332 resource_urls_to_fetch_.push_back(GURL(resource.url()));
333 }
334 unfinished_work_ = std::move(unfinished_work);
282 } 335 }
283 336
284 PrecacheFetcher::~PrecacheFetcher() { 337 PrecacheFetcher::~PrecacheFetcher() {
285 base::TimeDelta time_to_fetch = base::TimeTicks::Now() - start_time_; 338 }
286 UMA_HISTOGRAM_CUSTOM_TIMES("Precache.Fetch.TimeToComplete", time_to_fetch,
287 base::TimeDelta::FromSeconds(1),
288 base::TimeDelta::FromHours(4), 50);
289 339
290 // Number of manifests for which we have downloaded all resources. 340 std::unique_ptr<PrecacheUnfinishedWork> PrecacheFetcher::CancelPrecaching() {
291 int manifests_completed = 341 unfinished_work_->clear_manifest();
292 num_manifest_urls_to_fetch_ - manifest_urls_to_fetch_.size(); 342 unfinished_work_->clear_resource();
293 343 for (const auto& manifest : manifest_urls_to_fetch_)
294 // If there are resource URLs left to fetch, the last manifest is not yet 344 unfinished_work_->add_manifest()->set_url(manifest.spec());
295 // completed. 345 for (const auto& resource : resource_urls_to_fetch_)
296 if (!resource_urls_to_fetch_.empty()) 346 unfinished_work_->add_resource()->set_url(resource.spec());
297 --manifests_completed; 347 for (const auto& it : pool_.elements()) {
298 348 const Fetcher* fetcher = it.first;
299 DCHECK_GE(manifests_completed, 0); 349 if (fetcher->is_resource_request())
300 int percent_completed = num_manifest_urls_to_fetch_ == 0 350 unfinished_work_->add_resource()->set_url(fetcher->url().spec());
301 ? 0 351 else if (fetcher->url() != config_url_)
302 : (static_cast<double>(manifests_completed) / 352 unfinished_work_->add_manifest()->set_url(fetcher->url().spec());
303 num_manifest_urls_to_fetch_ * 100); 353 }
304 UMA_HISTOGRAM_PERCENTAGE("Precache.Fetch.PercentCompleted", 354 manifest_urls_to_fetch_.clear();
305 percent_completed); 355 resource_urls_to_fetch_.clear();
306 UMA_HISTOGRAM_CUSTOM_COUNTS("Precache.Fetch.ResponseBytes.Total", 356 pool_.DeleteAll();
307 total_response_bytes_, 1, kMaxResponseBytes, 100); 357 return std::move(unfinished_work_);
308 UMA_HISTOGRAM_CUSTOM_COUNTS("Precache.Fetch.ResponseBytes.Network",
309 network_response_bytes_, 1, kMaxResponseBytes,
310 100);
311 } 358 }
312 359
313 void PrecacheFetcher::Start() { 360 void PrecacheFetcher::Start() {
361 if (unfinished_work_->has_config_settings()) {
362 DCHECK(unfinished_work_->has_start_time());
363 DetermineManifests();
364 return;
365 }
366
314 GURL config_url = 367 GURL config_url =
315 config_url_.is_empty() ? GetDefaultConfigURL() : config_url_; 368 config_url_.is_empty() ? GetDefaultConfigURL() : config_url_;
316 369
317 DCHECK(config_url.is_valid()) << "Config URL not valid: " 370 DCHECK(config_url.is_valid()) << "Config URL not valid: "
318 << config_url.possibly_invalid_spec(); 371 << config_url.possibly_invalid_spec();
319 372
320 start_time_ = base::TimeTicks::Now();
321
322 // Fetch the precache configuration settings from the server. 373 // Fetch the precache configuration settings from the server.
323 DCHECK(pool_.IsEmpty()) << "All parallel requests should be available"; 374 DCHECK(pool_.IsEmpty()) << "All parallel requests should be available";
324 VLOG(3) << "Fetching " << config_url; 375 VLOG(3) << "Fetching " << config_url;
325 pool_.Add(base::WrapUnique(new Fetcher( 376 pool_.Add(base::WrapUnique(new Fetcher(
326 request_context_.get(), config_url, 377 request_context_.get(), config_url,
327 base::Bind(&PrecacheFetcher::OnConfigFetchComplete, 378 base::Bind(&PrecacheFetcher::OnConfigFetchComplete,
328 base::Unretained(this)), 379 base::Unretained(this)),
329 false /* is_resource_request */, std::numeric_limits<int32_t>::max()))); 380 false /* is_resource_request */, std::numeric_limits<int32_t>::max())));
330 } 381 }
331 382
332 void PrecacheFetcher::StartNextResourceFetch() { 383 void PrecacheFetcher::StartNextResourceFetch() {
384 DCHECK(unfinished_work_->has_config_settings());
333 while (!resource_urls_to_fetch_.empty() && pool_.IsAvailable()) { 385 while (!resource_urls_to_fetch_.empty() && pool_.IsAvailable()) {
334 const size_t max_bytes = 386 const size_t max_bytes =
335 std::min(config_->max_bytes_per_resource(), 387 std::min(unfinished_work_->config_settings().max_bytes_per_resource(),
336 config_->max_bytes_total() - total_response_bytes_); 388 unfinished_work_->config_settings().max_bytes_total() -
389 unfinished_work_->total_bytes());
337 VLOG(3) << "Fetching " << resource_urls_to_fetch_.front(); 390 VLOG(3) << "Fetching " << resource_urls_to_fetch_.front();
338 pool_.Add(base::WrapUnique( 391 pool_.Add(base::WrapUnique(
339 new Fetcher(request_context_.get(), resource_urls_to_fetch_.front(), 392 new Fetcher(request_context_.get(), resource_urls_to_fetch_.front(),
340 base::Bind(&PrecacheFetcher::OnResourceFetchComplete, 393 base::Bind(&PrecacheFetcher::OnResourceFetchComplete,
341 base::Unretained(this)), 394 base::Unretained(this)),
342 true /* is_resource_request */, max_bytes))); 395 true /* is_resource_request */, max_bytes)));
343 396
344 resource_urls_to_fetch_.pop_front(); 397 resource_urls_to_fetch_.pop_front();
345 } 398 }
346 } 399 }
(...skipping 10 matching lines...) Expand all
357 VLOG(3) << "Fetching " << manifest_urls_to_fetch_.front(); 410 VLOG(3) << "Fetching " << manifest_urls_to_fetch_.front();
358 pool_.Add(base::WrapUnique(new Fetcher( 411 pool_.Add(base::WrapUnique(new Fetcher(
359 request_context_.get(), manifest_urls_to_fetch_.front(), 412 request_context_.get(), manifest_urls_to_fetch_.front(),
360 base::Bind(&PrecacheFetcher::OnManifestFetchComplete, 413 base::Bind(&PrecacheFetcher::OnManifestFetchComplete,
361 base::Unretained(this)), 414 base::Unretained(this)),
362 false /* is_resource_request */, std::numeric_limits<int32_t>::max()))); 415 false /* is_resource_request */, std::numeric_limits<int32_t>::max())));
363 416
364 manifest_urls_to_fetch_.pop_front(); 417 manifest_urls_to_fetch_.pop_front();
365 } 418 }
366 419
420 void PrecacheFetcher::NotifyDone(
421 size_t remaining_manifest_urls_to_fetch,
422 size_t remaining_resource_urls_to_fetch) {
423 RecordCompletionStatistics(*unfinished_work_,
424 remaining_manifest_urls_to_fetch,
425 remaining_resource_urls_to_fetch);
426 precache_delegate_->OnDone();
427 }
428
367 void PrecacheFetcher::StartNextFetch() { 429 void PrecacheFetcher::StartNextFetch() {
430 DCHECK(unfinished_work_->has_config_settings());
368 // If over the precache total size cap, then stop prefetching. 431 // If over the precache total size cap, then stop prefetching.
369 if (total_response_bytes_ > config_->max_bytes_total()) { 432 if (unfinished_work_->total_bytes() >
370 precache_delegate_->OnDone(); 433 unfinished_work_->config_settings().max_bytes_total()) {
434 size_t pending_manifests_in_pool = 0;
435 size_t pending_resources_in_pool = 0;
436 for (const auto& element_pair : pool_.elements()) {
437 const Fetcher* fetcher = element_pair.first;
438 if (fetcher->is_resource_request())
439 pending_resources_in_pool++;
440 else if (fetcher->url() != config_url_)
441 pending_manifests_in_pool++;
442 }
443 pool_.DeleteAll();
444 NotifyDone(manifest_urls_to_fetch_.size() + pending_manifests_in_pool,
445 resource_urls_to_fetch_.size() + pending_resources_in_pool);
371 return; 446 return;
372 } 447 }
373 448
374 StartNextResourceFetch(); 449 StartNextResourceFetch();
375 StartNextManifestFetch(); 450 StartNextManifestFetch();
376
377 if (pool_.IsEmpty()) { 451 if (pool_.IsEmpty()) {
378 // There are no more URLs to fetch, so end the precache cycle. 452 // There are no more URLs to fetch, so end the precache cycle.
379 precache_delegate_->OnDone(); 453 NotifyDone(0, 0);
380 // OnDone may have deleted this PrecacheFetcher, so don't do anything after 454 // OnDone may have deleted this PrecacheFetcher, so don't do anything after
381 // it is called. 455 // it is called.
382 } 456 }
383 } 457 }
384 458
385 void PrecacheFetcher::OnConfigFetchComplete(const Fetcher& source) { 459 void PrecacheFetcher::OnConfigFetchComplete(const Fetcher& source) {
386 UpdateStats(source.response_bytes(), source.network_response_bytes()); 460 UpdateStats(source.response_bytes(), source.network_response_bytes());
387 if (source.network_url_fetcher() == nullptr) { 461 if (source.network_url_fetcher() == nullptr) {
388 pool_.DeleteAll(); // Cancel any other ongoing request. 462 pool_.DeleteAll(); // Cancel any other ongoing request.
389 } else { 463 } else {
390 // Attempt to parse the config proto. On failure, continue on with the 464 // Attempt to parse the config proto. On failure, continue on with the
391 // default configuration. 465 // default configuration.
392 ParseProtoFromFetchResponse(*source.network_url_fetcher(), config_.get()); 466 ParseProtoFromFetchResponse(
467 *source.network_url_fetcher(),
468 unfinished_work_->mutable_config_settings());
469 pool_.Delete(source);
470 DetermineManifests();
471 }
472 }
393 473
474 void PrecacheFetcher::DetermineManifests() {
475 DCHECK(unfinished_work_->has_config_settings());
394 std::string prefix = manifest_url_prefix_.empty() 476 std::string prefix = manifest_url_prefix_.empty()
395 ? GetDefaultManifestURLPrefix() 477 ? GetDefaultManifestURLPrefix()
396 : manifest_url_prefix_; 478 : manifest_url_prefix_;
397 DCHECK_NE(std::string(), prefix) 479 DCHECK_NE(std::string(), prefix)
398 << "Could not determine the precache manifest URL prefix."; 480 << "Could not determine the precache manifest URL prefix.";
399 481
400 // Keep track of manifest URLs that are being fetched, in order to elide 482 // Keep track of manifest URLs that are being fetched, in order to elide
401 // duplicates. 483 // duplicates.
402 base::hash_set<std::string> seen_manifest_urls; 484 base::hash_set<std::string> seen_manifest_urls;
403 485
404 // Attempt to fetch manifests for starting hosts up to the maximum top sites 486 // Attempt to fetch manifests for starting hosts up to the maximum top sites
405 // count. If a manifest does not exist for a particular starting host, then 487 // count. If a manifest does not exist for a particular starting host, then
406 // the fetch will fail, and that starting host will be ignored. 488 // the fetch will fail, and that starting host will be ignored. Starting
407 int64_t rank = 0; 489 // hosts are not added if this is a continuation from a previous precache
408 for (const std::string& host : starting_hosts_) { 490 // session.
409 ++rank; 491 if (manifest_urls_to_fetch_.empty() &&
410 if (rank > config_->top_sites_count()) 492 resource_urls_to_fetch_.empty()) {
411 break; 493 int64_t rank = 0;
412 AppendManifestURLIfNew(prefix, host, &seen_manifest_urls, 494 for (const auto& host : unfinished_work_->top_host()) {
413 &manifest_urls_to_fetch_); 495 ++rank;
496 if (rank > unfinished_work_->config_settings().top_sites_count())
497 break;
498 AppendManifestURLIfNew(prefix, host.hostname(), &seen_manifest_urls,
499 &manifest_urls_to_fetch_);
500 }
501
502 for (const std::string& host
503 : unfinished_work_->config_settings().forced_site()) {
504 AppendManifestURLIfNew(prefix, host, &seen_manifest_urls,
505 &manifest_urls_to_fetch_);
506 }
414 } 507 }
415 508 unfinished_work_->set_num_manifest_urls(manifest_urls_to_fetch_.size());
416 for (const std::string& host : config_->forced_site()) 509 StartNextFetch();
417 AppendManifestURLIfNew(prefix, host, &seen_manifest_urls,
418 &manifest_urls_to_fetch_);
419
420 num_manifest_urls_to_fetch_ = manifest_urls_to_fetch_.size();
421 }
422 pool_.Delete(source);
423
424 StartNextFetch();
425 } 510 }
426 511
427 void PrecacheFetcher::OnManifestFetchComplete(const Fetcher& source) { 512 void PrecacheFetcher::OnManifestFetchComplete(const Fetcher& source) {
513 DCHECK(unfinished_work_->has_config_settings());
428 UpdateStats(source.response_bytes(), source.network_response_bytes()); 514 UpdateStats(source.response_bytes(), source.network_response_bytes());
429 if (source.network_url_fetcher() == nullptr) { 515 if (source.network_url_fetcher() == nullptr) {
430 pool_.DeleteAll(); // Cancel any other ongoing request. 516 pool_.DeleteAll(); // Cancel any other ongoing request.
431 } else { 517 } else {
432 PrecacheManifest manifest; 518 PrecacheManifest manifest;
433 519
434 if (ParseProtoFromFetchResponse(*source.network_url_fetcher(), &manifest)) { 520 if (ParseProtoFromFetchResponse(*source.network_url_fetcher(), &manifest)) {
435 const int32_t len = 521 const int32_t len =
436 std::min(manifest.resource_size(), config_->top_resources_count()); 522 std::min(manifest.resource_size(),
523 unfinished_work_->config_settings().top_resources_count());
437 for (int i = 0; i < len; ++i) { 524 for (int i = 0; i < len; ++i) {
438 if (manifest.resource(i).has_url()) { 525 if (manifest.resource(i).has_url())
439 resource_urls_to_fetch_.push_back(GURL(manifest.resource(i).url())); 526 resource_urls_to_fetch_.push_back(GURL(manifest.resource(i).url()));
440 }
441 } 527 }
442 } 528 }
443 } 529 }
444 530
445 pool_.Delete(source); 531 pool_.Delete(source);
446 StartNextFetch(); 532 StartNextFetch();
447 } 533 }
448 534
449 void PrecacheFetcher::OnResourceFetchComplete(const Fetcher& source) { 535 void PrecacheFetcher::OnResourceFetchComplete(const Fetcher& source) {
450 UpdateStats(source.response_bytes(), source.network_response_bytes()); 536 UpdateStats(source.response_bytes(), source.network_response_bytes());
451 pool_.Delete(source); 537 pool_.Delete(source);
452 // The resource has already been put in the cache during the fetch process, so 538 // The resource has already been put in the cache during the fetch process, so
453 // nothing more needs to be done for the resource. 539 // nothing more needs to be done for the resource.
454 StartNextFetch(); 540 StartNextFetch();
455 } 541 }
456 542
457 void PrecacheFetcher::UpdateStats(int64_t response_bytes, 543 void PrecacheFetcher::UpdateStats(int64_t response_bytes,
458 int64_t network_response_bytes) { 544 int64_t network_response_bytes) {
459 total_response_bytes_ += response_bytes; 545 unfinished_work_->set_total_bytes(
460 network_response_bytes_ += network_response_bytes; 546 unfinished_work_->total_bytes() + response_bytes);
547 unfinished_work_->set_network_bytes(
548 unfinished_work_->network_bytes() + network_response_bytes);
461 } 549 }
462 550
463 } // namespace precache 551 } // namespace precache
OLDNEW
« no previous file with comments | « components/precache/core/precache_fetcher.h ('k') | components/precache/core/precache_fetcher_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698