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::SUPERVISED_USERS
, // Syncing supervised users on initial login
43 // might block creating a new supervised user,
44 // so we 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::SUPERVISED_USER_SETTINGS
,
58 syncer::SUPERVISED_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, DataTypeManager is notified of the
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 ModelAssociationManagerDelegate
* processor
)
115 controllers_(controllers
),
116 delegate_(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();
137 for (syncer::ModelTypeSet::Iterator it
= desired_types
.First();
138 it
.Good(); it
.Inc()) {
139 if (controllers_
->find(it
.Get()) != controllers_
->end())
140 desired_types_
.Put(it
.Get());
143 DVLOG(1) << "ModelAssociationManager: Initializing for "
144 << syncer::ModelTypeSetToString(desired_types_
);
146 state_
= INITIALIZED_TO_CONFIGURE
;
152 void ModelAssociationManager::StopDatatype(
153 const syncer::SyncError
& error
,
154 DataTypeController
* dtc
) {
155 loaded_types_
.Remove(dtc
->type());
156 associated_types_
.Remove(dtc
->type());
157 associating_types_
.Remove(dtc
->type());
159 if (error
.IsSet() || dtc
->state() != DataTypeController::NOT_RUNNING
) {
160 // If an error was set, the delegate must be informed of the error.
161 delegate_
->OnSingleDataTypeWillStop(dtc
->type(), error
);
166 void ModelAssociationManager::StopDisabledTypes() {
167 DVLOG(1) << "ModelAssociationManager: Stopping disabled types.";
168 for (DataTypeController::TypeMap::const_iterator it
= controllers_
->begin();
169 it
!= controllers_
->end(); ++it
) {
170 DataTypeController
* dtc
= (*it
).second
.get();
171 if (dtc
->state() != DataTypeController::NOT_RUNNING
&&
172 !desired_types_
.Has(dtc
->type())) {
173 DVLOG(1) << "ModelTypeToString: stop " << dtc
->name();
174 StopDatatype(syncer::SyncError(), dtc
);
179 void ModelAssociationManager::LoadEnabledTypes() {
180 // Load in kStartOrder.
181 for (size_t i
= 0; i
< arraysize(kStartOrder
); i
++) {
182 syncer::ModelType type
= kStartOrder
[i
];
183 if (!desired_types_
.Has(type
))
186 DCHECK(controllers_
->find(type
) != controllers_
->end());
187 DataTypeController
* dtc
= controllers_
->find(type
)->second
.get();
188 if (dtc
->state() == DataTypeController::NOT_RUNNING
) {
189 DCHECK(!loaded_types_
.Has(dtc
->type()));
190 DCHECK(!associated_types_
.Has(dtc
->type()));
191 dtc
->LoadModels(base::Bind(&ModelAssociationManager::ModelLoadCallback
,
192 weak_ptr_factory_
.GetWeakPtr()));
197 void ModelAssociationManager::StartAssociationAsync(
198 const syncer::ModelTypeSet
& types_to_associate
) {
199 DCHECK_NE(CONFIGURING
, state_
);
200 state_
= CONFIGURING
;
202 association_start_time_
= base::TimeTicks::Now();
204 requested_types_
= types_to_associate
;
206 associating_types_
= types_to_associate
;
207 associating_types_
.RetainAll(desired_types_
);
208 associating_types_
.RemoveAll(associated_types_
);
211 configure_status_
= DataTypeManager::OK
;
213 // Done if no types to associate.
214 if (associating_types_
.Empty()) {
215 ModelAssociationDone();
219 timer_
.Start(FROM_HERE
,
220 base::TimeDelta::FromSeconds(kAssociationTimeOutInSeconds
),
222 &ModelAssociationManager::ModelAssociationDone
);
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
))
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",
237 ModelTypeToString(type
));
239 dtc
->StartAssociating(
240 base::Bind(&ModelAssociationManager::TypeStartCallback
,
241 weak_ptr_factory_
.GetWeakPtr(),
242 type
, base::TimeTicks::Now()));
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 associating_types_
.Clear();
255 void ModelAssociationManager::Stop() {
256 // Ignore callbacks from controllers.
257 weak_ptr_factory_
.InvalidateWeakPtrs();
259 desired_types_
.Clear();
260 loaded_types_
.Clear();
261 associated_types_
.Clear();
262 associating_types_
.Clear();
264 // Stop started data types.
265 for (DataTypeController::TypeMap::const_iterator it
= controllers_
->begin();
266 it
!= controllers_
->end(); ++it
) {
267 DataTypeController
* dtc
= (*it
).second
.get();
268 if (dtc
->state() != DataTypeController::NOT_RUNNING
) {
269 StopDatatype(syncer::SyncError(), dtc
);
270 DVLOG(1) << "ModelAssociationManager: Stopped " << dtc
->name();
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::ModelLoadCallback(syncer::ModelType type
,
287 syncer::SyncError error
) {
288 DVLOG(1) << "ModelAssociationManager: ModelLoadCallback for "
289 << syncer::ModelTypeToString(type
);
291 // This happens when slow loading type is disabled by new configuration.
292 if (!desired_types_
.Has(type
))
295 DCHECK(!loaded_types_
.Has(type
));
297 syncer::SyncMergeResult
local_merge_result(type
);
298 local_merge_result
.set_error(error
);
299 TypeStartCallback(type
,
300 base::TimeTicks::Now(),
301 DataTypeController::ASSOCIATION_FAILED
,
303 syncer::SyncMergeResult(type
));
307 loaded_types_
.Put(type
);
308 if (associating_types_
.Has(type
)) {
309 DataTypeController
* dtc
= controllers_
->find(type
)->second
.get();
310 dtc
->StartAssociating(
311 base::Bind(&ModelAssociationManager::TypeStartCallback
,
312 weak_ptr_factory_
.GetWeakPtr(),
313 type
, base::TimeTicks::Now()));
317 void ModelAssociationManager::TypeStartCallback(
318 syncer::ModelType type
,
319 base::TimeTicks type_start_time
,
320 DataTypeController::ConfigureResult start_result
,
321 const syncer::SyncMergeResult
& local_merge_result
,
322 const syncer::SyncMergeResult
& syncer_merge_result
) {
323 if (desired_types_
.Has(type
) &&
324 !DataTypeController::IsSuccessfulResult(start_result
)) {
325 DVLOG(1) << "ModelAssociationManager: Type encountered an error.";
326 desired_types_
.Remove(type
);
327 DataTypeController
* dtc
= controllers_
->find(type
)->second
.get();
328 StopDatatype(local_merge_result
.error(), dtc
);
330 // Update configuration result.
331 if (start_result
== DataTypeController::UNRECOVERABLE_ERROR
)
332 configure_status_
= DataTypeManager::UNRECOVERABLE_ERROR
;
335 // This happens when a slow associating type is disabled or if a type
336 // disables itself after initial configuration.
337 if (!desired_types_
.Has(type
)) {
338 // It's possible all types failed to associate, in which case association
340 if (state_
== CONFIGURING
&& associating_types_
.Empty())
341 ModelAssociationDone();
345 DCHECK(!associated_types_
.Has(type
));
346 DCHECK(DataTypeController::IsSuccessfulResult(start_result
));
347 associated_types_
.Put(type
);
349 if (state_
!= CONFIGURING
)
352 TRACE_EVENT_ASYNC_END1("sync", "ModelAssociation",
353 controllers_
->find(type
)->second
.get(),
355 ModelTypeToString(type
));
357 // Track the merge results if we succeeded or an association failure
359 if (syncer::ProtocolTypes().Has(type
)) {
360 base::TimeDelta association_wait_time
=
361 std::max(base::TimeDelta(), type_start_time
- association_start_time_
);
362 base::TimeDelta association_time
=
363 base::TimeTicks::Now() - type_start_time
;;
364 syncer::DataTypeAssociationStats stats
=
365 BuildAssociationStatsFromMergeResults(local_merge_result
,
367 association_wait_time
,
369 delegate_
->OnSingleDataTypeAssociationDone(type
, stats
);
372 associating_types_
.Remove(type
);
374 if (associating_types_
.Empty())
375 ModelAssociationDone();
378 void ModelAssociationManager::ModelAssociationDone() {
379 CHECK_EQ(CONFIGURING
, state_
);
383 // Treat any unfinished types as having errors.
384 desired_types_
.RemoveAll(associating_types_
);
385 for (DataTypeController::TypeMap::const_iterator it
= controllers_
->begin();
386 it
!= controllers_
->end(); ++it
) {
387 DataTypeController
* dtc
= (*it
).second
.get();
388 if (associating_types_
.Has(dtc
->type()) &&
389 dtc
->state() != DataTypeController::NOT_RUNNING
) {
390 UMA_HISTOGRAM_ENUMERATION("Sync.ConfigureFailed",
391 ModelTypeToHistogramInt(dtc
->type()),
392 syncer::MODEL_TYPE_COUNT
);
393 StopDatatype(syncer::SyncError(FROM_HERE
,
394 syncer::SyncError::DATATYPE_ERROR
,
395 "Association timed out.",
401 DataTypeManager::ConfigureResult
result(configure_status_
,
404 // Reset state before notifying |delegate_| because that might
405 // trigger a new round of configuration.
406 ResetForNextAssociation();
409 delegate_
->OnModelAssociationDone(result
);
412 base::OneShotTimer
<ModelAssociationManager
>*
413 ModelAssociationManager::GetTimerForTesting() {
417 } // namespace sync_driver