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

Side by Side Diff: components/password_manager/core/browser/facet_manager.cc

Issue 947563002: Add prefetch support to AffiliationBackend. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Comment phrasing. Created 5 years, 10 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 2015 The Chromium Authors. All rights reserved. 1 // Copyright 2015 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 // Note: Read the class comment of AffiliationService for the definition of the
6 // terms used below.
7 //
8 // On-demand fetching strategy
9 //
10 // A GetAffiliations() request concerning facet X will be served from the cache
11 // as long as the cache contains fresh affiliation information for facet X, that
12 // is, if there is an equivalence class in the cache that contains X and has
13 // been fetched less than |kCacheHardExpiryInHours| hours ago.
14 //
15 // Otherwise, a network request is issued against the Affiliation API as soon as
16 // possible, that is, immediately if there is no fetch in flight, or right after
17 // completion of the fetch in flight if there is one, provided that the required
18 // data is not incidentally returned by the first fetch.
19 //
20 //
21 // Proactive fetching strategy
22 //
23 // A Prefetch() request concerning facet Y can trigger an initial network fetch,
24 // or periodic refetches only when:
25 // * The prefetch request is not already expired, i.e., its |keep_fresh_until|
26 // threshold is strictly in the future.
27 // * Affiliation information in the cache pertaining to facet Y will get stale
28 // strictly before the specified |keep_fresh_until| threshold.
29 //
30 // An initial fetch will be issued as soon as possible if, in addition to the
31 // two necessery conditions above, and at the time of the Prefetch() call, the
32 // cache contains no affiliation information regarding facet Y, or if the data
33 // in the cache is already near-stale or stale.
34 //
35 // A refetch will be issued every time the data in the cache regarding facet Y
36 // becomes near-stale, that is, exactly |kCacheSoftExpiry| hours after the last
37 // fetch, provided that the above two necessary conditions are also met.
38 //
39 //
40 // Cache freshness terminology
41 //
42 //
43 // Fetch (t=0) kCacheSoftExpiry kCacheHardExpiry
44 // / / /
45 // ---o------------------------o-----------------------o-----------------> t
46 // | | |
47 // | [-- Cache near-stale --)|
48 // [--------------- Cache is fresh ----------------)[-- Cache is stale ..
49 //
50
5 #include "components/password_manager/core/browser/facet_manager.h" 51 #include "components/password_manager/core/browser/facet_manager.h"
6 52
7 #include "base/bind.h" 53 #include "base/bind.h"
8 #include "base/location.h" 54 #include "base/location.h"
9 #include "base/task_runner.h" 55 #include "base/task_runner.h"
56 #include "base/time/clock.h"
57 #include "base/time/time.h"
10 #include "components/password_manager/core/browser/facet_manager_host.h" 58 #include "components/password_manager/core/browser/facet_manager_host.h"
11 59
12 namespace password_manager { 60 namespace password_manager {
13 61
14 namespace { 62 namespace {
15 63
16 // The duration after which cached affiliation data is considered stale and will 64 // The duration after which cached affiliation data is considered near-stale.
17 // not be used to serve requests any longer. 65 const int kCacheSoftExpiryInHours = 21;
18 const int kCacheLifetimeInHours = 24; 66
67 // The duration after which cached affiliation data is considered stale.
68 const int kCacheHardExpiryInHours = 24;
19 69
20 } // namespace 70 } // namespace
21 71
22 // Encapsulates the details of a pending GetAffiliations() request. 72 // Encapsulates the details of a pending GetAffiliations() request.
23 struct FacetManager::RequestInfo { 73 struct FacetManager::RequestInfo {
24 AffiliationService::ResultCallback callback; 74 AffiliationService::ResultCallback callback;
25 scoped_refptr<base::TaskRunner> callback_task_runner; 75 scoped_refptr<base::TaskRunner> callback_task_runner;
26 }; 76 };
27 77
28 FacetManager::FacetManager(FacetManagerHost* host, const FacetURI& facet_uri) 78 FacetManager::FacetManager(const FacetURI& facet_uri,
29 : backend_(host), 79 FacetManagerHost* backend,
30 facet_uri_(facet_uri), 80 base::Clock* clock)
31 last_update_time_(backend_->ReadLastUpdateTimeFromDatabase(facet_uri)) { 81 : facet_uri_(facet_uri), backend_(backend), clock_(clock) {
82 AffiliatedFacetsWithUpdateTime affiliations;
83 if (backend_->ReadAffiliationsFromDatabase(facet_uri_, &affiliations))
84 last_update_time_ = affiliations.last_update_time;
32 } 85 }
33 86
34 FacetManager::~FacetManager() { 87 FacetManager::~FacetManager() {
35 // The manager will only be destroyed while there are pending requests if the 88 // The manager will be destroyed while there are pending requests only if the
36 // entire backend is going. Call failure on pending requests in this case. 89 // entire backend is going away. Fail pending requests in this case.
37 for (const auto& request_info : pending_requests_) 90 for (const auto& request_info : pending_requests_)
38 ServeRequestWithFailure(request_info); 91 ServeRequestWithFailure(request_info);
39 } 92 }
40 93
41 void FacetManager::GetAffiliations( 94 void FacetManager::GetAffiliations(
42 bool cached_only, 95 bool cached_only,
43 const AffiliationService::ResultCallback& callback, 96 const AffiliationService::ResultCallback& callback,
44 const scoped_refptr<base::TaskRunner>& callback_task_runner) { 97 const scoped_refptr<base::TaskRunner>& callback_task_runner) {
45 RequestInfo request_info; 98 RequestInfo request_info;
46 request_info.callback = callback; 99 request_info.callback = callback;
47 request_info.callback_task_runner = callback_task_runner; 100 request_info.callback_task_runner = callback_task_runner;
48 if (IsCachedDataFresh()) { 101 if (IsCachedDataFresh()) {
49 AffiliatedFacetsWithUpdateTime affiliation; 102 AffiliatedFacetsWithUpdateTime affiliation;
50 if (!backend_->ReadAffiliationsFromDatabase(facet_uri_, &affiliation)) { 103 if (!backend_->ReadAffiliationsFromDatabase(facet_uri_, &affiliation)) {
51 // TODO(engedy): Implement this. crbug.com/437865. 104 // TODO(engedy): Implement this. crbug.com/437865.
52 NOTIMPLEMENTED(); 105 NOTIMPLEMENTED();
53 } 106 }
54 DCHECK_EQ(affiliation.last_update_time, last_update_time_) << facet_uri_; 107 DCHECK_EQ(affiliation.last_update_time, last_update_time_) << facet_uri_;
55 ServeRequestWithSuccess(request_info, affiliation.facets); 108 ServeRequestWithSuccess(request_info, affiliation.facets);
56 } else if (cached_only) { 109 } else if (cached_only) {
57 ServeRequestWithFailure(request_info); 110 ServeRequestWithFailure(request_info);
58 } else { 111 } else {
59 pending_requests_.push_back(request_info); 112 pending_requests_.push_back(request_info);
60 backend_->SignalNeedNetworkRequest(); 113 backend_->SignalNeedNetworkRequest();
61 } 114 }
62 } 115 }
63 116
117 void FacetManager::Prefetch(const base::Time& keep_fresh_until) {
118 keep_fresh_untils_.insert(keep_fresh_until);
119
120 // If an initial fetch if needed, trigger that (the refetch will be scheduled
121 // once the initial fetch completes). Otherwise schedule the next refetch.
122 base::Time next_required_fetch(GetNextRequiredFetchTimeDueToPrefetch());
123 if (next_required_fetch <= clock_->Now())
124 backend_->SignalNeedNetworkRequest();
125 else if (next_required_fetch < base::Time::Max())
126 backend_->RequestNotificationAtTime(facet_uri_, next_required_fetch);
127
128 // For a finite |keep_fresh_until|, schedule a callback so that once the
129 // prefetch expires, it can be removed from |keep_fresh_untils_|, and also the
130 // manager can get a chance to be destroyed unless it is otherwise needed.
131 if (keep_fresh_until > clock_->Now() && keep_fresh_until < base::Time::Max())
132 backend_->RequestNotificationAtTime(facet_uri_, keep_fresh_until);
133 }
134
135 void FacetManager::CancelPrefetch(const base::Time& keep_fresh_until) {
136 auto iter = keep_fresh_untils_.find(keep_fresh_until);
137 if (iter != keep_fresh_untils_.end())
138 keep_fresh_untils_.erase(iter);
139 }
140
64 void FacetManager::OnFetchSucceeded( 141 void FacetManager::OnFetchSucceeded(
65 const AffiliatedFacetsWithUpdateTime& affiliation) { 142 const AffiliatedFacetsWithUpdateTime& affiliation) {
66 last_update_time_ = affiliation.last_update_time; 143 last_update_time_ = affiliation.last_update_time;
67 DCHECK(IsCachedDataFresh()) << facet_uri_; 144 DCHECK(IsCachedDataFresh()) << facet_uri_;
68 for (const auto& request_info : pending_requests_) 145 for (const auto& request_info : pending_requests_)
69 ServeRequestWithSuccess(request_info, affiliation.facets); 146 ServeRequestWithSuccess(request_info, affiliation.facets);
70 pending_requests_.clear(); 147 pending_requests_.clear();
148
149 base::Time next_required_fetch(GetNextRequiredFetchTimeDueToPrefetch());
150 if (next_required_fetch < base::Time::Max())
151 backend_->RequestNotificationAtTime(facet_uri_, next_required_fetch);
152 }
153
154 void FacetManager::NotifyAtRequestedTime() {
155 base::Time next_required_fetch(GetNextRequiredFetchTimeDueToPrefetch());
156 if (next_required_fetch <= clock_->Now())
157 backend_->SignalNeedNetworkRequest();
158 else if (next_required_fetch < base::Time::Max())
159 backend_->RequestNotificationAtTime(facet_uri_, next_required_fetch);
160
161 auto iter_first_non_expired = keep_fresh_untils_.upper_bound(clock_->Now());
Mike West 2015/02/25 10:33:06 Shouldn't you clear out the list of expired "keep-
engedy 2015/03/10 11:01:00 Normally they should never expire at those time, b
162 keep_fresh_untils_.erase(keep_fresh_untils_.begin(), iter_first_non_expired);
71 } 163 }
72 164
73 bool FacetManager::CanBeDiscarded() const { 165 bool FacetManager::CanBeDiscarded() const {
74 return pending_requests_.empty(); 166 return pending_requests_.empty() &&
167 GetMaximumKeepFreshUntil() <= clock_->Now();
75 } 168 }
76 169
77 bool FacetManager::DoesRequireFetch() const { 170 bool FacetManager::DoesRequireFetch() const {
78 return !pending_requests_.empty() && !IsCachedDataFresh(); 171 return (!pending_requests_.empty() && !IsCachedDataFresh()) ||
172 GetNextRequiredFetchTimeDueToPrefetch() <= clock_->Now();
79 } 173 }
80 174
81 base::Time FacetManager::GetCacheExpirationTime() const { 175 base::Time FacetManager::GetCacheSoftExpiryTime() const {
82 if (last_update_time_.is_null()) 176 base::TimeDelta expiry(base::TimeDelta::FromHours(kCacheSoftExpiryInHours));
83 return base::Time(); 177 return !last_update_time_.is_null() ? last_update_time_ + expiry
84 return last_update_time_ + base::TimeDelta::FromHours(kCacheLifetimeInHours); 178 : base::Time();
179 }
180
181 base::Time FacetManager::GetCacheHardExpiryTime() const {
182 base::TimeDelta expiry(base::TimeDelta::FromHours(kCacheHardExpiryInHours));
183 return !last_update_time_.is_null() ? last_update_time_ + expiry
Mike West 2015/02/25 10:33:06 Do you need to distinguish between the first fetch
engedy 2015/03/10 11:01:00 Done.
184 : base::Time();
85 } 185 }
86 186
87 bool FacetManager::IsCachedDataFresh() const { 187 bool FacetManager::IsCachedDataFresh() const {
88 return backend_->GetCurrentTime() < GetCacheExpirationTime(); 188 return clock_->Now() < GetCacheHardExpiryTime();
189 }
190
191 base::Time FacetManager::GetMaximumKeepFreshUntil() const {
192 return !keep_fresh_untils_.empty() ? *keep_fresh_untils_.rbegin()
Mike West 2015/02/25 10:33:06 Nit: My brain refuses to believe that 'untils' is
engedy 2015/03/10 11:01:00 I have renamed to |keep_fresh_until_thresholds_|.
193 : base::Time();
194 }
195
196 base::Time FacetManager::GetNextRequiredFetchTimeDueToPrefetch() const {
197 // If there is at least one non-expired Prefetch() request that requires the
198 // data to be kept fresh until some time later than its current hard expiry
199 // time, then a fetch is needed once the cached data becomes near-stale.
200 if (clock_->Now() < GetMaximumKeepFreshUntil() &&
Mike West 2015/02/25 10:33:06 `<=`?
engedy 2015/03/10 11:01:00 I have clarified in the comment in affiliation_ser
201 GetCacheHardExpiryTime() < GetMaximumKeepFreshUntil()) {
Mike West 2015/02/25 10:33:06 `<=`?
engedy 2015/03/10 11:01:00 Same here.
202 return GetCacheSoftExpiryTime();
203 }
204 return base::Time::Max();
89 } 205 }
90 206
91 // static 207 // static
92 void FacetManager::ServeRequestWithSuccess( 208 void FacetManager::ServeRequestWithSuccess(
93 const RequestInfo& request_info, 209 const RequestInfo& request_info,
94 const AffiliatedFacets& affiliation) { 210 const AffiliatedFacets& affiliation) {
95 request_info.callback_task_runner->PostTask( 211 request_info.callback_task_runner->PostTask(
96 FROM_HERE, base::Bind(request_info.callback, affiliation, true)); 212 FROM_HERE, base::Bind(request_info.callback, affiliation, true));
97 } 213 }
98 214
99 // static 215 // static
100 void FacetManager::ServeRequestWithFailure(const RequestInfo& request_info) { 216 void FacetManager::ServeRequestWithFailure(const RequestInfo& request_info) {
101 request_info.callback_task_runner->PostTask( 217 request_info.callback_task_runner->PostTask(
102 FROM_HERE, base::Bind(request_info.callback, AffiliatedFacets(), false)); 218 FROM_HERE, base::Bind(request_info.callback, AffiliatedFacets(), false));
103 } 219 }
104 220
105 } // namespace password_manager 221 } // namespace password_manager
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698