Chromium Code Reviews| 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 c7645ff22b31e0b95019bc73d2c89f986e5b62fc..6e9fa501fc9c64bc584265739b30bcf89f742398 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, |
| @@ -437,9 +445,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( |
| @@ -462,7 +470,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. |
| @@ -471,11 +486,14 @@ void ResourcePrefetchPredictor::OnMainFrameRedirect( |
| if (response.redirect_url.is_empty()) |
| return; |
| + // If we lost information about first hop for some reason |
|
Benoit L
2016/09/26 11:18:36
I'm not entirely sure about what should be done he
alexilin
2016/09/26 12:14:54
It could happen in some rare cases, for example, t
Benoit L
2016/09/27 09:55:06
Ok, thanks.
Only a nit then:
// If we lost the in
alexilin
2016/09/27 14:52:45
Thanks!
Done.
|
| + if (!navigation) |
| + navigation.reset(new Navigation(response.navigation_id.main_frame_url)); |
|
pasko
2016/09/22 14:27:09
why not MakeUnique here as well?
alexilin
2016/09/22 16:48:19
How to connect unique_ptr::reset() with MakeUnique
pasko
2016/09/26 12:28:15
navigation = MakeUnique<Navigation>(...)?
alexilin
2016/09/26 15:38:28
There will be one extra construction of unique_ptr
pasko
2016/09/26 15:58:22
I "think" on-stack unique_ptr constructors have al
alexilin
2016/09/27 14:52:45
Done.
|
| + |
| 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( |
| @@ -488,7 +506,7 @@ void ResourcePrefetchPredictor::OnSubresourceResponse( |
| return; |
| } |
| - nav_it->second->push_back(response); |
| + nav_it->second->subresource_requests.push_back(response); |
| } |
| void ResourcePrefetchPredictor::OnNavigationComplete( |
| @@ -503,7 +521,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. |
| @@ -513,7 +531,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_); |
| @@ -618,29 +636,43 @@ void ResourcePrefetchPredictor::StartInitialization() { |
| // Create local caches using the database as loaded. |
| std::unique_ptr<PrefetchDataMap> url_data_map(new PrefetchDataMap()); |
|
Benoit L
2016/09/26 11:18:36
nit: Can you use MakeUnique, here and below?
auto
alexilin
2016/09/26 12:14:54
Done.
|
| 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(); |
|
Benoit L
2016/09/26 11:18:36
nit: you can directly call .get() below.
alexilin
2016/09/26 12:14:54
Done.
|
| + 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()); |
| @@ -717,11 +749,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)); |
| } |
| } |
| @@ -742,22 +773,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(); |
| @@ -767,17 +824,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()); |
| } |
| // Remove the navigation from the results map. |
| @@ -789,7 +848,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. |
| @@ -798,10 +859,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; |
| @@ -921,23 +981,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)); |
| } |
| } |