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.
5 #include "components/sync_driver/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 syncer::ModelTypeSet
;
18 namespace sync_driver
{
22 static const syncer::ModelType kStartOrder
[] = {
23 syncer::NIGORI
, // Listed for completeness.
24 syncer::DEVICE_INFO
, // Listed for completeness.
25 syncer::EXPERIMENTS
, // Listed for completeness.
26 syncer::PROXY_TABS
, // Listed for completeness.
28 // Kick off the association of the non-UI types first so they can associate
29 // in parallel with the UI types.
32 syncer::AUTOFILL_PROFILE
,
33 syncer::EXTENSION_SETTINGS
,
36 syncer::HISTORY_DELETE_DIRECTIVES
,
37 syncer::SYNCED_NOTIFICATIONS
,
38 syncer::SYNCED_NOTIFICATION_APP_INFO
,
40 // UI thread data types.
42 syncer::ENHANCED_BOOKMARKS
,
43 syncer::SUPERVISED_USERS
, // Syncing supervised users on initial login
44 // might block creating a new supervised user,
45 // so we want to do it early.
47 syncer::PRIORITY_PREFERENCES
,
52 syncer::SEARCH_ENGINES
,
54 syncer::APP_NOTIFICATIONS
,
56 syncer::FAVICON_IMAGES
,
57 syncer::FAVICON_TRACKING
,
58 syncer::SUPERVISED_USER_SETTINGS
,
59 syncer::SUPERVISED_USER_SHARED_SETTINGS
,
63 COMPILE_ASSERT(arraysize(kStartOrder
) ==
64 syncer::MODEL_TYPE_COUNT
- syncer::FIRST_REAL_MODEL_TYPE
,
65 kStartOrder_IncorrectSize
);
67 // The amount of time we wait for association to finish. If some types haven't
68 // finished association by the time, DataTypeManager is notified of the
70 const int64 kAssociationTimeOutInSeconds
= 600;
72 syncer::DataTypeAssociationStats
BuildAssociationStatsFromMergeResults(
73 const syncer::SyncMergeResult
& local_merge_result
,
74 const syncer::SyncMergeResult
& syncer_merge_result
,
75 const base::TimeDelta
& association_wait_time
,
76 const base::TimeDelta
& association_time
) {
77 DCHECK_EQ(local_merge_result
.model_type(), syncer_merge_result
.model_type());
78 syncer::DataTypeAssociationStats stats
;
79 stats
.had_error
= local_merge_result
.error().IsSet() ||
80 syncer_merge_result
.error().IsSet();
81 stats
.num_local_items_before_association
=
82 local_merge_result
.num_items_before_association();
83 stats
.num_sync_items_before_association
=
84 syncer_merge_result
.num_items_before_association();
85 stats
.num_local_items_after_association
=
86 local_merge_result
.num_items_after_association();
87 stats
.num_sync_items_after_association
=
88 syncer_merge_result
.num_items_after_association();
89 stats
.num_local_items_added
=
90 local_merge_result
.num_items_added();
91 stats
.num_local_items_deleted
=
92 local_merge_result
.num_items_deleted();
93 stats
.num_local_items_modified
=
94 local_merge_result
.num_items_modified();
95 stats
.local_version_pre_association
=
96 local_merge_result
.pre_association_version();
97 stats
.num_sync_items_added
=
98 syncer_merge_result
.num_items_added();
99 stats
.num_sync_items_deleted
=
100 syncer_merge_result
.num_items_deleted();
101 stats
.num_sync_items_modified
=
102 syncer_merge_result
.num_items_modified();
103 stats
.sync_version_pre_association
=
104 syncer_merge_result
.pre_association_version();
105 stats
.association_wait_time
= association_wait_time
;
106 stats
.association_time
= association_time
;
112 ModelAssociationManager::ModelAssociationManager(
113 const DataTypeController::TypeMap
* controllers
,
114 ModelAssociationManagerDelegate
* processor
)
116 controllers_(controllers
),
117 delegate_(processor
),
118 configure_status_(DataTypeManager::UNKNOWN
),
119 weak_ptr_factory_(this) {
120 // Ensure all data type controllers are stopped.
121 for (DataTypeController::TypeMap::const_iterator it
= controllers_
->begin();
122 it
!= controllers_
->end(); ++it
) {
123 DCHECK_EQ(DataTypeController::NOT_RUNNING
, (*it
).second
->state());
127 ModelAssociationManager::~ModelAssociationManager() {
130 void ModelAssociationManager::Initialize(syncer::ModelTypeSet desired_types
) {
131 // state_ can be INITIALIZED_TO_CONFIGURE if types are reconfigured when
132 // data is being downloaded, so StartAssociationAsync() is never called for
133 // the first configuration.
134 DCHECK_NE(CONFIGURING
, state_
);
136 // Only keep types that have controllers.
137 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::StopDatatype(
154 const syncer::SyncError
& error
,
155 DataTypeController
* dtc
) {
156 loaded_types_
.Remove(dtc
->type());
157 associated_types_
.Remove(dtc
->type());
158 associating_types_
.Remove(dtc
->type());
160 if (error
.IsSet() || dtc
->state() != DataTypeController::NOT_RUNNING
) {
161 // If an error was set, the delegate must be informed of the error.
162 delegate_
->OnSingleDataTypeWillStop(dtc
->type(), error
);
167 void ModelAssociationManager::StopDisabledTypes() {
168 DVLOG(1) << "ModelAssociationManager: Stopping disabled types.";
169 for (DataTypeController::TypeMap::const_iterator it
= controllers_
->begin();
170 it
!= controllers_
->end(); ++it
) {
171 DataTypeController
* dtc
= (*it
).second
.get();
172 if (dtc
->state() != DataTypeController::NOT_RUNNING
&&
173 !desired_types_
.Has(dtc
->type())) {
174 DVLOG(1) << "ModelTypeToString: stop " << dtc
->name();
175 StopDatatype(syncer::SyncError(), dtc
);
180 void ModelAssociationManager::LoadEnabledTypes() {
181 // Load in kStartOrder.
182 for (size_t i
= 0; i
< arraysize(kStartOrder
); i
++) {
183 syncer::ModelType type
= kStartOrder
[i
];
184 if (!desired_types_
.Has(type
))
187 DCHECK(controllers_
->find(type
) != controllers_
->end());
188 DataTypeController
* dtc
= controllers_
->find(type
)->second
.get();
189 if (dtc
->state() == DataTypeController::NOT_RUNNING
) {
190 DCHECK(!loaded_types_
.Has(dtc
->type()));
191 DCHECK(!associated_types_
.Has(dtc
->type()));
192 dtc
->LoadModels(base::Bind(&ModelAssociationManager::ModelLoadCallback
,
193 weak_ptr_factory_
.GetWeakPtr()));
198 void ModelAssociationManager::StartAssociationAsync(
199 const syncer::ModelTypeSet
& types_to_associate
) {
200 DCHECK_NE(CONFIGURING
, state_
);
201 state_
= CONFIGURING
;
203 association_start_time_
= base::TimeTicks::Now();
205 requested_types_
= types_to_associate
;
207 associating_types_
= types_to_associate
;
208 associating_types_
.RetainAll(desired_types_
);
209 associating_types_
.RemoveAll(associated_types_
);
212 configure_status_
= DataTypeManager::OK
;
214 // Done if no types to associate.
215 if (associating_types_
.Empty()) {
216 ModelAssociationDone();
220 timer_
.Start(FROM_HERE
,
221 base::TimeDelta::FromSeconds(kAssociationTimeOutInSeconds
),
223 &ModelAssociationManager::ModelAssociationDone
);
225 // Start association of types that are loaded in specified order.
226 for (size_t i
= 0; i
< arraysize(kStartOrder
); i
++) {
227 syncer::ModelType type
= kStartOrder
[i
];
228 if (!associating_types_
.Has(type
) || !loaded_types_
.Has(type
))
231 DataTypeController
* dtc
= controllers_
->find(type
)->second
.get();
232 DCHECK(DataTypeController::MODEL_LOADED
== dtc
->state() ||
233 DataTypeController::ASSOCIATING
== dtc
->state());
234 if (dtc
->state() == DataTypeController::MODEL_LOADED
) {
235 TRACE_EVENT_ASYNC_BEGIN1("sync", "ModelAssociation",
238 ModelTypeToString(type
));
240 dtc
->StartAssociating(
241 base::Bind(&ModelAssociationManager::TypeStartCallback
,
242 weak_ptr_factory_
.GetWeakPtr(),
243 type
, base::TimeTicks::Now()));
248 void ModelAssociationManager::ResetForNextAssociation() {
249 DVLOG(1) << "ModelAssociationManager: Reseting for next configuration";
250 // |loaded_types_| and |associated_types_| are not cleared. So
251 // reconfiguration won't restart types that are already started.
252 requested_types_
.Clear();
253 associating_types_
.Clear();
256 void ModelAssociationManager::Stop() {
257 // Ignore callbacks from controllers.
258 weak_ptr_factory_
.InvalidateWeakPtrs();
260 desired_types_
.Clear();
261 loaded_types_
.Clear();
262 associated_types_
.Clear();
263 associating_types_
.Clear();
265 // Stop started data types.
266 for (DataTypeController::TypeMap::const_iterator it
= controllers_
->begin();
267 it
!= controllers_
->end(); ++it
) {
268 DataTypeController
* dtc
= (*it
).second
.get();
269 if (dtc
->state() != DataTypeController::NOT_RUNNING
) {
270 StopDatatype(syncer::SyncError(), dtc
);
271 DVLOG(1) << "ModelAssociationManager: Stopped " << dtc
->name();
275 if (state_
== CONFIGURING
) {
276 if (configure_status_
== DataTypeManager::OK
)
277 configure_status_
= DataTypeManager::ABORTED
;
278 DVLOG(1) << "ModelAssociationManager: Calling OnModelAssociationDone";
279 ModelAssociationDone();
282 ResetForNextAssociation();
287 void ModelAssociationManager::ModelLoadCallback(syncer::ModelType type
,
288 syncer::SyncError error
) {
289 DVLOG(1) << "ModelAssociationManager: ModelLoadCallback for "
290 << syncer::ModelTypeToString(type
);
293 syncer::SyncMergeResult
local_merge_result(type
);
294 local_merge_result
.set_error(error
);
295 TypeStartCallback(type
,
296 base::TimeTicks::Now(),
297 DataTypeController::ASSOCIATION_FAILED
,
299 syncer::SyncMergeResult(type
));
303 // This happens when slow loading type is disabled by new configuration.
304 if (!desired_types_
.Has(type
))
307 DCHECK(!loaded_types_
.Has(type
));
308 loaded_types_
.Put(type
);
309 if (associating_types_
.Has(type
)) {
310 DataTypeController
* dtc
= controllers_
->find(type
)->second
.get();
311 dtc
->StartAssociating(
312 base::Bind(&ModelAssociationManager::TypeStartCallback
,
313 weak_ptr_factory_
.GetWeakPtr(),
314 type
, base::TimeTicks::Now()));
318 void ModelAssociationManager::TypeStartCallback(
319 syncer::ModelType type
,
320 base::TimeTicks type_start_time
,
321 DataTypeController::ConfigureResult start_result
,
322 const syncer::SyncMergeResult
& local_merge_result
,
323 const syncer::SyncMergeResult
& syncer_merge_result
) {
324 if (desired_types_
.Has(type
) &&
325 !DataTypeController::IsSuccessfulResult(start_result
)) {
326 DVLOG(1) << "ModelAssociationManager: Type encountered an error.";
327 desired_types_
.Remove(type
);
328 DataTypeController
* dtc
= controllers_
->find(type
)->second
.get();
329 StopDatatype(local_merge_result
.error(), dtc
);
331 // Update configuration result.
332 if (start_result
== DataTypeController::UNRECOVERABLE_ERROR
)
333 configure_status_
= DataTypeManager::UNRECOVERABLE_ERROR
;
336 // This happens when a slow associating type is disabled or if a type
337 // disables itself after initial configuration.
338 if (!desired_types_
.Has(type
)) {
339 // It's possible all types failed to associate, in which case association
341 if (state_
== CONFIGURING
&& associating_types_
.Empty())
342 ModelAssociationDone();
346 DCHECK(!associated_types_
.Has(type
));
347 DCHECK(DataTypeController::IsSuccessfulResult(start_result
));
348 associated_types_
.Put(type
);
350 if (state_
!= CONFIGURING
)
353 TRACE_EVENT_ASYNC_END1("sync", "ModelAssociation",
354 controllers_
->find(type
)->second
.get(),
356 ModelTypeToString(type
));
358 // Track the merge results if we succeeded or an association failure
360 if (syncer::ProtocolTypes().Has(type
)) {
361 base::TimeDelta association_wait_time
=
362 std::max(base::TimeDelta(), type_start_time
- association_start_time_
);
363 base::TimeDelta association_time
=
364 base::TimeTicks::Now() - type_start_time
;;
365 syncer::DataTypeAssociationStats stats
=
366 BuildAssociationStatsFromMergeResults(local_merge_result
,
368 association_wait_time
,
370 delegate_
->OnSingleDataTypeAssociationDone(type
, stats
);
373 associating_types_
.Remove(type
);
375 if (associating_types_
.Empty())
376 ModelAssociationDone();
379 void ModelAssociationManager::ModelAssociationDone() {
380 CHECK_EQ(CONFIGURING
, state_
);
384 // Treat any unfinished types as having errors.
385 desired_types_
.RemoveAll(associating_types_
);
386 for (DataTypeController::TypeMap::const_iterator it
= controllers_
->begin();
387 it
!= controllers_
->end(); ++it
) {
388 DataTypeController
* dtc
= (*it
).second
.get();
389 if (associating_types_
.Has(dtc
->type()) &&
390 dtc
->state() != DataTypeController::NOT_RUNNING
) {
391 UMA_HISTOGRAM_ENUMERATION("Sync.ConfigureFailed",
392 ModelTypeToHistogramInt(dtc
->type()),
393 syncer::MODEL_TYPE_COUNT
);
394 StopDatatype(syncer::SyncError(FROM_HERE
,
395 syncer::SyncError::DATATYPE_ERROR
,
396 "Association timed out.",
402 DataTypeManager::ConfigureResult
result(configure_status_
,
405 // Reset state before notifying |delegate_| because that might
406 // trigger a new round of configuration.
407 ResetForNextAssociation();
410 delegate_
->OnModelAssociationDone(result
);
413 base::OneShotTimer
<ModelAssociationManager
>*
414 ModelAssociationManager::GetTimerForTesting() {
418 } // namespace sync_driver