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/logging.h"
11 #include "base/message_loop/message_loop.h"
12 #include "base/metrics/histogram_macros.h"
13 #include "base/trace_event/trace_event.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::AUTOFILL_WALLET_DATA
,
34 syncer::AUTOFILL_WALLET_METADATA
,
35 syncer::EXTENSION_SETTINGS
,
38 syncer::HISTORY_DELETE_DIRECTIVES
,
39 syncer::SYNCED_NOTIFICATIONS
,
40 syncer::SYNCED_NOTIFICATION_APP_INFO
,
42 // UI thread data types.
44 syncer::SUPERVISED_USERS
, // Syncing supervised users on initial login
45 // might block creating a new supervised user,
46 // so we want to do it early.
48 syncer::PRIORITY_PREFERENCES
,
53 syncer::SEARCH_ENGINES
,
55 syncer::APP_NOTIFICATIONS
,
57 syncer::FAVICON_IMAGES
,
58 syncer::FAVICON_TRACKING
,
59 syncer::SUPERVISED_USER_SETTINGS
,
60 syncer::SUPERVISED_USER_SHARED_SETTINGS
,
61 syncer::SUPERVISED_USER_WHITELISTS
,
63 syncer::WIFI_CREDENTIALS
,
66 static_assert(arraysize(kStartOrder
) ==
67 syncer::MODEL_TYPE_COUNT
- syncer::FIRST_REAL_MODEL_TYPE
,
68 "kStartOrder must have MODEL_TYPE_COUNT - "
69 "FIRST_REAL_MODEL_TYPE elements");
71 // The amount of time we wait for association to finish. If some types haven't
72 // finished association by the time, DataTypeManager is notified of the
74 const int64 kAssociationTimeOutInSeconds
= 600;
76 syncer::DataTypeAssociationStats
BuildAssociationStatsFromMergeResults(
77 const syncer::SyncMergeResult
& local_merge_result
,
78 const syncer::SyncMergeResult
& syncer_merge_result
,
79 const base::TimeDelta
& association_wait_time
,
80 const base::TimeDelta
& association_time
) {
81 DCHECK_EQ(local_merge_result
.model_type(), syncer_merge_result
.model_type());
82 syncer::DataTypeAssociationStats stats
;
83 stats
.had_error
= local_merge_result
.error().IsSet() ||
84 syncer_merge_result
.error().IsSet();
85 stats
.num_local_items_before_association
=
86 local_merge_result
.num_items_before_association();
87 stats
.num_sync_items_before_association
=
88 syncer_merge_result
.num_items_before_association();
89 stats
.num_local_items_after_association
=
90 local_merge_result
.num_items_after_association();
91 stats
.num_sync_items_after_association
=
92 syncer_merge_result
.num_items_after_association();
93 stats
.num_local_items_added
=
94 local_merge_result
.num_items_added();
95 stats
.num_local_items_deleted
=
96 local_merge_result
.num_items_deleted();
97 stats
.num_local_items_modified
=
98 local_merge_result
.num_items_modified();
99 stats
.local_version_pre_association
=
100 local_merge_result
.pre_association_version();
101 stats
.num_sync_items_added
=
102 syncer_merge_result
.num_items_added();
103 stats
.num_sync_items_deleted
=
104 syncer_merge_result
.num_items_deleted();
105 stats
.num_sync_items_modified
=
106 syncer_merge_result
.num_items_modified();
107 stats
.sync_version_pre_association
=
108 syncer_merge_result
.pre_association_version();
109 stats
.association_wait_time
= association_wait_time
;
110 stats
.association_time
= association_time
;
116 ModelAssociationManager::ModelAssociationManager(
117 const DataTypeController::TypeMap
* controllers
,
118 ModelAssociationManagerDelegate
* processor
)
120 controllers_(controllers
),
121 delegate_(processor
),
122 configure_status_(DataTypeManager::UNKNOWN
),
123 weak_ptr_factory_(this) {
124 // Ensure all data type controllers are stopped.
125 for (DataTypeController::TypeMap::const_iterator it
= controllers_
->begin();
126 it
!= controllers_
->end(); ++it
) {
127 DCHECK_EQ(DataTypeController::NOT_RUNNING
, (*it
).second
->state());
131 ModelAssociationManager::~ModelAssociationManager() {
134 void ModelAssociationManager::Initialize(syncer::ModelTypeSet desired_types
) {
135 // state_ can be INITIALIZED if types are reconfigured when
136 // data is being downloaded, so StartAssociationAsync() is never called for
137 // the first configuration.
138 DCHECK_NE(ASSOCIATING
, state_
);
140 // Only keep types that have controllers.
141 desired_types_
.Clear();
142 for (syncer::ModelTypeSet::Iterator it
= desired_types
.First();
143 it
.Good(); it
.Inc()) {
144 if (controllers_
->find(it
.Get()) != controllers_
->end())
145 desired_types_
.Put(it
.Get());
148 DVLOG(1) << "ModelAssociationManager: Initializing for "
149 << syncer::ModelTypeSetToString(desired_types_
);
151 state_
= INITIALIZED
;
157 void ModelAssociationManager::StopDatatype(
158 const syncer::SyncError
& error
,
159 DataTypeController
* dtc
) {
160 loaded_types_
.Remove(dtc
->type());
161 associated_types_
.Remove(dtc
->type());
162 associating_types_
.Remove(dtc
->type());
164 if (error
.IsSet() || dtc
->state() != DataTypeController::NOT_RUNNING
) {
165 // If an error was set, the delegate must be informed of the error.
166 delegate_
->OnSingleDataTypeWillStop(dtc
->type(), error
);
171 void ModelAssociationManager::StopDisabledTypes() {
172 DVLOG(1) << "ModelAssociationManager: Stopping disabled types.";
173 for (DataTypeController::TypeMap::const_iterator it
= controllers_
->begin();
174 it
!= controllers_
->end(); ++it
) {
175 DataTypeController
* dtc
= (*it
).second
.get();
176 if (dtc
->state() != DataTypeController::NOT_RUNNING
&&
177 !desired_types_
.Has(dtc
->type())) {
178 DVLOG(1) << "ModelAssociationManager: stop " << dtc
->name();
179 StopDatatype(syncer::SyncError(), dtc
);
184 void ModelAssociationManager::LoadEnabledTypes() {
185 // Load in kStartOrder.
186 for (size_t i
= 0; i
< arraysize(kStartOrder
); i
++) {
187 syncer::ModelType type
= kStartOrder
[i
];
188 if (!desired_types_
.Has(type
))
191 DCHECK(controllers_
->find(type
) != controllers_
->end());
192 DataTypeController
* dtc
= controllers_
->find(type
)->second
.get();
193 if (dtc
->state() == DataTypeController::NOT_RUNNING
) {
194 DCHECK(!loaded_types_
.Has(dtc
->type()));
195 DCHECK(!associated_types_
.Has(dtc
->type()));
196 dtc
->LoadModels(base::Bind(&ModelAssociationManager::ModelLoadCallback
,
197 weak_ptr_factory_
.GetWeakPtr()));
202 void ModelAssociationManager::StartAssociationAsync(
203 const syncer::ModelTypeSet
& types_to_associate
) {
204 DCHECK_EQ(INITIALIZED
, state_
);
205 DVLOG(1) << "Starting association for "
206 << syncer::ModelTypeSetToString(types_to_associate
);
207 state_
= ASSOCIATING
;
209 association_start_time_
= base::TimeTicks::Now();
211 requested_types_
= types_to_associate
;
213 associating_types_
= types_to_associate
;
214 associating_types_
.RetainAll(desired_types_
);
215 associating_types_
.RemoveAll(associated_types_
);
218 configure_status_
= DataTypeManager::OK
;
220 // Done if no types to associate.
221 if (associating_types_
.Empty()) {
222 ModelAssociationDone(INITIALIZED
);
226 timer_
.Start(FROM_HERE
,
227 base::TimeDelta::FromSeconds(kAssociationTimeOutInSeconds
),
228 base::Bind(&ModelAssociationManager::ModelAssociationDone
,
229 weak_ptr_factory_
.GetWeakPtr(),
232 // Start association of types that are loaded in specified order.
233 for (size_t i
= 0; i
< arraysize(kStartOrder
); i
++) {
234 syncer::ModelType type
= kStartOrder
[i
];
235 if (!associating_types_
.Has(type
) || !loaded_types_
.Has(type
))
238 DataTypeController
* dtc
= controllers_
->find(type
)->second
.get();
239 DCHECK(DataTypeController::MODEL_LOADED
== dtc
->state() ||
240 DataTypeController::ASSOCIATING
== dtc
->state());
241 if (dtc
->state() == DataTypeController::MODEL_LOADED
) {
242 TRACE_EVENT_ASYNC_BEGIN1("sync", "ModelAssociation",
245 ModelTypeToString(type
));
247 dtc
->StartAssociating(
248 base::Bind(&ModelAssociationManager::TypeStartCallback
,
249 weak_ptr_factory_
.GetWeakPtr(),
250 type
, base::TimeTicks::Now()));
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
) {
264 StopDatatype(syncer::SyncError(), dtc
);
265 DVLOG(1) << "ModelAssociationManager: Stopped " << dtc
->name();
269 desired_types_
.Clear();
270 loaded_types_
.Clear();
271 associated_types_
.Clear();
273 if (state_
== ASSOCIATING
) {
274 if (configure_status_
== DataTypeManager::OK
)
275 configure_status_
= DataTypeManager::ABORTED
;
276 DVLOG(1) << "ModelAssociationManager: Calling OnModelAssociationDone";
277 ModelAssociationDone(IDLE
);
279 DCHECK(associating_types_
.Empty());
280 DCHECK(requested_types_
.Empty());
285 void ModelAssociationManager::ModelLoadCallback(syncer::ModelType type
,
286 syncer::SyncError error
) {
287 DVLOG(1) << "ModelAssociationManager: ModelLoadCallback for "
288 << syncer::ModelTypeToString(type
);
291 syncer::SyncMergeResult
local_merge_result(type
);
292 local_merge_result
.set_error(error
);
293 TypeStartCallback(type
,
294 base::TimeTicks::Now(),
295 DataTypeController::ASSOCIATION_FAILED
,
297 syncer::SyncMergeResult(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
));
306 loaded_types_
.Put(type
);
307 if (associating_types_
.Has(type
)) {
308 DataTypeController
* dtc
= controllers_
->find(type
)->second
.get();
309 dtc
->StartAssociating(
310 base::Bind(&ModelAssociationManager::TypeStartCallback
,
311 weak_ptr_factory_
.GetWeakPtr(),
312 type
, base::TimeTicks::Now()));
316 void ModelAssociationManager::TypeStartCallback(
317 syncer::ModelType type
,
318 base::TimeTicks type_start_time
,
319 DataTypeController::ConfigureResult start_result
,
320 const syncer::SyncMergeResult
& local_merge_result
,
321 const syncer::SyncMergeResult
& syncer_merge_result
) {
322 if (desired_types_
.Has(type
) &&
323 !DataTypeController::IsSuccessfulResult(start_result
)) {
324 DVLOG(1) << "ModelAssociationManager: Type encountered an error.";
325 desired_types_
.Remove(type
);
326 DataTypeController
* dtc
= controllers_
->find(type
)->second
.get();
327 StopDatatype(local_merge_result
.error(), dtc
);
329 // Update configuration result.
330 if (start_result
== DataTypeController::UNRECOVERABLE_ERROR
)
331 configure_status_
= DataTypeManager::UNRECOVERABLE_ERROR
;
334 // This happens when a slow associating type is disabled or if a type
335 // disables itself after initial configuration.
336 if (!desired_types_
.Has(type
)) {
337 // It's possible all types failed to associate, in which case association
339 if (state_
== ASSOCIATING
&& associating_types_
.Empty())
340 ModelAssociationDone(INITIALIZED
);
344 DCHECK(!associated_types_
.Has(type
));
345 DCHECK(DataTypeController::IsSuccessfulResult(start_result
));
346 associated_types_
.Put(type
);
348 if (state_
!= ASSOCIATING
)
351 TRACE_EVENT_ASYNC_END1("sync", "ModelAssociation",
352 controllers_
->find(type
)->second
.get(),
354 ModelTypeToString(type
));
356 // Track the merge results if we succeeded or an association failure
358 if (syncer::ProtocolTypes().Has(type
)) {
359 base::TimeDelta association_wait_time
=
360 std::max(base::TimeDelta(), type_start_time
- association_start_time_
);
361 base::TimeDelta association_time
=
362 base::TimeTicks::Now() - type_start_time
;;
363 syncer::DataTypeAssociationStats stats
=
364 BuildAssociationStatsFromMergeResults(local_merge_result
,
366 association_wait_time
,
368 delegate_
->OnSingleDataTypeAssociationDone(type
, stats
);
371 associating_types_
.Remove(type
);
373 if (associating_types_
.Empty())
374 ModelAssociationDone(INITIALIZED
);
377 void ModelAssociationManager::ModelAssociationDone(State new_state
) {
378 DCHECK_NE(IDLE
, state_
);
380 if (state_
== INITIALIZED
) {
381 // No associations are currently happening. Just reset the state.
386 DVLOG(1) << "Model association complete for "
387 << syncer::ModelTypeSetToString(requested_types_
);
391 // Treat any unfinished types as having errors.
392 desired_types_
.RemoveAll(associating_types_
);
393 for (DataTypeController::TypeMap::const_iterator it
= controllers_
->begin();
394 it
!= controllers_
->end(); ++it
) {
395 DataTypeController
* dtc
= (*it
).second
.get();
396 if (associating_types_
.Has(dtc
->type()) &&
397 dtc
->state() != DataTypeController::NOT_RUNNING
) {
398 UMA_HISTOGRAM_ENUMERATION("Sync.ConfigureFailed",
399 ModelTypeToHistogramInt(dtc
->type()),
400 syncer::MODEL_TYPE_COUNT
);
401 StopDatatype(syncer::SyncError(FROM_HERE
,
402 syncer::SyncError::DATATYPE_ERROR
,
403 "Association timed out.",
409 DataTypeManager::ConfigureResult
result(configure_status_
,
412 // Need to reset state before invoking delegate in order to avoid re-entrancy
413 // issues (delegate may trigger a reconfiguration).
414 associating_types_
.Clear();
415 requested_types_
.Clear();
418 delegate_
->OnModelAssociationDone(result
);
421 base::OneShotTimer
<ModelAssociationManager
>*
422 ModelAssociationManager::GetTimerForTesting() {
426 } // namespace sync_driver