OLD | NEW |
| (Empty) |
1 // Copyright (c) 2012 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 "chrome/browser/sync/glue/model_association_manager.h" | |
6 | |
7 #include <algorithm> | |
8 #include <functional> | |
9 | |
10 #include "base/debug/trace_event.h" | |
11 #include "base/logging.h" | |
12 #include "base/message_loop/message_loop.h" | |
13 #include "base/metrics/histogram.h" | |
14 #include "content/public/browser/browser_thread.h" | |
15 #include "sync/internal_api/public/base/model_type.h" | |
16 | |
17 using content::BrowserThread; | |
18 using syncer::ModelTypeSet; | |
19 | |
20 namespace browser_sync { | |
21 | |
22 namespace { | |
23 | |
24 static const syncer::ModelType kStartOrder[] = { | |
25 syncer::NIGORI, // Listed for completeness. | |
26 syncer::DEVICE_INFO, // Listed for completeness. | |
27 syncer::EXPERIMENTS, // Listed for completeness. | |
28 syncer::PROXY_TABS, // Listed for completeness. | |
29 | |
30 // Kick off the association of the non-UI types first so they can associate | |
31 // in parallel with the UI types. | |
32 syncer::PASSWORDS, | |
33 syncer::AUTOFILL, | |
34 syncer::AUTOFILL_PROFILE, | |
35 syncer::EXTENSION_SETTINGS, | |
36 syncer::APP_SETTINGS, | |
37 syncer::TYPED_URLS, | |
38 syncer::HISTORY_DELETE_DIRECTIVES, | |
39 syncer::SYNCED_NOTIFICATIONS, | |
40 syncer::SYNCED_NOTIFICATION_APP_INFO, | |
41 | |
42 // UI thread data types. | |
43 syncer::BOOKMARKS, | |
44 syncer::MANAGED_USERS, // Syncing managed users on initial login might | |
45 // block creating a new managed user, so we | |
46 // want to do it early. | |
47 syncer::PREFERENCES, | |
48 syncer::PRIORITY_PREFERENCES, | |
49 syncer::EXTENSIONS, | |
50 syncer::APPS, | |
51 syncer::APP_LIST, | |
52 syncer::THEMES, | |
53 syncer::SEARCH_ENGINES, | |
54 syncer::SESSIONS, | |
55 syncer::APP_NOTIFICATIONS, | |
56 syncer::DICTIONARY, | |
57 syncer::FAVICON_IMAGES, | |
58 syncer::FAVICON_TRACKING, | |
59 syncer::MANAGED_USER_SETTINGS, | |
60 syncer::MANAGED_USER_SHARED_SETTINGS, | |
61 syncer::ARTICLES, | |
62 }; | |
63 | |
64 COMPILE_ASSERT(arraysize(kStartOrder) == | |
65 syncer::MODEL_TYPE_COUNT - syncer::FIRST_REAL_MODEL_TYPE, | |
66 kStartOrder_IncorrectSize); | |
67 | |
68 // The amount of time we wait for association to finish. If some types haven't | |
69 // finished association by the time, configuration result will be | |
70 // PARTIAL_SUCCESS and DataTypeManager is notified of the unfinished types. | |
71 const int64 kAssociationTimeOutInSeconds = 600; | |
72 | |
73 syncer::DataTypeAssociationStats BuildAssociationStatsFromMergeResults( | |
74 const syncer::SyncMergeResult& local_merge_result, | |
75 const syncer::SyncMergeResult& syncer_merge_result, | |
76 const base::TimeDelta& association_wait_time, | |
77 const base::TimeDelta& association_time) { | |
78 DCHECK_EQ(local_merge_result.model_type(), syncer_merge_result.model_type()); | |
79 syncer::DataTypeAssociationStats stats; | |
80 stats.had_error = local_merge_result.error().IsSet() || | |
81 syncer_merge_result.error().IsSet(); | |
82 stats.num_local_items_before_association = | |
83 local_merge_result.num_items_before_association(); | |
84 stats.num_sync_items_before_association = | |
85 syncer_merge_result.num_items_before_association(); | |
86 stats.num_local_items_after_association = | |
87 local_merge_result.num_items_after_association(); | |
88 stats.num_sync_items_after_association = | |
89 syncer_merge_result.num_items_after_association(); | |
90 stats.num_local_items_added = | |
91 local_merge_result.num_items_added(); | |
92 stats.num_local_items_deleted = | |
93 local_merge_result.num_items_deleted(); | |
94 stats.num_local_items_modified = | |
95 local_merge_result.num_items_modified(); | |
96 stats.local_version_pre_association = | |
97 local_merge_result.pre_association_version(); | |
98 stats.num_sync_items_added = | |
99 syncer_merge_result.num_items_added(); | |
100 stats.num_sync_items_deleted = | |
101 syncer_merge_result.num_items_deleted(); | |
102 stats.num_sync_items_modified = | |
103 syncer_merge_result.num_items_modified(); | |
104 stats.sync_version_pre_association = | |
105 syncer_merge_result.pre_association_version(); | |
106 stats.association_wait_time = association_wait_time; | |
107 stats.association_time = association_time; | |
108 return stats; | |
109 } | |
110 | |
111 } // namespace | |
112 | |
113 ModelAssociationManager::ModelAssociationManager( | |
114 const DataTypeController::TypeMap* controllers, | |
115 ModelAssociationResultProcessor* processor) | |
116 : state_(IDLE), | |
117 controllers_(controllers), | |
118 result_processor_(processor), | |
119 weak_ptr_factory_(this), | |
120 configure_status_(DataTypeManager::UNKNOWN) { | |
121 // Ensure all data type controllers are stopped. | |
122 for (DataTypeController::TypeMap::const_iterator it = controllers_->begin(); | |
123 it != controllers_->end(); ++it) { | |
124 DCHECK_EQ(DataTypeController::NOT_RUNNING, (*it).second->state()); | |
125 } | |
126 } | |
127 | |
128 ModelAssociationManager::~ModelAssociationManager() { | |
129 } | |
130 | |
131 void ModelAssociationManager::Initialize(syncer::ModelTypeSet desired_types) { | |
132 // state_ can be INITIALIZED_TO_CONFIGURE if types are reconfigured when | |
133 // data is being downloaded, so StartAssociationAsync() is never called for | |
134 // the first configuration. | |
135 DCHECK_NE(CONFIGURING, state_); | |
136 | |
137 // Only keep types that have controllers. | |
138 desired_types_.Clear(); | |
139 slow_types_.Clear(); | |
140 for (syncer::ModelTypeSet::Iterator it = desired_types.First(); | |
141 it.Good(); it.Inc()) { | |
142 if (controllers_->find(it.Get()) != controllers_->end()) | |
143 desired_types_.Put(it.Get()); | |
144 } | |
145 | |
146 DVLOG(1) << "ModelAssociationManager: Initializing for " | |
147 << syncer::ModelTypeSetToString(desired_types_); | |
148 | |
149 state_ = INITIALIZED_TO_CONFIGURE; | |
150 | |
151 StopDisabledTypes(); | |
152 LoadEnabledTypes(); | |
153 } | |
154 | |
155 void ModelAssociationManager::StopDisabledTypes() { | |
156 DVLOG(1) << "ModelAssociationManager: Stopping disabled types."; | |
157 for (DataTypeController::TypeMap::const_iterator it = controllers_->begin(); | |
158 it != controllers_->end(); ++it) { | |
159 DataTypeController* dtc = (*it).second.get(); | |
160 if (dtc->state() != DataTypeController::NOT_RUNNING && | |
161 (!desired_types_.Has(dtc->type()) || | |
162 failed_data_types_info_.count(dtc->type()) > 0)) { | |
163 DVLOG(1) << "ModelTypeToString: stop " << dtc->name(); | |
164 dtc->Stop(); | |
165 | |
166 loaded_types_.Remove(dtc->type()); | |
167 associated_types_.Remove(dtc->type()); | |
168 } | |
169 } | |
170 } | |
171 | |
172 void ModelAssociationManager::LoadEnabledTypes() { | |
173 // Load in kStartOrder. | |
174 for (size_t i = 0; i < arraysize(kStartOrder); i++) { | |
175 syncer::ModelType type = kStartOrder[i]; | |
176 if (!desired_types_.Has(type)) | |
177 continue; | |
178 | |
179 DCHECK(controllers_->find(type) != controllers_->end()); | |
180 DataTypeController* dtc = controllers_->find(type)->second.get(); | |
181 if (dtc->state() == DataTypeController::NOT_RUNNING) { | |
182 DCHECK(!loaded_types_.Has(dtc->type())); | |
183 DCHECK(!associated_types_.Has(dtc->type())); | |
184 dtc->LoadModels(base::Bind(&ModelAssociationManager::ModelLoadCallback, | |
185 weak_ptr_factory_.GetWeakPtr())); | |
186 } | |
187 } | |
188 } | |
189 | |
190 void ModelAssociationManager::StartAssociationAsync( | |
191 const syncer::ModelTypeSet& types_to_associate) { | |
192 DCHECK_NE(CONFIGURING, state_); | |
193 state_ = CONFIGURING; | |
194 | |
195 association_start_time_ = base::TimeTicks::Now(); | |
196 | |
197 requested_types_ = types_to_associate; | |
198 | |
199 associating_types_ = types_to_associate; | |
200 associating_types_.RetainAll(desired_types_); | |
201 associating_types_.RemoveAll(associated_types_); | |
202 | |
203 // Assume success. | |
204 configure_status_ = DataTypeManager::OK; | |
205 | |
206 // Remove types that already failed. | |
207 for (std::map<syncer::ModelType, syncer::SyncError>::const_iterator it = | |
208 failed_data_types_info_.begin(); | |
209 it != failed_data_types_info_.end(); ++it) { | |
210 associating_types_.Remove(it->first); | |
211 } | |
212 | |
213 // Done if no types to associate. | |
214 if (associating_types_.Empty()) { | |
215 ModelAssociationDone(); | |
216 return; | |
217 } | |
218 | |
219 timer_.Start(FROM_HERE, | |
220 base::TimeDelta::FromSeconds(kAssociationTimeOutInSeconds), | |
221 this, | |
222 &ModelAssociationManager::ModelAssociationDone); | |
223 | |
224 // Start association of types that are loaded in specified order. | |
225 for (size_t i = 0; i < arraysize(kStartOrder); i++) { | |
226 syncer::ModelType type = kStartOrder[i]; | |
227 if (!associating_types_.Has(type) || !loaded_types_.Has(type)) | |
228 continue; | |
229 | |
230 DataTypeController* dtc = controllers_->find(type)->second.get(); | |
231 DCHECK(DataTypeController::MODEL_LOADED == dtc->state() || | |
232 DataTypeController::ASSOCIATING == dtc->state()); | |
233 if (dtc->state() == DataTypeController::MODEL_LOADED) { | |
234 TRACE_EVENT_ASYNC_BEGIN1("sync", "ModelAssociation", | |
235 dtc, | |
236 "DataType", | |
237 ModelTypeToString(type)); | |
238 | |
239 dtc->StartAssociating( | |
240 base::Bind(&ModelAssociationManager::TypeStartCallback, | |
241 weak_ptr_factory_.GetWeakPtr(), | |
242 type, base::TimeTicks::Now())); | |
243 } | |
244 } | |
245 } | |
246 | |
247 void ModelAssociationManager::ResetForNextAssociation() { | |
248 DVLOG(1) << "ModelAssociationManager: Reseting for next configuration"; | |
249 // |loaded_types_| and |associated_types_| are not cleared. So | |
250 // reconfiguration won't restart types that are already started. | |
251 requested_types_.Clear(); | |
252 failed_data_types_info_.clear(); | |
253 associating_types_.Clear(); | |
254 needs_crypto_types_.Clear(); | |
255 } | |
256 | |
257 void ModelAssociationManager::Stop() { | |
258 // Ignore callbacks from controllers. | |
259 weak_ptr_factory_.InvalidateWeakPtrs(); | |
260 | |
261 // Stop started data types. | |
262 for (DataTypeController::TypeMap::const_iterator it = controllers_->begin(); | |
263 it != controllers_->end(); ++it) { | |
264 DataTypeController* dtc = (*it).second.get(); | |
265 if (dtc->state() != DataTypeController::NOT_RUNNING) { | |
266 dtc->Stop(); | |
267 DVLOG(1) << "ModelAssociationManager: Stopped " << dtc->name(); | |
268 } | |
269 } | |
270 | |
271 desired_types_.Clear(); | |
272 loaded_types_.Clear(); | |
273 associated_types_.Clear(); | |
274 slow_types_.Clear(); | |
275 | |
276 if (state_ == CONFIGURING) { | |
277 if (configure_status_ == DataTypeManager::OK) | |
278 configure_status_ = DataTypeManager::ABORTED; | |
279 DVLOG(1) << "ModelAssociationManager: Calling OnModelAssociationDone"; | |
280 ModelAssociationDone(); | |
281 } | |
282 | |
283 ResetForNextAssociation(); | |
284 | |
285 state_ = IDLE; | |
286 } | |
287 | |
288 void ModelAssociationManager::AppendToFailedDatatypesAndLogError( | |
289 const syncer::SyncError& error) { | |
290 failed_data_types_info_[error.model_type()] = error; | |
291 LOG(ERROR) << "Failed to associate models for " | |
292 << syncer::ModelTypeToString(error.model_type()); | |
293 UMA_HISTOGRAM_ENUMERATION("Sync.ConfigureFailed", | |
294 ModelTypeToHistogramInt(error.model_type()), | |
295 syncer::MODEL_TYPE_COUNT); | |
296 } | |
297 | |
298 void ModelAssociationManager::ModelLoadCallback(syncer::ModelType type, | |
299 syncer::SyncError error) { | |
300 DVLOG(1) << "ModelAssociationManager: ModelLoadCallback for " | |
301 << syncer::ModelTypeToString(type); | |
302 | |
303 // TODO(haitaol): temporary fix for 335606. | |
304 if (slow_types_.Has(type)) | |
305 return; | |
306 | |
307 // This happens when slow loading type is disabled by new configuration. | |
308 if (!desired_types_.Has(type)) | |
309 return; | |
310 | |
311 DCHECK(!loaded_types_.Has(type)); | |
312 if (error.IsSet()) { | |
313 syncer::SyncMergeResult local_merge_result(type); | |
314 local_merge_result.set_error(error); | |
315 TypeStartCallback(type, | |
316 base::TimeTicks::Now(), | |
317 DataTypeController::ASSOCIATION_FAILED, | |
318 local_merge_result, | |
319 syncer::SyncMergeResult(type)); | |
320 return; | |
321 } | |
322 | |
323 loaded_types_.Put(type); | |
324 if (associating_types_.Has(type) || slow_types_.Has(type)) { | |
325 DataTypeController* dtc = controllers_->find(type)->second.get(); | |
326 dtc->StartAssociating( | |
327 base::Bind(&ModelAssociationManager::TypeStartCallback, | |
328 weak_ptr_factory_.GetWeakPtr(), | |
329 type, base::TimeTicks::Now())); | |
330 } | |
331 } | |
332 | |
333 void ModelAssociationManager::TypeStartCallback( | |
334 syncer::ModelType type, | |
335 base::TimeTicks type_start_time, | |
336 DataTypeController::StartResult start_result, | |
337 const syncer::SyncMergeResult& local_merge_result, | |
338 const syncer::SyncMergeResult& syncer_merge_result) { | |
339 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
340 | |
341 // TODO(haitaol): temporary fix for 335606. | |
342 if (slow_types_.Has(type)) | |
343 return; | |
344 | |
345 // This happens when slow associating type is disabled by new configuration. | |
346 if (!desired_types_.Has(type)) | |
347 return; | |
348 | |
349 slow_types_.Remove(type); | |
350 | |
351 DCHECK(!associated_types_.Has(type)); | |
352 if (DataTypeController::IsSuccessfulResult(start_result)) { | |
353 associated_types_.Put(type); | |
354 } else if (state_ == IDLE) { | |
355 // For type that failed in IDLE mode, simply stop the controller. Next | |
356 // configuration will try to restart from scratch if the type is still | |
357 // enabled. | |
358 DataTypeController* dtc = controllers_->find(type)->second.get(); | |
359 if (dtc->state() != DataTypeController::NOT_RUNNING) | |
360 dtc->Stop(); | |
361 loaded_types_.Remove(type); | |
362 } else { | |
363 // Record error in CONFIGURING or INITIALIZED_TO_CONFIGURE mode. The error | |
364 // will be reported when data types association finishes. | |
365 if (start_result == DataTypeController::NEEDS_CRYPTO) { | |
366 DVLOG(1) << "ModelAssociationManager: Encountered an undecryptable type"; | |
367 needs_crypto_types_.Put(type); | |
368 } else { | |
369 DVLOG(1) << "ModelAssociationManager: Encountered a failed type"; | |
370 AppendToFailedDatatypesAndLogError(local_merge_result.error()); | |
371 } | |
372 } | |
373 | |
374 if (state_ != CONFIGURING) | |
375 return; | |
376 | |
377 TRACE_EVENT_ASYNC_END1("sync", "ModelAssociation", | |
378 controllers_->find(type)->second.get(), | |
379 "DataType", | |
380 ModelTypeToString(type)); | |
381 | |
382 // Track the merge results if we succeeded or an association failure | |
383 // occurred. | |
384 if ((DataTypeController::IsSuccessfulResult(start_result) || | |
385 start_result == DataTypeController::ASSOCIATION_FAILED) && | |
386 syncer::ProtocolTypes().Has(type)) { | |
387 base::TimeDelta association_wait_time = | |
388 std::max(base::TimeDelta(), type_start_time - association_start_time_); | |
389 base::TimeDelta association_time = | |
390 base::TimeTicks::Now() - type_start_time;; | |
391 syncer::DataTypeAssociationStats stats = | |
392 BuildAssociationStatsFromMergeResults(local_merge_result, | |
393 syncer_merge_result, | |
394 association_wait_time, | |
395 association_time); | |
396 result_processor_->OnSingleDataTypeAssociationDone(type, stats); | |
397 } | |
398 | |
399 // Update configuration result. | |
400 if (configure_status_ == DataTypeManager::OK && | |
401 start_result == DataTypeController::ASSOCIATION_FAILED) { | |
402 configure_status_ = DataTypeManager::PARTIAL_SUCCESS; | |
403 } | |
404 if (start_result == DataTypeController::UNRECOVERABLE_ERROR) | |
405 configure_status_ = DataTypeManager::UNRECOVERABLE_ERROR; | |
406 | |
407 associating_types_.Remove(type); | |
408 | |
409 if (associating_types_.Empty()) | |
410 ModelAssociationDone(); | |
411 } | |
412 | |
413 void ModelAssociationManager::ModelAssociationDone() { | |
414 CHECK_EQ(CONFIGURING, state_); | |
415 | |
416 timer_.Stop(); | |
417 | |
418 slow_types_.PutAll(associating_types_); | |
419 | |
420 // TODO(haitaol): temporary fix for 335606. | |
421 for (syncer::ModelTypeSet::Iterator it = associating_types_.First(); | |
422 it.Good(); it.Inc()) { | |
423 AppendToFailedDatatypesAndLogError( | |
424 syncer::SyncError(FROM_HERE, syncer::SyncError::DATATYPE_ERROR, | |
425 "Association timed out.", it.Get())); | |
426 } | |
427 | |
428 // Stop controllers of failed types. | |
429 StopDisabledTypes(); | |
430 | |
431 if (configure_status_ == DataTypeManager::OK && | |
432 (!associating_types_.Empty() || !failed_data_types_info_.empty() || | |
433 !needs_crypto_types_.Empty())) { | |
434 // We have not configured all types that we have been asked to configure. | |
435 // Either we have failed types or types that have not completed loading | |
436 // yet. | |
437 DVLOG(1) << "ModelAssociationManager: setting partial success"; | |
438 configure_status_ = DataTypeManager::PARTIAL_SUCCESS; | |
439 } | |
440 | |
441 DataTypeManager::ConfigureResult result(configure_status_, | |
442 requested_types_, | |
443 failed_data_types_info_, | |
444 associating_types_, | |
445 needs_crypto_types_); | |
446 | |
447 // Reset state before notifying |result_processor_| because that might | |
448 // trigger a new round of configuration. | |
449 ResetForNextAssociation(); | |
450 state_ = IDLE; | |
451 | |
452 result_processor_->OnModelAssociationDone(result); | |
453 } | |
454 | |
455 base::OneShotTimer<ModelAssociationManager>* | |
456 ModelAssociationManager::GetTimerForTesting() { | |
457 return &timer_; | |
458 } | |
459 | |
460 } // namespace browser_sync | |
OLD | NEW |