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_NE(PrecacheReferrerHostEntry::kInvalidId, referrer_host_id); |
| 138 precache_url_table_.GetURLListForReferrerHost(referrer_host_id, used_urls, |
| 139 unused_urls); |
| 140 } |
| 141 |
| 142 void PrecacheDatabase::RecordURLPrefetchMetrics( |
| 143 const net::HttpResponseInfo& info, |
| 144 const base::TimeDelta& latency) { |
| 145 DCHECK(thread_checker_.CalledOnValidThread()); |
| 146 |
125 UMA_HISTOGRAM_TIMES("Precache.Latency.Prefetch", latency); | 147 UMA_HISTOGRAM_TIMES("Precache.Latency.Prefetch", latency); |
126 | 148 |
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."; | 149 DCHECK(info.headers) << "The headers are required to get the freshness."; |
139 if (info.headers) { | 150 if (info.headers) { |
140 UMA_HISTOGRAM_CUSTOM_COUNTS( | 151 UMA_HISTOGRAM_CUSTOM_COUNTS( |
141 "Precache.Freshness.Prefetch", | 152 "Precache.Freshness.Prefetch", |
142 info.headers->GetFreshnessLifetimes(info.response_time) | 153 info.headers->GetFreshnessLifetimes(info.response_time) |
143 .freshness.InSeconds(), | 154 .freshness.InSeconds(), |
144 base::TimeDelta::FromMinutes(5).InSeconds() /* min */, | 155 base::TimeDelta::FromMinutes(5).InSeconds() /* min */, |
145 base::TimeDelta::FromDays(356).InSeconds() /* max */, | 156 base::TimeDelta::FromDays(356).InSeconds() /* max */, |
146 100 /* bucket_count */); | 157 100 /* bucket_count */); |
147 } | 158 } |
| 159 } |
148 | 160 |
149 if (info.was_cached && !precache_url_table_.HasURL(url)) { | 161 void PrecacheDatabase::RecordURLPrefetch(const GURL& url, |
150 // Since the precache came from the cache, and there's no entry in the URL | 162 const std::string& referrer_host, |
151 // table for the URL, this means that the resource was already in the cache | 163 const base::Time& fetch_time, |
152 // because of user browsing. Therefore, this precache won't be considered as | 164 bool was_cached, |
153 // precache-motivated since it had no significant effect (besides a possible | 165 int64_t size) { |
154 // revalidation and a change in the cache LRU priority). | 166 DCHECK(thread_checker_.CalledOnValidThread()); |
| 167 |
| 168 if (!IsDatabaseAccessible()) { |
| 169 // Don't track anything if unable to access the database. |
155 return; | 170 return; |
156 } | 171 } |
157 | 172 |
158 if (!info.was_cached) { | 173 if (buffered_urls_.find(url.spec()) != buffered_urls_.end()) { |
| 174 // If the URL for this fetch is in the write buffer, then flush the write |
| 175 // buffer. |
| 176 Flush(); |
| 177 } |
| 178 |
| 179 if (!was_cached) { |
159 // The precache only counts as overhead if it was downloaded over the | 180 // The precache only counts as overhead if it was downloaded over the |
160 // network. | 181 // network. |
161 UMA_HISTOGRAM_COUNTS("Precache.DownloadedPrecacheMotivated", | 182 UMA_HISTOGRAM_COUNTS("Precache.DownloadedPrecacheMotivated", |
162 static_cast<base::HistogramBase::Sample>(size)); | 183 static_cast<base::HistogramBase::Sample>(size)); |
163 } | 184 } |
164 | 185 |
165 // Use the URL table to keep track of URLs that are in the cache thanks to | 186 // 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 | 187 // or already in the cache due to prior precaching are recorded as |
167 // to |fetch_time|. | 188 // precache-motivated. URLs that came from the cache and not recorded as |
168 buffered_writes_.push_back( | 189 // precached previously, were already in the cache because of user browsing. |
169 base::Bind(&PrecacheURLTable::AddURL, | 190 // Therefore, this precache will not be considered as precache-motivated, |
170 base::Unretained(&precache_url_table_), url, fetch_time)); | 191 // since it had no significant effect (besides a possible revalidation and a |
| 192 // change in the cache LRU priority). If a row for the URL already exists, |
| 193 // then the timestamp is updated. |
| 194 buffered_writes_.push_back(base::Bind( |
| 195 &PrecacheDatabase::RecordURLPrefetchInternal, GetWeakPtr(), url, |
| 196 referrer_host, !was_cached || precache_url_table_.IsURLPrecached(url), |
| 197 fetch_time)); |
171 buffered_urls_.insert(url.spec()); | 198 buffered_urls_.insert(url.spec()); |
172 MaybePostFlush(); | 199 MaybePostFlush(); |
173 } | 200 } |
174 | 201 |
| 202 void PrecacheDatabase::RecordURLPrefetchInternal( |
| 203 const GURL& url, |
| 204 const std::string& referrer_host, |
| 205 bool is_precached, |
| 206 const base::Time& fetch_time) { |
| 207 auto referrer_host_id = precache_referrer_host_table_.UpdateReferrerHost( |
| 208 referrer_host, 0, fetch_time); |
| 209 DCHECK_NE(referrer_host_id, PrecacheReferrerHostEntry::kInvalidId); |
| 210 precache_url_table_.AddURL(url, referrer_host_id, is_precached, fetch_time); |
| 211 } |
| 212 |
175 void PrecacheDatabase::RecordURLNonPrefetch(const GURL& url, | 213 void PrecacheDatabase::RecordURLNonPrefetch(const GURL& url, |
176 const base::TimeDelta& latency, | 214 const base::TimeDelta& latency, |
177 const base::Time& fetch_time, | 215 const base::Time& fetch_time, |
178 const net::HttpResponseInfo& info, | 216 const net::HttpResponseInfo& info, |
179 int64_t size, | 217 int64_t size, |
180 int host_rank, | 218 int host_rank, |
181 bool is_connection_cellular) { | 219 bool is_connection_cellular) { |
182 UMA_HISTOGRAM_TIMES("Precache.Latency.NonPrefetch", latency); | 220 UMA_HISTOGRAM_TIMES("Precache.Latency.NonPrefetch", latency); |
183 UMA_HISTOGRAM_ENUMERATION("Precache.CacheStatus.NonPrefetch", | 221 UMA_HISTOGRAM_ENUMERATION("Precache.CacheStatus.NonPrefetch", |
184 info.cache_entry_status, | 222 info.cache_entry_status, |
(...skipping 15 matching lines...) Expand all Loading... |
200 } | 238 } |
201 | 239 |
202 RecordTimeSinceLastPrecache(fetch_time); | 240 RecordTimeSinceLastPrecache(fetch_time); |
203 | 241 |
204 if (buffered_urls_.find(url.spec()) != buffered_urls_.end()) { | 242 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 | 243 // If the URL for this fetch is in the write buffer, then flush the write |
206 // buffer. | 244 // buffer. |
207 Flush(); | 245 Flush(); |
208 } | 246 } |
209 | 247 |
210 if (info.was_cached && !precache_url_table_.HasURL(url)) { | 248 bool is_precached = precache_url_table_.IsURLPrecachedAndUnused(url); |
| 249 if (info.was_cached && !is_precached) { |
211 // Ignore cache hits that precache can't take credit for. | 250 // Ignore cache hits that precache can't take credit for. |
212 return; | 251 return; |
213 } | 252 } |
214 | 253 |
215 base::HistogramBase::Sample size_sample = | 254 base::HistogramBase::Sample size_sample = |
216 static_cast<base::HistogramBase::Sample>(size); | 255 static_cast<base::HistogramBase::Sample>(size); |
217 if (!info.was_cached) { | 256 if (!info.was_cached) { |
218 // The fetch was served over the network during user browsing, so count it | 257 // The fetch was served over the network during user browsing, so count it |
219 // as downloaded non-precache bytes. | 258 // as downloaded non-precache bytes. |
220 UMA_HISTOGRAM_COUNTS("Precache.DownloadedNonPrecache", size_sample); | 259 UMA_HISTOGRAM_COUNTS("Precache.DownloadedNonPrecache", size_sample); |
221 if (is_connection_cellular) { | 260 if (is_connection_cellular) { |
222 UMA_HISTOGRAM_COUNTS("Precache.DownloadedNonPrecache.Cellular", | 261 UMA_HISTOGRAM_COUNTS("Precache.DownloadedNonPrecache.Cellular", |
223 size_sample); | 262 size_sample); |
224 } | 263 } |
| 264 // Since the resource has been fetched during user browsing, mark the URL as |
| 265 // used in the precache URL table, if any exists. The current fetch would |
| 266 // have put this resource in the cache regardless of whether or not it was |
| 267 // previously precached, so mark the URL as used. |
| 268 buffered_writes_.push_back( |
| 269 base::Bind(&PrecacheURLTable::SetURLAsNotPrecached, |
| 270 base::Unretained(&precache_url_table_), url)); |
| 271 buffered_urls_.insert(url.spec()); |
| 272 MaybePostFlush(); |
225 } else { // info.was_cached. | 273 } else { // info.was_cached. |
226 // The fetch was served from the cache, and since there's an entry for this | 274 // 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 | 275 // 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, | 276 // cache only because precaching put it there. Thus, precaching was helpful, |
229 // so count the fetch as saved bytes. | 277 // so count the fetch as saved bytes. |
230 UMA_HISTOGRAM_COUNTS("Precache.Saved", size_sample); | 278 UMA_HISTOGRAM_COUNTS("Precache.Saved", size_sample); |
231 if (is_connection_cellular) { | 279 if (is_connection_cellular) { |
232 UMA_HISTOGRAM_COUNTS("Precache.Saved.Cellular", size_sample); | 280 UMA_HISTOGRAM_COUNTS("Precache.Saved.Cellular", size_sample); |
233 } | 281 } |
234 | 282 |
235 DCHECK(info.headers) << "The headers are required to get the freshness."; | 283 DCHECK(info.headers) << "The headers are required to get the freshness."; |
236 if (info.headers) { | 284 if (info.headers) { |
237 // TODO(jamartin): Maybe report stale_while_validate as well. | 285 // TODO(jamartin): Maybe report stale_while_validate as well. |
238 UMA_HISTOGRAM_CUSTOM_COUNTS( | 286 UMA_HISTOGRAM_CUSTOM_COUNTS( |
239 "Precache.Saved.Freshness", | 287 "Precache.Saved.Freshness", |
240 info.headers->GetFreshnessLifetimes(info.response_time) | 288 info.headers->GetFreshnessLifetimes(info.response_time) |
241 .freshness.InSeconds(), | 289 .freshness.InSeconds(), |
242 base::TimeDelta::FromMinutes(5).InSeconds() /* min */, | 290 base::TimeDelta::FromMinutes(5).InSeconds() /* min */, |
243 base::TimeDelta::FromDays(356).InSeconds() /* max */, | 291 base::TimeDelta::FromDays(356).InSeconds() /* max */, |
244 100 /* bucket_count */); | 292 100 /* bucket_count */); |
245 } | 293 } |
| 294 |
| 295 buffered_writes_.push_back( |
| 296 base::Bind(&PrecacheURLTable::SetPrecachedURLAsUsed, |
| 297 base::Unretained(&precache_url_table_), url)); |
| 298 buffered_urls_.insert(url.spec()); |
| 299 MaybePostFlush(); |
| 300 } |
| 301 } |
| 302 |
| 303 void PrecacheDatabase::UpdatePrecacheReferrerHost( |
| 304 const std::string& hostname, |
| 305 int64_t manifest_id, |
| 306 const base::Time& fetch_time) { |
| 307 DCHECK(thread_checker_.CalledOnValidThread()); |
| 308 |
| 309 if (!IsDatabaseAccessible()) { |
| 310 // Don't track anything if unable to access the database. |
| 311 return; |
246 } | 312 } |
247 | 313 |
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( | 314 buffered_writes_.push_back( |
254 base::Bind(&PrecacheURLTable::DeleteURL, | 315 base::Bind(&PrecacheDatabase::UpdatePrecacheReferrerHostInternal, |
255 base::Unretained(&precache_url_table_), url)); | 316 GetWeakPtr(), hostname, manifest_id, fetch_time)); |
256 buffered_urls_.insert(url.spec()); | |
257 MaybePostFlush(); | 317 MaybePostFlush(); |
258 } | 318 } |
259 | 319 |
| 320 void PrecacheDatabase::UpdatePrecacheReferrerHostInternal( |
| 321 const std::string& hostname, |
| 322 int64_t manifest_id, |
| 323 const base::Time& fetch_time) { |
| 324 precache_referrer_host_table_.UpdateReferrerHost(hostname, manifest_id, |
| 325 fetch_time); |
| 326 } |
| 327 |
260 void PrecacheDatabase::RecordTimeSinceLastPrecache( | 328 void PrecacheDatabase::RecordTimeSinceLastPrecache( |
261 const base::Time& fetch_time) { | 329 const base::Time& fetch_time) { |
262 const base::Time& last_precache_timestamp = GetLastPrecacheTimestamp(); | 330 const base::Time& last_precache_timestamp = GetLastPrecacheTimestamp(); |
263 // It could still be null if the DB was not accessible. | 331 // It could still be null if the DB was not accessible. |
264 if (!last_precache_timestamp.is_null()) { | 332 if (!last_precache_timestamp.is_null()) { |
265 // This is the timespan (in seconds) between the last call to | 333 // This is the timespan (in seconds) between the last call to |
266 // PrecacheManager::StartPrecaching and the fetch time of a non-precache | 334 // PrecacheManager::StartPrecaching and the fetch time of a non-precache |
267 // URL. Please note that the session started by that call to | 335 // URL. Please note that the session started by that call to |
268 // PrecacheManager::StartPrecaching may not have precached this particular | 336 // PrecacheManager::StartPrecaching may not have precached this particular |
269 // URL or even any URL for that matter. | 337 // URL or even any URL for that matter. |
(...skipping 82 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
352 | 420 |
353 void PrecacheDatabase::DeleteUnfinishedWork() { | 421 void PrecacheDatabase::DeleteUnfinishedWork() { |
354 precache_session_table_.DeleteUnfinishedWork(); | 422 precache_session_table_.DeleteUnfinishedWork(); |
355 } | 423 } |
356 | 424 |
357 base::WeakPtr<PrecacheDatabase> PrecacheDatabase::GetWeakPtr() { | 425 base::WeakPtr<PrecacheDatabase> PrecacheDatabase::GetWeakPtr() { |
358 return weak_factory_.GetWeakPtr(); | 426 return weak_factory_.GetWeakPtr(); |
359 } | 427 } |
360 | 428 |
361 } // namespace precache | 429 } // namespace precache |
OLD | NEW |