| Index: chrome/browser/predictors/resource_prefetch_predictor.cc
|
| diff --git a/chrome/browser/predictors/resource_prefetch_predictor.cc b/chrome/browser/predictors/resource_prefetch_predictor.cc
|
| index b975788f4577717489009f99d40b8383fe033e8b..d587c099e02eebbe2d243f9754b8ead6544065b4 100644
|
| --- a/chrome/browser/predictors/resource_prefetch_predictor.cc
|
| +++ b/chrome/browser/predictors/resource_prefetch_predictor.cc
|
| @@ -10,6 +10,7 @@
|
|
|
| #include "base/command_line.h"
|
| #include "base/macros.h"
|
| +#include "base/memory/ptr_util.h"
|
| #include "base/metrics/histogram_macros.h"
|
| #include "base/metrics/sparse_histogram.h"
|
| #include "base/strings/string_number_conversions.h"
|
| @@ -65,6 +66,8 @@ enum ReportingEvent {
|
|
|
| namespace predictors {
|
|
|
| +const int kMaxRedirectsPerEntry = 20;
|
| +
|
| ////////////////////////////////////////////////////////////////////////////////
|
| // History lookup task.
|
|
|
| @@ -72,20 +75,20 @@ namespace predictors {
|
| class GetUrlVisitCountTask : public history::HistoryDBTask {
|
| public:
|
| typedef ResourcePrefetchPredictor::URLRequestSummary URLRequestSummary;
|
| - typedef base::Callback<void(
|
| - size_t, // Visit count.
|
| - const NavigationID&,
|
| - const std::vector<URLRequestSummary>&)> VisitInfoCallback;
|
| -
|
| - GetUrlVisitCountTask(
|
| - const NavigationID& navigation_id,
|
| - std::vector<URLRequestSummary>* requests,
|
| - VisitInfoCallback callback)
|
| + typedef ResourcePrefetchPredictor::Navigation Navigation;
|
| + typedef base::Callback<void(size_t, // Visit count.
|
| + const NavigationID&,
|
| + const Navigation&)>
|
| + VisitInfoCallback;
|
| +
|
| + GetUrlVisitCountTask(const NavigationID& navigation_id,
|
| + std::unique_ptr<Navigation> navigation,
|
| + VisitInfoCallback callback)
|
| : visit_count_(0),
|
| navigation_id_(navigation_id),
|
| - requests_(requests),
|
| + navigation_(std::move(navigation)),
|
| callback_(callback) {
|
| - DCHECK(requests_.get());
|
| + DCHECK(navigation_.get());
|
| }
|
|
|
| bool RunOnDBThread(history::HistoryBackend* backend,
|
| @@ -97,7 +100,7 @@ class GetUrlVisitCountTask : public history::HistoryDBTask {
|
| }
|
|
|
| void DoneRunOnMainThread() override {
|
| - callback_.Run(visit_count_, navigation_id_, *requests_);
|
| + callback_.Run(visit_count_, navigation_id_, *navigation_);
|
| }
|
|
|
| private:
|
| @@ -105,7 +108,7 @@ class GetUrlVisitCountTask : public history::HistoryDBTask {
|
|
|
| int visit_count_;
|
| NavigationID navigation_id_;
|
| - std::unique_ptr<std::vector<URLRequestSummary>> requests_;
|
| + std::unique_ptr<Navigation> navigation_;
|
| VisitInfoCallback callback_;
|
|
|
| DISALLOW_COPY_AND_ASSIGN(GetUrlVisitCountTask);
|
| @@ -281,6 +284,11 @@ ResourcePrefetchPredictor::URLRequestSummary::URLRequestSummary(
|
| ResourcePrefetchPredictor::URLRequestSummary::~URLRequestSummary() {
|
| }
|
|
|
| +ResourcePrefetchPredictor::Navigation::Navigation(const GURL& i_initial_url)
|
| + : initial_url(i_initial_url) {}
|
| +
|
| +ResourcePrefetchPredictor::Navigation::~Navigation() {}
|
| +
|
| // static
|
| bool ResourcePrefetchPredictor::URLRequestSummary::SummarizeResponse(
|
| const net::URLRequest& request,
|
| @@ -414,9 +422,9 @@ void ResourcePrefetchPredictor::OnMainFrameRequest(
|
| CleanupAbandonedNavigations(request.navigation_id);
|
|
|
| // New empty navigation entry.
|
| + const GURL& initial_url = request.navigation_id.main_frame_url;
|
| inflight_navigations_.insert(std::make_pair(
|
| - request.navigation_id,
|
| - make_linked_ptr(new std::vector<URLRequestSummary>())));
|
| + request.navigation_id, base::MakeUnique<Navigation>(initial_url)));
|
| }
|
|
|
| void ResourcePrefetchPredictor::OnMainFrameResponse(
|
| @@ -439,7 +447,14 @@ void ResourcePrefetchPredictor::OnMainFrameRedirect(
|
|
|
| // Stop any inflight prefetching. Remove the older navigation.
|
| StopPrefetching(response.navigation_id);
|
| - inflight_navigations_.erase(response.navigation_id);
|
| +
|
| + std::unique_ptr<Navigation> navigation;
|
| + NavigationMap::iterator nav_it =
|
| + inflight_navigations_.find(response.navigation_id);
|
| + if (nav_it != inflight_navigations_.end()) {
|
| + navigation.reset(nav_it->second.release());
|
| + inflight_navigations_.erase(nav_it);
|
| + }
|
|
|
| // A redirect will not lead to another OnMainFrameRequest call, so record the
|
| // redirect url as a new navigation.
|
| @@ -448,11 +463,14 @@ void ResourcePrefetchPredictor::OnMainFrameRedirect(
|
| if (response.redirect_url.is_empty())
|
| return;
|
|
|
| + // If we lost information about first hop for some reason
|
| + if (!navigation)
|
| + navigation.reset(new Navigation(response.navigation_id.main_frame_url));
|
| +
|
| NavigationID navigation_id(response.navigation_id);
|
| navigation_id.main_frame_url = response.redirect_url;
|
| - inflight_navigations_.insert(std::make_pair(
|
| - navigation_id,
|
| - make_linked_ptr(new std::vector<URLRequestSummary>())));
|
| + inflight_navigations_.insert(
|
| + std::make_pair(navigation_id, std::move(navigation)));
|
| }
|
|
|
| void ResourcePrefetchPredictor::OnSubresourceResponse(
|
| @@ -465,7 +483,7 @@ void ResourcePrefetchPredictor::OnSubresourceResponse(
|
| return;
|
| }
|
|
|
| - nav_it->second->push_back(response);
|
| + nav_it->second->subresource_requests.push_back(response);
|
| }
|
|
|
| void ResourcePrefetchPredictor::OnNavigationComplete(
|
| @@ -480,7 +498,7 @@ void ResourcePrefetchPredictor::OnNavigationComplete(
|
| const NavigationID navigation_id(nav_it->first);
|
|
|
| // Remove the navigation from the inflight navigations.
|
| - std::vector<URLRequestSummary>* requests = (nav_it->second).release();
|
| + std::unique_ptr<Navigation> navigation = std::move(nav_it->second);
|
| inflight_navigations_.erase(nav_it);
|
|
|
| // Kick off history lookup to determine if we should record the URL.
|
| @@ -490,7 +508,7 @@ void ResourcePrefetchPredictor::OnNavigationComplete(
|
| DCHECK(history_service);
|
| history_service->ScheduleDBTask(
|
| std::unique_ptr<history::HistoryDBTask>(new GetUrlVisitCountTask(
|
| - navigation_id, requests,
|
| + navigation_id, std::move(navigation),
|
| base::Bind(&ResourcePrefetchPredictor::OnVisitCountLookup,
|
| AsWeakPtr()))),
|
| &history_lookup_consumer_);
|
| @@ -588,29 +606,43 @@ void ResourcePrefetchPredictor::StartInitialization() {
|
| // Create local caches using the database as loaded.
|
| std::unique_ptr<PrefetchDataMap> url_data_map(new PrefetchDataMap());
|
| std::unique_ptr<PrefetchDataMap> host_data_map(new PrefetchDataMap());
|
| + std::unique_ptr<RedirectDataMap> url_redirect_data_map(new RedirectDataMap());
|
| + std::unique_ptr<RedirectDataMap> host_redirect_data_map(
|
| + new RedirectDataMap());
|
| PrefetchDataMap* url_data_ptr = url_data_map.get();
|
| PrefetchDataMap* host_data_ptr = host_data_map.get();
|
| + RedirectDataMap* url_redirect_data_ptr = url_redirect_data_map.get();
|
| + RedirectDataMap* host_redirect_data_ptr = host_redirect_data_map.get();
|
|
|
| BrowserThread::PostTaskAndReply(
|
| BrowserThread::DB, FROM_HERE,
|
| - base::Bind(&ResourcePrefetchPredictorTables::GetAllData,
|
| - tables_, url_data_ptr, host_data_ptr),
|
| + base::Bind(&ResourcePrefetchPredictorTables::GetAllData, tables_,
|
| + url_data_ptr, host_data_ptr, url_redirect_data_ptr,
|
| + host_redirect_data_ptr),
|
| base::Bind(&ResourcePrefetchPredictor::CreateCaches, AsWeakPtr(),
|
| - base::Passed(&url_data_map), base::Passed(&host_data_map)));
|
| + base::Passed(&url_data_map), base::Passed(&host_data_map),
|
| + base::Passed(&url_redirect_data_map),
|
| + base::Passed(&host_redirect_data_map)));
|
| }
|
|
|
| void ResourcePrefetchPredictor::CreateCaches(
|
| std::unique_ptr<PrefetchDataMap> url_data_map,
|
| - std::unique_ptr<PrefetchDataMap> host_data_map) {
|
| + std::unique_ptr<PrefetchDataMap> host_data_map,
|
| + std::unique_ptr<RedirectDataMap> url_redirect_data_map,
|
| + std::unique_ptr<RedirectDataMap> host_redirect_data_map) {
|
| DCHECK_CURRENTLY_ON(BrowserThread::UI);
|
|
|
| DCHECK_EQ(INITIALIZING, initialization_state_);
|
| DCHECK(!url_table_cache_);
|
| DCHECK(!host_table_cache_);
|
| + DCHECK(!url_redirect_table_cache_);
|
| + DCHECK(!host_redirect_table_cache_);
|
| DCHECK(inflight_navigations_.empty());
|
|
|
| url_table_cache_.reset(url_data_map.release());
|
| host_table_cache_.reset(host_data_map.release());
|
| + url_redirect_table_cache_.reset(url_redirect_data_map.release());
|
| + host_redirect_table_cache_.reset(host_redirect_data_map.release());
|
|
|
| UMA_HISTOGRAM_COUNTS("ResourcePrefetchPredictor.UrlTableMainFrameUrlCount",
|
| url_table_cache_->size());
|
| @@ -678,11 +710,10 @@ void ResourcePrefetchPredictor::DeleteUrls(const history::URLRows& urls) {
|
| }
|
|
|
| if (!urls_to_delete.empty() || !hosts_to_delete.empty()) {
|
| - BrowserThread::PostTask(BrowserThread::DB, FROM_HERE,
|
| - base::Bind(&ResourcePrefetchPredictorTables::DeleteData,
|
| - tables_,
|
| - urls_to_delete,
|
| - hosts_to_delete));
|
| + BrowserThread::PostTask(
|
| + BrowserThread::DB, FROM_HERE,
|
| + base::Bind(&ResourcePrefetchPredictorTables::DeleteResourceData,
|
| + tables_, urls_to_delete, hosts_to_delete));
|
| }
|
| }
|
|
|
| @@ -703,22 +734,48 @@ void ResourcePrefetchPredictor::RemoveOldestEntryInPrefetchDataMap(
|
| }
|
|
|
| data_map->erase(key_to_delete);
|
| - BrowserThread::PostTask(BrowserThread::DB, FROM_HERE,
|
| - base::Bind(&ResourcePrefetchPredictorTables::DeleteSingleDataPoint,
|
| - tables_,
|
| - key_to_delete,
|
| - key_type));
|
| + BrowserThread::PostTask(
|
| + BrowserThread::DB, FROM_HERE,
|
| + base::Bind(
|
| + &ResourcePrefetchPredictorTables::DeleteSingleResourceDataPoint,
|
| + tables_, key_to_delete, key_type));
|
| +}
|
| +
|
| +void ResourcePrefetchPredictor::RemoveOldestEntryInRedirectDataMap(
|
| + PrefetchKeyType key_type,
|
| + RedirectDataMap* data_map) {
|
| + if (data_map->empty())
|
| + return;
|
| +
|
| + uint64_t oldest_time;
|
| + std::string key_to_delete;
|
| + for (const auto& kv : *data_map) {
|
| + const RedirectData& data = kv.second;
|
| + if (key_to_delete.empty() || data.last_visit_time() < oldest_time) {
|
| + key_to_delete = data.primary_key();
|
| + oldest_time = data.last_visit_time();
|
| + }
|
| + }
|
| +
|
| + data_map->erase(key_to_delete);
|
| + BrowserThread::PostTask(
|
| + BrowserThread::DB, FROM_HERE,
|
| + base::Bind(
|
| + &ResourcePrefetchPredictorTables::DeleteSingleRedirectDataPoint,
|
| + tables_, key_to_delete, key_type));
|
| }
|
|
|
| void ResourcePrefetchPredictor::OnVisitCountLookup(
|
| size_t visit_count,
|
| const NavigationID& navigation_id,
|
| - const std::vector<URLRequestSummary>& requests) {
|
| + const Navigation& navigation) {
|
| DCHECK_CURRENTLY_ON(BrowserThread::UI);
|
|
|
| UMA_HISTOGRAM_COUNTS("ResourcePrefetchPredictor.HistoryVisitCountForUrl",
|
| visit_count);
|
|
|
| + // TODO(alexilin) make only one request to DB thread
|
| +
|
| // URL level data - merge only if we are already saving the data, or we it
|
| // meets the cutoff requirement.
|
| const std::string url_spec = navigation_id.main_frame_url.spec();
|
| @@ -728,17 +785,19 @@ void ResourcePrefetchPredictor::OnVisitCountLookup(
|
| (visit_count >= config_.min_url_visit_count);
|
|
|
| if (should_track_url && config_.IsURLLearningEnabled()) {
|
| - LearnNavigation(url_spec, PREFETCH_KEY_TYPE_URL, requests,
|
| - config_.max_urls_to_track, url_table_cache_.get());
|
| + LearnNavigation(url_spec, PREFETCH_KEY_TYPE_URL,
|
| + navigation.subresource_requests, config_.max_urls_to_track,
|
| + url_table_cache_.get(), navigation.initial_url.spec(),
|
| + url_redirect_table_cache_.get());
|
| }
|
|
|
| // Host level data - no cutoff, always learn the navigation if enabled.
|
| if (config_.IsHostLearningEnabled()) {
|
| - LearnNavigation(navigation_id.main_frame_url.host(),
|
| - PREFETCH_KEY_TYPE_HOST,
|
| - requests,
|
| - config_.max_hosts_to_track,
|
| - host_table_cache_.get());
|
| + const std::string host = navigation_id.main_frame_url.host();
|
| + LearnNavigation(host, PREFETCH_KEY_TYPE_HOST,
|
| + navigation.subresource_requests, config_.max_hosts_to_track,
|
| + host_table_cache_.get(), navigation.initial_url.host(),
|
| + host_redirect_table_cache_.get());
|
| }
|
| }
|
|
|
| @@ -747,7 +806,9 @@ void ResourcePrefetchPredictor::LearnNavigation(
|
| PrefetchKeyType key_type,
|
| const std::vector<URLRequestSummary>& new_resources,
|
| size_t max_data_map_size,
|
| - PrefetchDataMap* data_map) {
|
| + PrefetchDataMap* data_map,
|
| + const std::string& redirect_origin_key,
|
| + RedirectDataMap* redirect_map) {
|
| DCHECK_CURRENTLY_ON(BrowserThread::UI);
|
|
|
| // If the primary key is too long reject it.
|
| @@ -756,10 +817,9 @@ void ResourcePrefetchPredictor::LearnNavigation(
|
|
|
| PrefetchDataMap::iterator cache_entry = data_map->find(key);
|
| if (cache_entry == data_map->end()) {
|
| - if (data_map->size() >= max_data_map_size) {
|
| - // The table is full, delete an entry.
|
| + // If the table is full, delete an entry.
|
| + if (data_map->size() >= max_data_map_size)
|
| RemoveOldestEntryInPrefetchDataMap(key_type, data_map);
|
| - }
|
|
|
| cache_entry = data_map->insert(std::make_pair(
|
| key, PrefetchData(key_type, key))).first;
|
| @@ -879,23 +939,110 @@ void ResourcePrefetchPredictor::LearnNavigation(
|
| data_map->erase(key);
|
| BrowserThread::PostTask(
|
| BrowserThread::DB, FROM_HERE,
|
| - base::Bind(&ResourcePrefetchPredictorTables::DeleteSingleDataPoint,
|
| - tables_,
|
| - key,
|
| - key_type));
|
| + base::Bind(
|
| + &ResourcePrefetchPredictorTables::DeleteSingleResourceDataPoint,
|
| + tables_, key, key_type));
|
| } else {
|
| bool is_host = key_type == PREFETCH_KEY_TYPE_HOST;
|
| PrefetchData empty_data(
|
| !is_host ? PREFETCH_KEY_TYPE_HOST : PREFETCH_KEY_TYPE_URL,
|
| std::string());
|
| + RedirectData empty_redirect_data;
|
| const PrefetchData& host_data = is_host ? cache_entry->second : empty_data;
|
| const PrefetchData& url_data = is_host ? empty_data : cache_entry->second;
|
| BrowserThread::PostTask(
|
| BrowserThread::DB, FROM_HERE,
|
| - base::Bind(&ResourcePrefetchPredictorTables::UpdateData,
|
| - tables_,
|
| - url_data,
|
| - host_data));
|
| + base::Bind(&ResourcePrefetchPredictorTables::UpdateData, tables_,
|
| + url_data, host_data, empty_redirect_data,
|
| + empty_redirect_data));
|
| + }
|
| +
|
| + if (key != redirect_origin_key) {
|
| + LearnRedirect(redirect_origin_key, key_type, key, max_data_map_size,
|
| + redirect_map);
|
| + }
|
| +}
|
| +
|
| +void ResourcePrefetchPredictor::LearnRedirect(const std::string& key,
|
| + PrefetchKeyType key_type,
|
| + const std::string& final_redirect,
|
| + size_t max_redirect_map_size,
|
| + RedirectDataMap* redirect_map) {
|
| + DCHECK_CURRENTLY_ON(BrowserThread::UI);
|
| +
|
| + RedirectDataMap::iterator cache_entry = redirect_map->find(key);
|
| + if (cache_entry == redirect_map->end()) {
|
| + if (redirect_map->size() >= max_redirect_map_size)
|
| + RemoveOldestEntryInRedirectDataMap(key_type, redirect_map);
|
| +
|
| + RedirectData new_data;
|
| + new_data.set_primary_key(key);
|
| + cache_entry = redirect_map->insert(std::make_pair(key, new_data)).first;
|
| + cache_entry->second.set_last_visit_time(
|
| + base::Time::Now().ToInternalValue());
|
| + RedirectStat* redirect_to_add = cache_entry->second.add_redirects();
|
| + redirect_to_add->set_url(final_redirect);
|
| + redirect_to_add->set_number_of_hits(1);
|
| + } else {
|
| + bool need_to_add = true;
|
| + cache_entry->second.set_last_visit_time(
|
| + base::Time::Now().ToInternalValue());
|
| +
|
| + for (RedirectStat& redirect : *(cache_entry->second.mutable_redirects())) {
|
| + if (redirect.url() == final_redirect) {
|
| + need_to_add = false;
|
| + redirect.set_number_of_hits(redirect.number_of_hits() + 1);
|
| + redirect.set_consecutive_misses(0);
|
| + } else {
|
| + redirect.set_number_of_misses(redirect.number_of_misses() + 1);
|
| + redirect.set_consecutive_misses(redirect.consecutive_misses() + 1);
|
| + }
|
| + }
|
| +
|
| + if (need_to_add) {
|
| + RedirectStat* redirect_to_add = cache_entry->second.add_redirects();
|
| + redirect_to_add->set_url(final_redirect);
|
| + redirect_to_add->set_number_of_hits(1);
|
| + }
|
| + }
|
| +
|
| + // Trim and sort redirects after update.
|
| + std::vector<RedirectStat> redirects;
|
| + redirects.reserve(cache_entry->second.redirects_size());
|
| + for (const RedirectStat& redirect : cache_entry->second.redirects()) {
|
| + if (redirect.consecutive_misses() < config_.max_consecutive_misses)
|
| + redirects.push_back(redirect);
|
| + }
|
| + ResourcePrefetchPredictorTables::SortRedirects(&redirects);
|
| +
|
| + if (redirects.size() > kMaxRedirectsPerEntry)
|
| + redirects.resize(kMaxRedirectsPerEntry);
|
| +
|
| + cache_entry->second.clear_redirects();
|
| + for (const RedirectStat& redirect : redirects)
|
| + cache_entry->second.add_redirects()->CopyFrom(redirect);
|
| +
|
| + if (redirects.empty()) {
|
| + redirect_map->erase(cache_entry);
|
| + BrowserThread::PostTask(
|
| + BrowserThread::DB, FROM_HERE,
|
| + base::Bind(
|
| + &ResourcePrefetchPredictorTables::DeleteSingleRedirectDataPoint,
|
| + tables_, key, key_type));
|
| + } else {
|
| + bool is_host = key_type == PREFETCH_KEY_TYPE_HOST;
|
| + RedirectData empty_redirect_data;
|
| + PrefetchData empty_url_data(PREFETCH_KEY_TYPE_URL, std::string());
|
| + PrefetchData empty_host_data(PREFETCH_KEY_TYPE_HOST, std::string());
|
| + const RedirectData& host_redirect_data =
|
| + is_host ? cache_entry->second : empty_redirect_data;
|
| + const RedirectData& url_redirect_data =
|
| + is_host ? empty_redirect_data : cache_entry->second;
|
| + BrowserThread::PostTask(
|
| + BrowserThread::DB, FROM_HERE,
|
| + base::Bind(&ResourcePrefetchPredictorTables::UpdateData, tables_,
|
| + empty_url_data, empty_host_data, url_redirect_data,
|
| + host_redirect_data));
|
| }
|
| }
|
|
|
|
|