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.
5 #include "chrome/browser/sync/glue/model_association_manager.h"
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 "sync/internal_api/public/base/model_type.h"
16 using content::BrowserThread
;
17 using syncer::ModelTypeSet
;
19 namespace browser_sync
{
23 static const syncer::ModelType kStartOrder
[] = {
24 syncer::NIGORI
, // Listed for completeness.
25 syncer::DEVICE_INFO
, // Listed for completeness.
26 syncer::EXPERIMENTS
, // Listed for completeness.
27 syncer::PROXY_TABS
, // Listed for completeness.
29 // Kick off the association of the non-UI types first so they can associate
30 // in parallel with the UI types.
33 syncer::AUTOFILL_PROFILE
,
34 syncer::EXTENSION_SETTINGS
,
37 syncer::HISTORY_DELETE_DIRECTIVES
,
38 syncer::SYNCED_NOTIFICATIONS
,
40 // UI thread data types.
42 syncer::MANAGED_USERS
, // Syncing managed users on initial login might
43 // block creating a new managed user, so we
44 // want to do it early.
46 syncer::PRIORITY_PREFERENCES
,
51 syncer::SEARCH_ENGINES
,
53 syncer::APP_NOTIFICATIONS
,
55 syncer::FAVICON_IMAGES
,
56 syncer::FAVICON_TRACKING
,
57 syncer::MANAGED_USER_SETTINGS
,
58 syncer::MANAGED_USER_SHARED_SETTINGS
,
62 COMPILE_ASSERT(arraysize(kStartOrder
) ==
63 syncer::MODEL_TYPE_COUNT
- syncer::FIRST_REAL_MODEL_TYPE
,
64 kStartOrder_IncorrectSize
);
66 // The amount of time we wait for association to finish. If some types haven't
67 // finished association by the time, configuration result will be
68 // PARTIAL_SUCCESS and DataTypeManager is notified of the unfinished types.
69 const int64 kAssociationTimeOutInSeconds
= 600;
71 syncer::DataTypeAssociationStats
BuildAssociationStatsFromMergeResults(
72 const syncer::SyncMergeResult
& local_merge_result
,
73 const syncer::SyncMergeResult
& syncer_merge_result
,
74 const base::TimeDelta
& association_wait_time
,
75 const base::TimeDelta
& association_time
) {
76 DCHECK_EQ(local_merge_result
.model_type(), syncer_merge_result
.model_type());
77 syncer::DataTypeAssociationStats stats
;
78 stats
.had_error
= local_merge_result
.error().IsSet() ||
79 syncer_merge_result
.error().IsSet();
80 stats
.num_local_items_before_association
=
81 local_merge_result
.num_items_before_association();
82 stats
.num_sync_items_before_association
=
83 syncer_merge_result
.num_items_before_association();
84 stats
.num_local_items_after_association
=
85 local_merge_result
.num_items_after_association();
86 stats
.num_sync_items_after_association
=
87 syncer_merge_result
.num_items_after_association();
88 stats
.num_local_items_added
=
89 local_merge_result
.num_items_added();
90 stats
.num_local_items_deleted
=
91 local_merge_result
.num_items_deleted();
92 stats
.num_local_items_modified
=
93 local_merge_result
.num_items_modified();
94 stats
.local_version_pre_association
=
95 local_merge_result
.pre_association_version();
96 stats
.num_sync_items_added
=
97 syncer_merge_result
.num_items_added();
98 stats
.num_sync_items_deleted
=
99 syncer_merge_result
.num_items_deleted();
100 stats
.num_sync_items_modified
=
101 syncer_merge_result
.num_items_modified();
102 stats
.sync_version_pre_association
=
103 syncer_merge_result
.pre_association_version();
104 stats
.association_wait_time
= association_wait_time
;
105 stats
.association_time
= association_time
;
111 ModelAssociationManager::ModelAssociationManager(
112 const DataTypeController::TypeMap
* controllers
,
113 ModelAssociationResultProcessor
* processor
)
115 controllers_(controllers
),
116 result_processor_(processor
),
117 weak_ptr_factory_(this),
118 configure_status_(DataTypeManager::UNKNOWN
) {
119 // Ensure all data type controllers are stopped.
120 for (DataTypeController::TypeMap::const_iterator it
= controllers_
->begin();
121 it
!= controllers_
->end(); ++it
) {
122 DCHECK_EQ(DataTypeController::NOT_RUNNING
, (*it
).second
->state());
126 ModelAssociationManager::~ModelAssociationManager() {
129 void ModelAssociationManager::Initialize(syncer::ModelTypeSet desired_types
) {
130 // state_ can be INITIALIZED_TO_CONFIGURE if types are reconfigured when
131 // data is being downloaded, so StartAssociationAsync() is never called for
132 // the first configuration.
133 DCHECK_NE(CONFIGURING
, state_
);
135 // Only keep types that have controllers.
136 desired_types_
.Clear();
138 for (syncer::ModelTypeSet::Iterator it
= desired_types
.First();
139 it
.Good(); it
.Inc()) {
140 if (controllers_
->find(it
.Get()) != controllers_
->end())
141 desired_types_
.Put(it
.Get());
144 DVLOG(1) << "ModelAssociationManager: Initializing for "
145 << syncer::ModelTypeSetToString(desired_types_
);
147 state_
= INITIALIZED_TO_CONFIGURE
;
153 void ModelAssociationManager::StopDisabledTypes() {
154 DVLOG(1) << "ModelAssociationManager: Stopping disabled types.";
155 for (DataTypeController::TypeMap::const_iterator it
= controllers_
->begin();
156 it
!= controllers_
->end(); ++it
) {
157 DataTypeController
* dtc
= (*it
).second
.get();
158 if (dtc
->state() != DataTypeController::NOT_RUNNING
&&
159 (!desired_types_
.Has(dtc
->type()) ||
160 failed_data_types_info_
.count(dtc
->type()) > 0)) {
161 DVLOG(1) << "ModelTypeToString: stop " << dtc
->name();
164 loaded_types_
.Remove(dtc
->type());
165 associated_types_
.Remove(dtc
->type());
170 void ModelAssociationManager::LoadEnabledTypes() {
171 // Load in kStartOrder.
172 for (size_t i
= 0; i
< arraysize(kStartOrder
); i
++) {
173 syncer::ModelType type
= kStartOrder
[i
];
174 if (!desired_types_
.Has(type
))
177 DCHECK(controllers_
->find(type
) != controllers_
->end());
178 DataTypeController
* dtc
= controllers_
->find(type
)->second
.get();
179 if (dtc
->state() == DataTypeController::NOT_RUNNING
) {
180 DCHECK(!loaded_types_
.Has(dtc
->type()));
181 DCHECK(!associated_types_
.Has(dtc
->type()));
182 dtc
->LoadModels(base::Bind(&ModelAssociationManager::ModelLoadCallback
,
183 weak_ptr_factory_
.GetWeakPtr()));
188 void ModelAssociationManager::StartAssociationAsync(
189 const syncer::ModelTypeSet
& types_to_associate
) {
190 DCHECK_NE(CONFIGURING
, state_
);
191 state_
= CONFIGURING
;
193 association_start_time_
= base::TimeTicks::Now();
195 requested_types_
= types_to_associate
;
197 associating_types_
= types_to_associate
;
198 associating_types_
.RetainAll(desired_types_
);
199 associating_types_
.RemoveAll(associated_types_
);
202 configure_status_
= DataTypeManager::OK
;
204 // Remove types that already failed.
205 for (std::map
<syncer::ModelType
, syncer::SyncError
>::const_iterator it
=
206 failed_data_types_info_
.begin();
207 it
!= failed_data_types_info_
.end(); ++it
) {
208 associating_types_
.Remove(it
->first
);
211 // Done if no types to associate.
212 if (associating_types_
.Empty()) {
213 ModelAssociationDone();
217 timer_
.Start(FROM_HERE
,
218 base::TimeDelta::FromSeconds(kAssociationTimeOutInSeconds
),
220 &ModelAssociationManager::ModelAssociationDone
);
222 // Start association of types that are loaded in specified order.
223 for (size_t i
= 0; i
< arraysize(kStartOrder
); i
++) {
224 syncer::ModelType type
= kStartOrder
[i
];
225 if (!associating_types_
.Has(type
) || !loaded_types_
.Has(type
))
228 DataTypeController
* dtc
= controllers_
->find(type
)->second
.get();
229 DCHECK(DataTypeController::MODEL_LOADED
== dtc
->state() ||
230 DataTypeController::ASSOCIATING
== dtc
->state());
231 if (dtc
->state() == DataTypeController::MODEL_LOADED
) {
232 TRACE_EVENT_ASYNC_BEGIN1("sync", "ModelAssociation",
235 ModelTypeToString(type
));
237 dtc
->StartAssociating(
238 base::Bind(&ModelAssociationManager::TypeStartCallback
,
239 weak_ptr_factory_
.GetWeakPtr(),
240 type
, base::TimeTicks::Now()));
245 void ModelAssociationManager::ResetForNextAssociation() {
246 DVLOG(1) << "ModelAssociationManager: Reseting for next configuration";
247 // |loaded_types_| and |associated_types_| are not cleared. So
248 // reconfiguration won't restart types that are already started.
249 requested_types_
.Clear();
250 failed_data_types_info_
.clear();
251 associating_types_
.Clear();
252 needs_crypto_types_
.Clear();
255 void ModelAssociationManager::Stop() {
256 // Ignore callbacks from controllers.
257 weak_ptr_factory_
.InvalidateWeakPtrs();
259 // Stop started data types.
260 for (DataTypeController::TypeMap::const_iterator it
= controllers_
->begin();
261 it
!= controllers_
->end(); ++it
) {
262 DataTypeController
* dtc
= (*it
).second
.get();
263 if (dtc
->state() != DataTypeController::NOT_RUNNING
) {
265 DVLOG(1) << "ModelAssociationManager: Stopped " << dtc
->name();
269 desired_types_
.Clear();
270 loaded_types_
.Clear();
271 associated_types_
.Clear();
274 if (state_
== CONFIGURING
) {
275 if (configure_status_
== DataTypeManager::OK
)
276 configure_status_
= DataTypeManager::ABORTED
;
277 DVLOG(1) << "ModelAssociationManager: Calling OnModelAssociationDone";
278 ModelAssociationDone();
281 ResetForNextAssociation();
286 void ModelAssociationManager::AppendToFailedDatatypesAndLogError(
287 const syncer::SyncError
& error
) {
288 failed_data_types_info_
[error
.model_type()] = error
;
289 LOG(ERROR
) << "Failed to associate models for "
290 << syncer::ModelTypeToString(error
.model_type());
291 UMA_HISTOGRAM_ENUMERATION("Sync.ConfigureFailed",
292 ModelTypeToHistogramInt(error
.model_type()),
293 syncer::MODEL_TYPE_COUNT
);
296 void ModelAssociationManager::ModelLoadCallback(syncer::ModelType type
,
297 syncer::SyncError error
) {
298 DVLOG(1) << "ModelAssociationManager: ModelLoadCallback for "
299 << syncer::ModelTypeToString(type
);
301 // This happens when slow loading type is disabled by new configuration.
302 if (!desired_types_
.Has(type
))
305 DCHECK(!loaded_types_
.Has(type
));
307 syncer::SyncMergeResult
local_merge_result(type
);
308 local_merge_result
.set_error(error
);
309 TypeStartCallback(type
,
310 base::TimeTicks::Now(),
311 DataTypeController::ASSOCIATION_FAILED
,
313 syncer::SyncMergeResult(type
));
317 loaded_types_
.Put(type
);
318 if (associating_types_
.Has(type
) || slow_types_
.Has(type
)) {
319 DataTypeController
* dtc
= controllers_
->find(type
)->second
.get();
320 dtc
->StartAssociating(
321 base::Bind(&ModelAssociationManager::TypeStartCallback
,
322 weak_ptr_factory_
.GetWeakPtr(),
323 type
, base::TimeTicks::Now()));
327 void ModelAssociationManager::TypeStartCallback(
328 syncer::ModelType type
,
329 base::TimeTicks type_start_time
,
330 DataTypeController::StartResult start_result
,
331 const syncer::SyncMergeResult
& local_merge_result
,
332 const syncer::SyncMergeResult
& syncer_merge_result
) {
333 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
335 // This happens when slow associating type is disabled by new configuration.
336 if (!desired_types_
.Has(type
))
339 slow_types_
.Remove(type
);
341 DCHECK(!associated_types_
.Has(type
));
342 if (DataTypeController::IsSuccessfulResult(start_result
)) {
343 associated_types_
.Put(type
);
344 } else if (state_
== IDLE
) {
345 // For type that failed in IDLE mode, simply stop the controller. Next
346 // configuration will try to restart from scratch if the type is still
348 DataTypeController
* dtc
= controllers_
->find(type
)->second
.get();
349 if (dtc
->state() != DataTypeController::NOT_RUNNING
)
351 loaded_types_
.Remove(type
);
353 // Record error in CONFIGURING or INITIALIZED_TO_CONFIGURE mode. The error
354 // will be reported when data types association finishes.
355 if (start_result
== DataTypeController::NEEDS_CRYPTO
) {
356 DVLOG(1) << "ModelAssociationManager: Encountered an undecryptable type";
357 needs_crypto_types_
.Put(type
);
359 DVLOG(1) << "ModelAssociationManager: Encountered a failed type";
360 AppendToFailedDatatypesAndLogError(local_merge_result
.error());
364 if (state_
!= CONFIGURING
)
367 TRACE_EVENT_ASYNC_END1("sync", "ModelAssociation",
368 controllers_
->find(type
)->second
.get(),
370 ModelTypeToString(type
));
372 // Track the merge results if we succeeded or an association failure
374 if ((DataTypeController::IsSuccessfulResult(start_result
) ||
375 start_result
== DataTypeController::ASSOCIATION_FAILED
) &&
376 syncer::ProtocolTypes().Has(type
)) {
377 base::TimeDelta association_wait_time
=
378 std::max(base::TimeDelta(), type_start_time
- association_start_time_
);
379 base::TimeDelta association_time
=
380 base::TimeTicks::Now() - type_start_time
;;
381 syncer::DataTypeAssociationStats stats
=
382 BuildAssociationStatsFromMergeResults(local_merge_result
,
384 association_wait_time
,
386 result_processor_
->OnSingleDataTypeAssociationDone(type
, stats
);
389 // Update configuration result.
390 if (configure_status_
== DataTypeManager::OK
&&
391 start_result
== DataTypeController::ASSOCIATION_FAILED
) {
392 configure_status_
= DataTypeManager::PARTIAL_SUCCESS
;
394 if (start_result
== DataTypeController::UNRECOVERABLE_ERROR
)
395 configure_status_
= DataTypeManager::UNRECOVERABLE_ERROR
;
397 associating_types_
.Remove(type
);
399 if (associating_types_
.Empty())
400 ModelAssociationDone();
403 void ModelAssociationManager::ModelAssociationDone() {
404 CHECK_EQ(CONFIGURING
, state_
);
408 // Stop controllers of failed types.
411 if (configure_status_
== DataTypeManager::OK
&&
412 (!associating_types_
.Empty() || !failed_data_types_info_
.empty() ||
413 !needs_crypto_types_
.Empty())) {
414 // We have not configured all types that we have been asked to configure.
415 // Either we have failed types or types that have not completed loading
417 DVLOG(1) << "ModelAssociationManager: setting partial success";
418 configure_status_
= DataTypeManager::PARTIAL_SUCCESS
;
421 slow_types_
.PutAll(associating_types_
);
423 DataTypeManager::ConfigureResult
result(configure_status_
,
425 failed_data_types_info_
,
427 needs_crypto_types_
);
429 // Reset state before notifying |result_processor_| because that might
430 // trigger a new round of configuration.
431 ResetForNextAssociation();
434 result_processor_
->OnModelAssociationDone(result
);
437 base::OneShotTimer
<ModelAssociationManager
>*
438 ModelAssociationManager::GetTimerForTesting() {
442 } // namespace browser_sync