| OLD | NEW |
| 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_database.h" | 5 #include "components/precache/core/precache_database.h" |
| 6 | 6 |
| 7 #include "base/bind.h" | 7 #include "base/bind.h" |
| 8 #include "base/files/file_path.h" | 8 #include "base/files/file_path.h" |
| 9 #include "base/location.h" | 9 #include "base/location.h" |
| 10 #include "base/metrics/histogram_macros.h" | 10 #include "base/metrics/histogram_macros.h" |
| (...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 51 db_.reset(new sql::Connection()); | 51 db_.reset(new sql::Connection()); |
| 52 db_->set_histogram_tag("Precache"); | 52 db_->set_histogram_tag("Precache"); |
| 53 | 53 |
| 54 if (!db_->Open(db_path)) { | 54 if (!db_->Open(db_path)) { |
| 55 // Don't initialize the URL table if unable to access | 55 // Don't initialize the URL table if unable to access |
| 56 // the database. | 56 // the database. |
| 57 return false; | 57 return false; |
| 58 } | 58 } |
| 59 | 59 |
| 60 if (!precache_url_table_.Init(db_.get()) || | 60 if (!precache_url_table_.Init(db_.get()) || |
| 61 !precache_referrer_host_table_.Init(db_.get()) || |
| 61 !precache_session_table_.Init(db_.get())) { | 62 !precache_session_table_.Init(db_.get())) { |
| 62 // Raze and close the database connection to indicate that it's not usable, | 63 // Raze and close the database connection to indicate that it's not usable, |
| 63 // and so that the database will be created anew next time, in case it's | 64 // and so that the database will be created anew next time, in case it's |
| 64 // corrupted. | 65 // corrupted. |
| 65 db_->RazeAndClose(); | 66 db_->RazeAndClose(); |
| 66 return false; | 67 return false; |
| 67 } | 68 } |
| 68 return true; | 69 return true; |
| 69 } | 70 } |
| 70 | 71 |
| 71 void PrecacheDatabase::DeleteExpiredPrecacheHistory( | 72 void PrecacheDatabase::DeleteExpiredPrecacheHistory( |
| 72 const base::Time& current_time) { | 73 const base::Time& current_time) { |
| 73 if (!IsDatabaseAccessible()) { | 74 if (!IsDatabaseAccessible()) { |
| 74 // Do nothing if unable to access the database. | 75 // Do nothing if unable to access the database. |
| 75 return; | 76 return; |
| 76 } | 77 } |
| 77 | 78 |
| 78 // Delete old precache history that has expired. | 79 // Delete old precache history that has expired. |
| 79 base::Time delete_end = current_time - base::TimeDelta::FromDays( | 80 base::Time delete_end = current_time - base::TimeDelta::FromDays( |
| 80 kPrecacheHistoryExpiryPeriodDays); | 81 kPrecacheHistoryExpiryPeriodDays); |
| 81 buffered_writes_.push_back( | 82 buffered_writes_.push_back( |
| 82 base::Bind(&PrecacheURLTable::DeleteAllPrecachedBefore, | 83 base::Bind(&PrecacheURLTable::DeleteAllPrecachedBefore, |
| 83 base::Unretained(&precache_url_table_), delete_end)); | 84 base::Unretained(&precache_url_table_), delete_end)); |
| 85 buffered_writes_.push_back( |
| 86 base::Bind(&PrecacheReferrerHostTable::DeleteAllEntriesBefore, |
| 87 base::Unretained(&precache_referrer_host_table_), delete_end)); |
| 84 Flush(); | 88 Flush(); |
| 85 } | 89 } |
| 86 | 90 |
| 87 void PrecacheDatabase::ClearHistory() { | 91 void PrecacheDatabase::ClearHistory() { |
| 88 if (!IsDatabaseAccessible()) { | 92 if (!IsDatabaseAccessible()) { |
| 89 // Do nothing if unable to access the database. | 93 // Do nothing if unable to access the database. |
| 90 return; | 94 return; |
| 91 } | 95 } |
| 92 | 96 |
| 93 buffered_writes_.push_back(base::Bind( | 97 buffered_writes_.push_back(base::Bind( |
| 94 &PrecacheURLTable::DeleteAll, base::Unretained(&precache_url_table_))); | 98 &PrecacheURLTable::DeleteAll, base::Unretained(&precache_url_table_))); |
| 99 buffered_writes_.push_back( |
| 100 base::Bind(&PrecacheReferrerHostTable::DeleteAll, |
| 101 base::Unretained(&precache_referrer_host_table_))); |
| 95 Flush(); | 102 Flush(); |
| 96 } | 103 } |
| 97 | 104 |
| 98 void PrecacheDatabase::SetLastPrecacheTimestamp(const base::Time& time) { | 105 void PrecacheDatabase::SetLastPrecacheTimestamp(const base::Time& time) { |
| 99 last_precache_timestamp_ = time; | 106 last_precache_timestamp_ = time; |
| 100 | 107 |
| 101 if (!IsDatabaseAccessible()) { | 108 if (!IsDatabaseAccessible()) { |
| 102 // Do nothing if unable to access the database. | 109 // Do nothing if unable to access the database. |
| 103 return; | 110 return; |
| 104 } | 111 } |
| 105 | 112 |
| 106 buffered_writes_.push_back( | 113 buffered_writes_.push_back( |
| 107 base::Bind(&PrecacheSessionTable::SetLastPrecacheTimestamp, | 114 base::Bind(&PrecacheSessionTable::SetLastPrecacheTimestamp, |
| 108 base::Unretained(&precache_session_table_), time)); | 115 base::Unretained(&precache_session_table_), time)); |
| 109 MaybePostFlush(); | 116 MaybePostFlush(); |
| 110 } | 117 } |
| 111 | 118 |
| 112 base::Time PrecacheDatabase::GetLastPrecacheTimestamp() { | 119 base::Time PrecacheDatabase::GetLastPrecacheTimestamp() { |
| 113 if (last_precache_timestamp_.is_null() && IsDatabaseAccessible()) { | 120 if (last_precache_timestamp_.is_null() && IsDatabaseAccessible()) { |
| 114 last_precache_timestamp_ = | 121 last_precache_timestamp_ = |
| 115 precache_session_table_.GetLastPrecacheTimestamp(); | 122 precache_session_table_.GetLastPrecacheTimestamp(); |
| 116 } | 123 } |
| 117 return last_precache_timestamp_; | 124 return last_precache_timestamp_; |
| 118 } | 125 } |
| 119 | 126 |
| 120 void PrecacheDatabase::RecordURLPrefetch(const GURL& url, | 127 PrecacheReferrerHostEntry PrecacheDatabase::GetReferrerHost( |
| 121 const base::TimeDelta& latency, | 128 const std::string& referrer_host) { |
| 122 const base::Time& fetch_time, | 129 DCHECK(thread_checker_.CalledOnValidThread()); |
| 123 const net::HttpResponseInfo& info, | 130 return precache_referrer_host_table_.GetReferrerHost(referrer_host); |
| 124 int64_t size) { | 131 } |
| 132 |
| 133 void PrecacheDatabase::GetURLListForReferrerHost( |
| 134 int64_t referrer_host_id, |
| 135 std::vector<GURL>* used_urls, |
| 136 std::vector<GURL>* unused_urls) { |
| 137 DCHECK(thread_checker_.CalledOnValidThread()); |
| 138 DCHECK_NE(PrecacheReferrerHostEntry::kInvalidId, referrer_host_id); |
| 139 |
| 140 // Flush any pending writes to the URL and referrer host tables. |
| 141 Flush(); |
| 142 |
| 143 precache_url_table_.GetURLListForReferrerHost(referrer_host_id, used_urls, |
| 144 unused_urls); |
| 145 } |
| 146 |
| 147 void PrecacheDatabase::RecordURLPrefetchMetrics( |
| 148 const net::HttpResponseInfo& info, |
| 149 const base::TimeDelta& latency) { |
| 150 DCHECK(thread_checker_.CalledOnValidThread()); |
| 151 |
| 125 UMA_HISTOGRAM_TIMES("Precache.Latency.Prefetch", latency); | 152 UMA_HISTOGRAM_TIMES("Precache.Latency.Prefetch", latency); |
| 126 | 153 |
| 127 if (!IsDatabaseAccessible()) { | |
| 128 // Don't track anything if unable to access the database. | |
| 129 return; | |
| 130 } | |
| 131 | |
| 132 if (buffered_urls_.find(url.spec()) != buffered_urls_.end()) { | |
| 133 // If the URL for this fetch is in the write buffer, then flush the write | |
| 134 // buffer. | |
| 135 Flush(); | |
| 136 } | |
| 137 | |
| 138 DCHECK(info.headers) << "The headers are required to get the freshness."; | 154 DCHECK(info.headers) << "The headers are required to get the freshness."; |
| 139 if (info.headers) { | 155 if (info.headers) { |
| 140 UMA_HISTOGRAM_CUSTOM_COUNTS( | 156 UMA_HISTOGRAM_CUSTOM_COUNTS( |
| 141 "Precache.Freshness.Prefetch", | 157 "Precache.Freshness.Prefetch", |
| 142 info.headers->GetFreshnessLifetimes(info.response_time) | 158 info.headers->GetFreshnessLifetimes(info.response_time) |
| 143 .freshness.InSeconds(), | 159 .freshness.InSeconds(), |
| 144 base::TimeDelta::FromMinutes(5).InSeconds() /* min */, | 160 base::TimeDelta::FromMinutes(5).InSeconds() /* min */, |
| 145 base::TimeDelta::FromDays(356).InSeconds() /* max */, | 161 base::TimeDelta::FromDays(356).InSeconds() /* max */, |
| 146 100 /* bucket_count */); | 162 100 /* bucket_count */); |
| 147 } | 163 } |
| 164 } |
| 148 | 165 |
| 149 if (info.was_cached && !precache_url_table_.HasURL(url)) { | 166 void PrecacheDatabase::RecordURLPrefetch(const GURL& url, |
| 150 // Since the precache came from the cache, and there's no entry in the URL | 167 const std::string& referrer_host, |
| 151 // table for the URL, this means that the resource was already in the cache | 168 const base::Time& fetch_time, |
| 152 // because of user browsing. Therefore, this precache won't be considered as | 169 bool was_cached, |
| 153 // precache-motivated since it had no significant effect (besides a possible | 170 int64_t size) { |
| 154 // revalidation and a change in the cache LRU priority). | 171 DCHECK(thread_checker_.CalledOnValidThread()); |
| 172 |
| 173 if (!IsDatabaseAccessible()) { |
| 174 // Don't track anything if unable to access the database. |
| 155 return; | 175 return; |
| 156 } | 176 } |
| 157 | 177 |
| 158 if (!info.was_cached) { | 178 if (buffered_urls_.find(url.spec()) != buffered_urls_.end()) { |
| 179 // If the URL for this fetch is in the write buffer, then flush the write |
| 180 // buffer. |
| 181 Flush(); |
| 182 } |
| 183 |
| 184 if (!was_cached) { |
| 159 // The precache only counts as overhead if it was downloaded over the | 185 // The precache only counts as overhead if it was downloaded over the |
| 160 // network. | 186 // network. |
| 161 UMA_HISTOGRAM_COUNTS("Precache.DownloadedPrecacheMotivated", | 187 UMA_HISTOGRAM_COUNTS("Precache.DownloadedPrecacheMotivated", |
| 162 static_cast<base::HistogramBase::Sample>(size)); | 188 static_cast<base::HistogramBase::Sample>(size)); |
| 163 } | 189 } |
| 164 | 190 |
| 165 // Use the URL table to keep track of URLs that are in the cache thanks to | 191 // Use the URL table to keep track of URLs. URLs that are fetched via network |
| 166 // precaching. If a row for the URL already exists, than update the timestamp | 192 // or already in the cache due to prior precaching are recorded as |
| 167 // to |fetch_time|. | 193 // precache-motivated. URLs that came from the cache and not recorded as |
| 168 buffered_writes_.push_back( | 194 // precached previously, were already in the cache because of user browsing. |
| 169 base::Bind(&PrecacheURLTable::AddURL, | 195 // Therefore, this precache will not be considered as precache-motivated, |
| 170 base::Unretained(&precache_url_table_), url, fetch_time)); | 196 // since it had no significant effect (besides a possible revalidation and a |
| 197 // change in the cache LRU priority). If a row for the URL already exists, |
| 198 // then the timestamp is updated. |
| 199 buffered_writes_.push_back(base::Bind( |
| 200 &PrecacheDatabase::RecordURLPrefetchInternal, GetWeakPtr(), url, |
| 201 referrer_host, !was_cached || precache_url_table_.IsURLPrecached(url), |
| 202 fetch_time)); |
| 171 buffered_urls_.insert(url.spec()); | 203 buffered_urls_.insert(url.spec()); |
| 172 MaybePostFlush(); | 204 MaybePostFlush(); |
| 173 } | 205 } |
| 174 | 206 |
| 207 void PrecacheDatabase::RecordURLPrefetchInternal( |
| 208 const GURL& url, |
| 209 const std::string& referrer_host, |
| 210 bool is_precached, |
| 211 const base::Time& fetch_time) { |
| 212 auto referrer_host_id = precache_referrer_host_table_.UpdateReferrerHost( |
| 213 referrer_host, 0, fetch_time); |
| 214 DCHECK_NE(referrer_host_id, PrecacheReferrerHostEntry::kInvalidId); |
| 215 precache_url_table_.AddURL(url, referrer_host_id, is_precached, fetch_time); |
| 216 } |
| 217 |
| 175 void PrecacheDatabase::RecordURLNonPrefetch(const GURL& url, | 218 void PrecacheDatabase::RecordURLNonPrefetch(const GURL& url, |
| 176 const base::TimeDelta& latency, | 219 const base::TimeDelta& latency, |
| 177 const base::Time& fetch_time, | 220 const base::Time& fetch_time, |
| 178 const net::HttpResponseInfo& info, | 221 const net::HttpResponseInfo& info, |
| 179 int64_t size, | 222 int64_t size, |
| 180 int host_rank, | 223 int host_rank, |
| 181 bool is_connection_cellular) { | 224 bool is_connection_cellular) { |
| 182 UMA_HISTOGRAM_TIMES("Precache.Latency.NonPrefetch", latency); | 225 UMA_HISTOGRAM_TIMES("Precache.Latency.NonPrefetch", latency); |
| 183 UMA_HISTOGRAM_ENUMERATION("Precache.CacheStatus.NonPrefetch", | 226 UMA_HISTOGRAM_ENUMERATION("Precache.CacheStatus.NonPrefetch", |
| 184 info.cache_entry_status, | 227 info.cache_entry_status, |
| (...skipping 15 matching lines...) Expand all Loading... |
| 200 } | 243 } |
| 201 | 244 |
| 202 RecordTimeSinceLastPrecache(fetch_time); | 245 RecordTimeSinceLastPrecache(fetch_time); |
| 203 | 246 |
| 204 if (buffered_urls_.find(url.spec()) != buffered_urls_.end()) { | 247 if (buffered_urls_.find(url.spec()) != buffered_urls_.end()) { |
| 205 // If the URL for this fetch is in the write buffer, then flush the write | 248 // If the URL for this fetch is in the write buffer, then flush the write |
| 206 // buffer. | 249 // buffer. |
| 207 Flush(); | 250 Flush(); |
| 208 } | 251 } |
| 209 | 252 |
| 210 if (info.was_cached && !precache_url_table_.HasURL(url)) { | 253 bool is_precached = precache_url_table_.IsURLPrecachedAndUnused(url); |
| 254 if (info.was_cached && !is_precached) { |
| 211 // Ignore cache hits that precache can't take credit for. | 255 // Ignore cache hits that precache can't take credit for. |
| 212 return; | 256 return; |
| 213 } | 257 } |
| 214 | 258 |
| 215 base::HistogramBase::Sample size_sample = | 259 base::HistogramBase::Sample size_sample = |
| 216 static_cast<base::HistogramBase::Sample>(size); | 260 static_cast<base::HistogramBase::Sample>(size); |
| 217 if (!info.was_cached) { | 261 if (!info.was_cached) { |
| 218 // The fetch was served over the network during user browsing, so count it | 262 // The fetch was served over the network during user browsing, so count it |
| 219 // as downloaded non-precache bytes. | 263 // as downloaded non-precache bytes. |
| 220 UMA_HISTOGRAM_COUNTS("Precache.DownloadedNonPrecache", size_sample); | 264 UMA_HISTOGRAM_COUNTS("Precache.DownloadedNonPrecache", size_sample); |
| 221 if (is_connection_cellular) { | 265 if (is_connection_cellular) { |
| 222 UMA_HISTOGRAM_COUNTS("Precache.DownloadedNonPrecache.Cellular", | 266 UMA_HISTOGRAM_COUNTS("Precache.DownloadedNonPrecache.Cellular", |
| 223 size_sample); | 267 size_sample); |
| 224 } | 268 } |
| 269 // Since the resource has been fetched during user browsing, mark the URL as |
| 270 // used in the precache URL table, if any exists. The current fetch would |
| 271 // have put this resource in the cache regardless of whether or not it was |
| 272 // previously precached, so mark the URL as used. |
| 273 buffered_writes_.push_back( |
| 274 base::Bind(&PrecacheURLTable::SetURLAsNotPrecached, |
| 275 base::Unretained(&precache_url_table_), url)); |
| 276 buffered_urls_.insert(url.spec()); |
| 277 MaybePostFlush(); |
| 225 } else { // info.was_cached. | 278 } else { // info.was_cached. |
| 226 // The fetch was served from the cache, and since there's an entry for this | 279 // The fetch was served from the cache, and since there's an entry for this |
| 227 // URL in the URL table, this means that the resource was served from the | 280 // URL in the URL table, this means that the resource was served from the |
| 228 // cache only because precaching put it there. Thus, precaching was helpful, | 281 // cache only because precaching put it there. Thus, precaching was helpful, |
| 229 // so count the fetch as saved bytes. | 282 // so count the fetch as saved bytes. |
| 230 UMA_HISTOGRAM_COUNTS("Precache.Saved", size_sample); | 283 UMA_HISTOGRAM_COUNTS("Precache.Saved", size_sample); |
| 231 if (is_connection_cellular) { | 284 if (is_connection_cellular) { |
| 232 UMA_HISTOGRAM_COUNTS("Precache.Saved.Cellular", size_sample); | 285 UMA_HISTOGRAM_COUNTS("Precache.Saved.Cellular", size_sample); |
| 233 } | 286 } |
| 234 | 287 |
| 235 DCHECK(info.headers) << "The headers are required to get the freshness."; | 288 DCHECK(info.headers) << "The headers are required to get the freshness."; |
| 236 if (info.headers) { | 289 if (info.headers) { |
| 237 // TODO(jamartin): Maybe report stale_while_validate as well. | 290 // TODO(jamartin): Maybe report stale_while_validate as well. |
| 238 UMA_HISTOGRAM_CUSTOM_COUNTS( | 291 UMA_HISTOGRAM_CUSTOM_COUNTS( |
| 239 "Precache.Saved.Freshness", | 292 "Precache.Saved.Freshness", |
| 240 info.headers->GetFreshnessLifetimes(info.response_time) | 293 info.headers->GetFreshnessLifetimes(info.response_time) |
| 241 .freshness.InSeconds(), | 294 .freshness.InSeconds(), |
| 242 base::TimeDelta::FromMinutes(5).InSeconds() /* min */, | 295 base::TimeDelta::FromMinutes(5).InSeconds() /* min */, |
| 243 base::TimeDelta::FromDays(356).InSeconds() /* max */, | 296 base::TimeDelta::FromDays(356).InSeconds() /* max */, |
| 244 100 /* bucket_count */); | 297 100 /* bucket_count */); |
| 245 } | 298 } |
| 299 |
| 300 buffered_writes_.push_back( |
| 301 base::Bind(&PrecacheURLTable::SetPrecachedURLAsUsed, |
| 302 base::Unretained(&precache_url_table_), url)); |
| 303 buffered_urls_.insert(url.spec()); |
| 304 MaybePostFlush(); |
| 305 } |
| 306 } |
| 307 |
| 308 void PrecacheDatabase::UpdatePrecacheReferrerHost( |
| 309 const std::string& hostname, |
| 310 int64_t manifest_id, |
| 311 const base::Time& fetch_time) { |
| 312 DCHECK(thread_checker_.CalledOnValidThread()); |
| 313 |
| 314 if (!IsDatabaseAccessible()) { |
| 315 // Don't track anything if unable to access the database. |
| 316 return; |
| 246 } | 317 } |
| 247 | 318 |
| 248 // Since the resource has been fetched during user browsing, remove any record | |
| 249 // of that URL having been precached from the URL table, if any exists. | |
| 250 // The current fetch would have put this resource in the cache regardless of | |
| 251 // whether or not it was previously precached, so delete any record of that | |
| 252 // URL having been precached from the URL table. | |
| 253 buffered_writes_.push_back( | 319 buffered_writes_.push_back( |
| 254 base::Bind(&PrecacheURLTable::DeleteURL, | 320 base::Bind(&PrecacheDatabase::UpdatePrecacheReferrerHostInternal, |
| 255 base::Unretained(&precache_url_table_), url)); | 321 GetWeakPtr(), hostname, manifest_id, fetch_time)); |
| 256 buffered_urls_.insert(url.spec()); | |
| 257 MaybePostFlush(); | 322 MaybePostFlush(); |
| 258 } | 323 } |
| 259 | 324 |
| 325 void PrecacheDatabase::UpdatePrecacheReferrerHostInternal( |
| 326 const std::string& hostname, |
| 327 int64_t manifest_id, |
| 328 const base::Time& fetch_time) { |
| 329 int64_t referrer_host_id = precache_referrer_host_table_.UpdateReferrerHost( |
| 330 hostname, manifest_id, fetch_time); |
| 331 if (referrer_host_id != PrecacheReferrerHostEntry::kInvalidId) { |
| 332 precache_url_table_.DeleteAllForReferrerHost(referrer_host_id); |
| 333 } |
| 334 } |
| 335 |
| 260 void PrecacheDatabase::RecordTimeSinceLastPrecache( | 336 void PrecacheDatabase::RecordTimeSinceLastPrecache( |
| 261 const base::Time& fetch_time) { | 337 const base::Time& fetch_time) { |
| 262 const base::Time& last_precache_timestamp = GetLastPrecacheTimestamp(); | 338 const base::Time& last_precache_timestamp = GetLastPrecacheTimestamp(); |
| 263 // It could still be null if the DB was not accessible. | 339 // It could still be null if the DB was not accessible. |
| 264 if (!last_precache_timestamp.is_null()) { | 340 if (!last_precache_timestamp.is_null()) { |
| 265 // This is the timespan (in seconds) between the last call to | 341 // This is the timespan (in seconds) between the last call to |
| 266 // PrecacheManager::StartPrecaching and the fetch time of a non-precache | 342 // PrecacheManager::StartPrecaching and the fetch time of a non-precache |
| 267 // URL. Please note that the session started by that call to | 343 // URL. Please note that the session started by that call to |
| 268 // PrecacheManager::StartPrecaching may not have precached this particular | 344 // PrecacheManager::StartPrecaching may not have precached this particular |
| 269 // URL or even any URL for that matter. | 345 // URL or even any URL for that matter. |
| (...skipping 82 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 352 | 428 |
| 353 void PrecacheDatabase::DeleteUnfinishedWork() { | 429 void PrecacheDatabase::DeleteUnfinishedWork() { |
| 354 precache_session_table_.DeleteUnfinishedWork(); | 430 precache_session_table_.DeleteUnfinishedWork(); |
| 355 } | 431 } |
| 356 | 432 |
| 357 base::WeakPtr<PrecacheDatabase> PrecacheDatabase::GetWeakPtr() { | 433 base::WeakPtr<PrecacheDatabase> PrecacheDatabase::GetWeakPtr() { |
| 358 return weak_factory_.GetWeakPtr(); | 434 return weak_factory_.GetWeakPtr(); |
| 359 } | 435 } |
| 360 | 436 |
| 361 } // namespace precache | 437 } // namespace precache |
| OLD | NEW |