OLD | NEW |
| (Empty) |
1 // Copyright 2014 The Chromium Authors. All rights reserved. | |
2 // Use of this source code is governed by a BSD-style license that can be | |
3 // found in the LICENSE file. | |
4 | |
5 #include "components/sync_driver/model_association_manager.h" | |
6 | |
7 #include <stddef.h> | |
8 #include <stdint.h> | |
9 | |
10 #include <algorithm> | |
11 #include <functional> | |
12 | |
13 #include "base/logging.h" | |
14 #include "base/macros.h" | |
15 #include "base/message_loop/message_loop.h" | |
16 #include "base/metrics/histogram_macros.h" | |
17 #include "base/trace_event/trace_event.h" | |
18 #include "components/sync/api/sync_merge_result.h" | |
19 #include "components/sync/base/model_type.h" | |
20 | |
21 using syncer::ModelTypeSet; | |
22 | |
23 namespace sync_driver { | |
24 | |
25 namespace { | |
26 | |
27 static const syncer::ModelType kStartOrder[] = { | |
28 syncer::NIGORI, // Listed for completeness. | |
29 syncer::DEVICE_INFO, // Listed for completeness. | |
30 syncer::EXPERIMENTS, // Listed for completeness. | |
31 syncer::PROXY_TABS, // Listed for completeness. | |
32 | |
33 // Kick off the association of the non-UI types first so they can associate | |
34 // in parallel with the UI types. | |
35 syncer::PASSWORDS, | |
36 syncer::AUTOFILL, | |
37 syncer::AUTOFILL_PROFILE, | |
38 syncer::AUTOFILL_WALLET_DATA, | |
39 syncer::AUTOFILL_WALLET_METADATA, | |
40 syncer::EXTENSION_SETTINGS, | |
41 syncer::APP_SETTINGS, | |
42 syncer::TYPED_URLS, | |
43 syncer::HISTORY_DELETE_DIRECTIVES, | |
44 syncer::SYNCED_NOTIFICATIONS, | |
45 syncer::SYNCED_NOTIFICATION_APP_INFO, | |
46 | |
47 // UI thread data types. | |
48 syncer::BOOKMARKS, | |
49 syncer::SUPERVISED_USERS, // Syncing supervised users on initial login | |
50 // might block creating a new supervised user, | |
51 // so we want to do it early. | |
52 syncer::PREFERENCES, | |
53 syncer::PRIORITY_PREFERENCES, | |
54 syncer::EXTENSIONS, | |
55 syncer::APPS, | |
56 syncer::APP_LIST, | |
57 syncer::ARC_PACKAGE, | |
58 syncer::THEMES, | |
59 syncer::SEARCH_ENGINES, | |
60 syncer::SESSIONS, | |
61 syncer::APP_NOTIFICATIONS, | |
62 syncer::DICTIONARY, | |
63 syncer::FAVICON_IMAGES, | |
64 syncer::FAVICON_TRACKING, | |
65 syncer::SUPERVISED_USER_SETTINGS, | |
66 syncer::SUPERVISED_USER_SHARED_SETTINGS, | |
67 syncer::SUPERVISED_USER_WHITELISTS, | |
68 syncer::ARTICLES, | |
69 syncer::WIFI_CREDENTIALS, | |
70 }; | |
71 | |
72 static_assert(arraysize(kStartOrder) == | |
73 syncer::MODEL_TYPE_COUNT - syncer::FIRST_REAL_MODEL_TYPE, | |
74 "kStartOrder must have MODEL_TYPE_COUNT - " | |
75 "FIRST_REAL_MODEL_TYPE elements"); | |
76 | |
77 // The amount of time we wait for association to finish. If some types haven't | |
78 // finished association by the time, DataTypeManager is notified of the | |
79 // unfinished types. | |
80 const int64_t kAssociationTimeOutInSeconds = 600; | |
81 | |
82 syncer::DataTypeAssociationStats BuildAssociationStatsFromMergeResults( | |
83 const syncer::SyncMergeResult& local_merge_result, | |
84 const syncer::SyncMergeResult& syncer_merge_result, | |
85 const base::TimeDelta& association_wait_time, | |
86 const base::TimeDelta& association_time) { | |
87 DCHECK_EQ(local_merge_result.model_type(), syncer_merge_result.model_type()); | |
88 syncer::DataTypeAssociationStats stats; | |
89 stats.had_error = local_merge_result.error().IsSet() || | |
90 syncer_merge_result.error().IsSet(); | |
91 stats.num_local_items_before_association = | |
92 local_merge_result.num_items_before_association(); | |
93 stats.num_sync_items_before_association = | |
94 syncer_merge_result.num_items_before_association(); | |
95 stats.num_local_items_after_association = | |
96 local_merge_result.num_items_after_association(); | |
97 stats.num_sync_items_after_association = | |
98 syncer_merge_result.num_items_after_association(); | |
99 stats.num_local_items_added = | |
100 local_merge_result.num_items_added(); | |
101 stats.num_local_items_deleted = | |
102 local_merge_result.num_items_deleted(); | |
103 stats.num_local_items_modified = | |
104 local_merge_result.num_items_modified(); | |
105 stats.local_version_pre_association = | |
106 local_merge_result.pre_association_version(); | |
107 stats.num_sync_items_added = | |
108 syncer_merge_result.num_items_added(); | |
109 stats.num_sync_items_deleted = | |
110 syncer_merge_result.num_items_deleted(); | |
111 stats.num_sync_items_modified = | |
112 syncer_merge_result.num_items_modified(); | |
113 stats.sync_version_pre_association = | |
114 syncer_merge_result.pre_association_version(); | |
115 stats.association_wait_time = association_wait_time; | |
116 stats.association_time = association_time; | |
117 return stats; | |
118 } | |
119 | |
120 } // namespace | |
121 | |
122 ModelAssociationManager::ModelAssociationManager( | |
123 const DataTypeController::TypeMap* controllers, | |
124 ModelAssociationManagerDelegate* processor) | |
125 : state_(IDLE), | |
126 controllers_(controllers), | |
127 delegate_(processor), | |
128 configure_status_(DataTypeManager::UNKNOWN), | |
129 notified_about_ready_for_configure_(false), | |
130 weak_ptr_factory_(this) { | |
131 // Ensure all data type controllers are stopped. | |
132 for (DataTypeController::TypeMap::const_iterator it = controllers_->begin(); | |
133 it != controllers_->end(); ++it) { | |
134 DCHECK_EQ(DataTypeController::NOT_RUNNING, (*it).second->state()); | |
135 } | |
136 } | |
137 | |
138 ModelAssociationManager::~ModelAssociationManager() { | |
139 } | |
140 | |
141 void ModelAssociationManager::Initialize(syncer::ModelTypeSet desired_types) { | |
142 // state_ can be INITIALIZED if types are reconfigured when | |
143 // data is being downloaded, so StartAssociationAsync() is never called for | |
144 // the first configuration. | |
145 DCHECK_NE(ASSOCIATING, state_); | |
146 | |
147 // Only keep types that have controllers. | |
148 desired_types_.Clear(); | |
149 for (syncer::ModelTypeSet::Iterator it = desired_types.First(); | |
150 it.Good(); it.Inc()) { | |
151 if (controllers_->find(it.Get()) != controllers_->end()) | |
152 desired_types_.Put(it.Get()); | |
153 } | |
154 | |
155 DVLOG(1) << "ModelAssociationManager: Initializing for " | |
156 << syncer::ModelTypeSetToString(desired_types_); | |
157 | |
158 state_ = INITIALIZED; | |
159 notified_about_ready_for_configure_ = false; | |
160 | |
161 StopDisabledTypes(); | |
162 LoadEnabledTypes(); | |
163 } | |
164 | |
165 void ModelAssociationManager::StopDatatype( | |
166 const syncer::SyncError& error, | |
167 DataTypeController* dtc) { | |
168 loaded_types_.Remove(dtc->type()); | |
169 associated_types_.Remove(dtc->type()); | |
170 associating_types_.Remove(dtc->type()); | |
171 | |
172 if (error.IsSet() || dtc->state() != DataTypeController::NOT_RUNNING) { | |
173 // If an error was set, the delegate must be informed of the error. | |
174 delegate_->OnSingleDataTypeWillStop(dtc->type(), error); | |
175 dtc->Stop(); | |
176 } | |
177 } | |
178 | |
179 void ModelAssociationManager::StopDisabledTypes() { | |
180 DVLOG(1) << "ModelAssociationManager: Stopping disabled types."; | |
181 for (DataTypeController::TypeMap::const_iterator it = controllers_->begin(); | |
182 it != controllers_->end(); ++it) { | |
183 DataTypeController* dtc = (*it).second.get(); | |
184 if (dtc->state() != DataTypeController::NOT_RUNNING && | |
185 !desired_types_.Has(dtc->type())) { | |
186 DVLOG(1) << "ModelAssociationManager: stop " << dtc->name(); | |
187 StopDatatype(syncer::SyncError(), dtc); | |
188 } | |
189 } | |
190 } | |
191 | |
192 void ModelAssociationManager::LoadEnabledTypes() { | |
193 // Load in kStartOrder. | |
194 for (size_t i = 0; i < arraysize(kStartOrder); i++) { | |
195 syncer::ModelType type = kStartOrder[i]; | |
196 if (!desired_types_.Has(type)) | |
197 continue; | |
198 | |
199 DCHECK(controllers_->find(type) != controllers_->end()); | |
200 DataTypeController* dtc = controllers_->find(type)->second.get(); | |
201 if (dtc->state() == DataTypeController::NOT_RUNNING) { | |
202 DCHECK(!loaded_types_.Has(dtc->type())); | |
203 DCHECK(!associated_types_.Has(dtc->type())); | |
204 dtc->LoadModels(base::Bind(&ModelAssociationManager::ModelLoadCallback, | |
205 weak_ptr_factory_.GetWeakPtr())); | |
206 } | |
207 } | |
208 NotifyDelegateIfReadyForConfigure(); | |
209 } | |
210 | |
211 void ModelAssociationManager::StartAssociationAsync( | |
212 const syncer::ModelTypeSet& types_to_associate) { | |
213 DCHECK_EQ(INITIALIZED, state_); | |
214 DVLOG(1) << "Starting association for " | |
215 << syncer::ModelTypeSetToString(types_to_associate); | |
216 state_ = ASSOCIATING; | |
217 | |
218 association_start_time_ = base::TimeTicks::Now(); | |
219 | |
220 requested_types_ = types_to_associate; | |
221 | |
222 associating_types_ = types_to_associate; | |
223 associating_types_.RetainAll(desired_types_); | |
224 associating_types_.RemoveAll(associated_types_); | |
225 | |
226 // Assume success. | |
227 configure_status_ = DataTypeManager::OK; | |
228 | |
229 // Done if no types to associate. | |
230 if (associating_types_.Empty()) { | |
231 ModelAssociationDone(INITIALIZED); | |
232 return; | |
233 } | |
234 | |
235 timer_.Start(FROM_HERE, | |
236 base::TimeDelta::FromSeconds(kAssociationTimeOutInSeconds), | |
237 base::Bind(&ModelAssociationManager::ModelAssociationDone, | |
238 weak_ptr_factory_.GetWeakPtr(), | |
239 INITIALIZED)); | |
240 | |
241 // Start association of types that are loaded in specified order. | |
242 for (size_t i = 0; i < arraysize(kStartOrder); i++) { | |
243 syncer::ModelType type = kStartOrder[i]; | |
244 if (!associating_types_.Has(type) || !loaded_types_.Has(type)) | |
245 continue; | |
246 | |
247 DataTypeController* dtc = controllers_->find(type)->second.get(); | |
248 DCHECK(DataTypeController::MODEL_LOADED == dtc->state() || | |
249 DataTypeController::ASSOCIATING == dtc->state()); | |
250 if (dtc->state() == DataTypeController::MODEL_LOADED) { | |
251 TRACE_EVENT_ASYNC_BEGIN1("sync", "ModelAssociation", | |
252 dtc, | |
253 "DataType", | |
254 ModelTypeToString(type)); | |
255 | |
256 dtc->StartAssociating( | |
257 base::Bind(&ModelAssociationManager::TypeStartCallback, | |
258 weak_ptr_factory_.GetWeakPtr(), | |
259 type, base::TimeTicks::Now())); | |
260 } | |
261 } | |
262 } | |
263 | |
264 void ModelAssociationManager::Stop() { | |
265 // Ignore callbacks from controllers. | |
266 weak_ptr_factory_.InvalidateWeakPtrs(); | |
267 | |
268 // Stop started data types. | |
269 for (DataTypeController::TypeMap::const_iterator it = controllers_->begin(); | |
270 it != controllers_->end(); ++it) { | |
271 DataTypeController* dtc = (*it).second.get(); | |
272 if (dtc->state() != DataTypeController::NOT_RUNNING) { | |
273 StopDatatype(syncer::SyncError(), dtc); | |
274 DVLOG(1) << "ModelAssociationManager: Stopped " << dtc->name(); | |
275 } | |
276 } | |
277 | |
278 desired_types_.Clear(); | |
279 loaded_types_.Clear(); | |
280 associated_types_.Clear(); | |
281 | |
282 if (state_ == ASSOCIATING) { | |
283 if (configure_status_ == DataTypeManager::OK) | |
284 configure_status_ = DataTypeManager::ABORTED; | |
285 DVLOG(1) << "ModelAssociationManager: Calling OnModelAssociationDone"; | |
286 ModelAssociationDone(IDLE); | |
287 } else { | |
288 DCHECK(associating_types_.Empty()); | |
289 DCHECK(requested_types_.Empty()); | |
290 state_ = IDLE; | |
291 } | |
292 } | |
293 | |
294 void ModelAssociationManager::ModelLoadCallback(syncer::ModelType type, | |
295 syncer::SyncError error) { | |
296 DVLOG(1) << "ModelAssociationManager: ModelLoadCallback for " | |
297 << syncer::ModelTypeToString(type); | |
298 | |
299 if (error.IsSet()) { | |
300 syncer::SyncMergeResult local_merge_result(type); | |
301 local_merge_result.set_error(error); | |
302 TypeStartCallback(type, | |
303 base::TimeTicks::Now(), | |
304 DataTypeController::ASSOCIATION_FAILED, | |
305 local_merge_result, | |
306 syncer::SyncMergeResult(type)); | |
307 return; | |
308 } | |
309 | |
310 // This happens when slow loading type is disabled by new configuration. | |
311 if (!desired_types_.Has(type)) | |
312 return; | |
313 | |
314 DCHECK(!loaded_types_.Has(type)); | |
315 loaded_types_.Put(type); | |
316 NotifyDelegateIfReadyForConfigure(); | |
317 if (associating_types_.Has(type)) { | |
318 DataTypeController* dtc = controllers_->find(type)->second.get(); | |
319 // If initial sync was done for this datatype then | |
320 // NotifyDelegateIfReadyForConfigure possibly already triggered model | |
321 // association and StartAssociating was already called for this type. To | |
322 // ensure StartAssociating is called only once only make a call if state is | |
323 // MODEL_LOADED. | |
324 // TODO(pavely): Add test for this scenario in DataTypeManagerImpl | |
325 // unittests. | |
326 if (dtc->state() == DataTypeController::MODEL_LOADED) { | |
327 dtc->StartAssociating(base::Bind( | |
328 &ModelAssociationManager::TypeStartCallback, | |
329 weak_ptr_factory_.GetWeakPtr(), type, base::TimeTicks::Now())); | |
330 } | |
331 } | |
332 } | |
333 | |
334 void ModelAssociationManager::TypeStartCallback( | |
335 syncer::ModelType type, | |
336 base::TimeTicks type_start_time, | |
337 DataTypeController::ConfigureResult start_result, | |
338 const syncer::SyncMergeResult& local_merge_result, | |
339 const syncer::SyncMergeResult& syncer_merge_result) { | |
340 if (desired_types_.Has(type) && | |
341 !DataTypeController::IsSuccessfulResult(start_result)) { | |
342 DVLOG(1) << "ModelAssociationManager: Type encountered an error."; | |
343 desired_types_.Remove(type); | |
344 DataTypeController* dtc = controllers_->find(type)->second.get(); | |
345 StopDatatype(local_merge_result.error(), dtc); | |
346 NotifyDelegateIfReadyForConfigure(); | |
347 | |
348 // Update configuration result. | |
349 if (start_result == DataTypeController::UNRECOVERABLE_ERROR) | |
350 configure_status_ = DataTypeManager::UNRECOVERABLE_ERROR; | |
351 } | |
352 | |
353 // This happens when a slow associating type is disabled or if a type | |
354 // disables itself after initial configuration. | |
355 if (!desired_types_.Has(type)) { | |
356 // It's possible all types failed to associate, in which case association | |
357 // is complete. | |
358 if (state_ == ASSOCIATING && associating_types_.Empty()) | |
359 ModelAssociationDone(INITIALIZED); | |
360 return; | |
361 } | |
362 | |
363 DCHECK(!associated_types_.Has(type)); | |
364 DCHECK(DataTypeController::IsSuccessfulResult(start_result)); | |
365 associated_types_.Put(type); | |
366 | |
367 if (state_ != ASSOCIATING) | |
368 return; | |
369 | |
370 TRACE_EVENT_ASYNC_END1("sync", "ModelAssociation", | |
371 controllers_->find(type)->second.get(), | |
372 "DataType", | |
373 ModelTypeToString(type)); | |
374 | |
375 // Track the merge results if we succeeded or an association failure | |
376 // occurred. | |
377 if (syncer::ProtocolTypes().Has(type)) { | |
378 base::TimeDelta association_wait_time = | |
379 std::max(base::TimeDelta(), type_start_time - association_start_time_); | |
380 base::TimeDelta association_time = | |
381 base::TimeTicks::Now() - type_start_time; | |
382 syncer::DataTypeAssociationStats stats = | |
383 BuildAssociationStatsFromMergeResults(local_merge_result, | |
384 syncer_merge_result, | |
385 association_wait_time, | |
386 association_time); | |
387 delegate_->OnSingleDataTypeAssociationDone(type, stats); | |
388 } | |
389 | |
390 associating_types_.Remove(type); | |
391 | |
392 if (associating_types_.Empty()) | |
393 ModelAssociationDone(INITIALIZED); | |
394 } | |
395 | |
396 void ModelAssociationManager::ModelAssociationDone(State new_state) { | |
397 DCHECK_NE(IDLE, state_); | |
398 | |
399 if (state_ == INITIALIZED) { | |
400 // No associations are currently happening. Just reset the state. | |
401 state_ = new_state; | |
402 return; | |
403 } | |
404 | |
405 DVLOG(1) << "Model association complete for " | |
406 << syncer::ModelTypeSetToString(requested_types_); | |
407 | |
408 timer_.Stop(); | |
409 | |
410 // Treat any unfinished types as having errors. | |
411 desired_types_.RemoveAll(associating_types_); | |
412 for (DataTypeController::TypeMap::const_iterator it = controllers_->begin(); | |
413 it != controllers_->end(); ++it) { | |
414 DataTypeController* dtc = (*it).second.get(); | |
415 if (associating_types_.Has(dtc->type()) && | |
416 dtc->state() != DataTypeController::NOT_RUNNING) { | |
417 UMA_HISTOGRAM_ENUMERATION("Sync.ConfigureFailed", | |
418 ModelTypeToHistogramInt(dtc->type()), | |
419 syncer::MODEL_TYPE_COUNT); | |
420 StopDatatype(syncer::SyncError(FROM_HERE, | |
421 syncer::SyncError::DATATYPE_ERROR, | |
422 "Association timed out.", | |
423 dtc->type()), | |
424 dtc); | |
425 } | |
426 } | |
427 | |
428 DataTypeManager::ConfigureResult result(configure_status_, | |
429 requested_types_); | |
430 | |
431 // Need to reset state before invoking delegate in order to avoid re-entrancy | |
432 // issues (delegate may trigger a reconfiguration). | |
433 associating_types_.Clear(); | |
434 requested_types_.Clear(); | |
435 state_ = new_state; | |
436 | |
437 delegate_->OnModelAssociationDone(result); | |
438 } | |
439 | |
440 base::OneShotTimer* ModelAssociationManager::GetTimerForTesting() { | |
441 return &timer_; | |
442 } | |
443 | |
444 void ModelAssociationManager::NotifyDelegateIfReadyForConfigure() { | |
445 if (notified_about_ready_for_configure_) | |
446 return; | |
447 for (const auto& type_dtc_pair : *controllers_) { | |
448 syncer::ModelType type = type_dtc_pair.first; | |
449 if (!desired_types_.Has(type)) | |
450 continue; | |
451 DataTypeController* dtc = type_dtc_pair.second.get(); | |
452 if (dtc->ShouldLoadModelBeforeConfigure() && !loaded_types_.Has(type)) { | |
453 // At least one type is not ready. | |
454 return; | |
455 } | |
456 } | |
457 | |
458 notified_about_ready_for_configure_ = true; | |
459 delegate_->OnAllDataTypesReadyForConfigure(); | |
460 } | |
461 | |
462 } // namespace sync_driver | |
OLD | NEW |