[Sync] Componentize UIModelWorker.
[chromium-blink-merge.git] / components / sync_driver / model_association_manager.cc
blob7602385a1071eb79302697fc74d8853b9030a8ee
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"
7 #include <algorithm>
8 #include <functional>
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/api/sync_merge_result.h"
15 #include "sync/internal_api/public/base/model_type.h"
17 using syncer::ModelTypeSet;
19 namespace sync_driver {
21 namespace {
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.
31 syncer::PASSWORDS,
32 syncer::AUTOFILL,
33 syncer::AUTOFILL_PROFILE,
34 syncer::AUTOFILL_WALLET_DATA,
35 syncer::AUTOFILL_WALLET_METADATA,
36 syncer::EXTENSION_SETTINGS,
37 syncer::APP_SETTINGS,
38 syncer::TYPED_URLS,
39 syncer::HISTORY_DELETE_DIRECTIVES,
40 syncer::SYNCED_NOTIFICATIONS,
41 syncer::SYNCED_NOTIFICATION_APP_INFO,
43 // UI thread data types.
44 syncer::BOOKMARKS,
45 syncer::SUPERVISED_USERS, // Syncing supervised users on initial login
46 // might block creating a new supervised user,
47 // so we want to do it early.
48 syncer::PREFERENCES,
49 syncer::PRIORITY_PREFERENCES,
50 syncer::EXTENSIONS,
51 syncer::APPS,
52 syncer::APP_LIST,
53 syncer::THEMES,
54 syncer::SEARCH_ENGINES,
55 syncer::SESSIONS,
56 syncer::APP_NOTIFICATIONS,
57 syncer::DICTIONARY,
58 syncer::FAVICON_IMAGES,
59 syncer::FAVICON_TRACKING,
60 syncer::SUPERVISED_USER_SETTINGS,
61 syncer::SUPERVISED_USER_SHARED_SETTINGS,
62 syncer::SUPERVISED_USER_WHITELISTS,
63 syncer::ARTICLES,
64 syncer::WIFI_CREDENTIALS,
67 static_assert(arraysize(kStartOrder) ==
68 syncer::MODEL_TYPE_COUNT - syncer::FIRST_REAL_MODEL_TYPE,
69 "kStartOrder must have MODEL_TYPE_COUNT - "
70 "FIRST_REAL_MODEL_TYPE elements");
72 // The amount of time we wait for association to finish. If some types haven't
73 // finished association by the time, DataTypeManager is notified of the
74 // unfinished types.
75 const int64 kAssociationTimeOutInSeconds = 600;
77 syncer::DataTypeAssociationStats BuildAssociationStatsFromMergeResults(
78 const syncer::SyncMergeResult& local_merge_result,
79 const syncer::SyncMergeResult& syncer_merge_result,
80 const base::TimeDelta& association_wait_time,
81 const base::TimeDelta& association_time) {
82 DCHECK_EQ(local_merge_result.model_type(), syncer_merge_result.model_type());
83 syncer::DataTypeAssociationStats stats;
84 stats.had_error = local_merge_result.error().IsSet() ||
85 syncer_merge_result.error().IsSet();
86 stats.num_local_items_before_association =
87 local_merge_result.num_items_before_association();
88 stats.num_sync_items_before_association =
89 syncer_merge_result.num_items_before_association();
90 stats.num_local_items_after_association =
91 local_merge_result.num_items_after_association();
92 stats.num_sync_items_after_association =
93 syncer_merge_result.num_items_after_association();
94 stats.num_local_items_added =
95 local_merge_result.num_items_added();
96 stats.num_local_items_deleted =
97 local_merge_result.num_items_deleted();
98 stats.num_local_items_modified =
99 local_merge_result.num_items_modified();
100 stats.local_version_pre_association =
101 local_merge_result.pre_association_version();
102 stats.num_sync_items_added =
103 syncer_merge_result.num_items_added();
104 stats.num_sync_items_deleted =
105 syncer_merge_result.num_items_deleted();
106 stats.num_sync_items_modified =
107 syncer_merge_result.num_items_modified();
108 stats.sync_version_pre_association =
109 syncer_merge_result.pre_association_version();
110 stats.association_wait_time = association_wait_time;
111 stats.association_time = association_time;
112 return stats;
115 } // namespace
117 ModelAssociationManager::ModelAssociationManager(
118 const DataTypeController::TypeMap* controllers,
119 ModelAssociationManagerDelegate* processor)
120 : state_(IDLE),
121 controllers_(controllers),
122 delegate_(processor),
123 configure_status_(DataTypeManager::UNKNOWN),
124 weak_ptr_factory_(this) {
125 // Ensure all data type controllers are stopped.
126 for (DataTypeController::TypeMap::const_iterator it = controllers_->begin();
127 it != controllers_->end(); ++it) {
128 DCHECK_EQ(DataTypeController::NOT_RUNNING, (*it).second->state());
132 ModelAssociationManager::~ModelAssociationManager() {
135 void ModelAssociationManager::Initialize(syncer::ModelTypeSet desired_types) {
136 // state_ can be INITIALIZED if types are reconfigured when
137 // data is being downloaded, so StartAssociationAsync() is never called for
138 // the first configuration.
139 DCHECK_NE(ASSOCIATING, state_);
141 // Only keep types that have controllers.
142 desired_types_.Clear();
143 for (syncer::ModelTypeSet::Iterator it = desired_types.First();
144 it.Good(); it.Inc()) {
145 if (controllers_->find(it.Get()) != controllers_->end())
146 desired_types_.Put(it.Get());
149 DVLOG(1) << "ModelAssociationManager: Initializing for "
150 << syncer::ModelTypeSetToString(desired_types_);
152 state_ = INITIALIZED;
154 StopDisabledTypes();
155 LoadEnabledTypes();
158 void ModelAssociationManager::StopDatatype(
159 const syncer::SyncError& error,
160 DataTypeController* dtc) {
161 loaded_types_.Remove(dtc->type());
162 associated_types_.Remove(dtc->type());
163 associating_types_.Remove(dtc->type());
165 if (error.IsSet() || dtc->state() != DataTypeController::NOT_RUNNING) {
166 // If an error was set, the delegate must be informed of the error.
167 delegate_->OnSingleDataTypeWillStop(dtc->type(), error);
168 dtc->Stop();
172 void ModelAssociationManager::StopDisabledTypes() {
173 DVLOG(1) << "ModelAssociationManager: Stopping disabled types.";
174 for (DataTypeController::TypeMap::const_iterator it = controllers_->begin();
175 it != controllers_->end(); ++it) {
176 DataTypeController* dtc = (*it).second.get();
177 if (dtc->state() != DataTypeController::NOT_RUNNING &&
178 !desired_types_.Has(dtc->type())) {
179 DVLOG(1) << "ModelAssociationManager: stop " << dtc->name();
180 StopDatatype(syncer::SyncError(), dtc);
185 void ModelAssociationManager::LoadEnabledTypes() {
186 // Load in kStartOrder.
187 for (size_t i = 0; i < arraysize(kStartOrder); i++) {
188 syncer::ModelType type = kStartOrder[i];
189 if (!desired_types_.Has(type))
190 continue;
192 DCHECK(controllers_->find(type) != controllers_->end());
193 DataTypeController* dtc = controllers_->find(type)->second.get();
194 if (dtc->state() == DataTypeController::NOT_RUNNING) {
195 DCHECK(!loaded_types_.Has(dtc->type()));
196 DCHECK(!associated_types_.Has(dtc->type()));
197 dtc->LoadModels(base::Bind(&ModelAssociationManager::ModelLoadCallback,
198 weak_ptr_factory_.GetWeakPtr()));
203 void ModelAssociationManager::StartAssociationAsync(
204 const syncer::ModelTypeSet& types_to_associate) {
205 DCHECK_EQ(INITIALIZED, state_);
206 DVLOG(1) << "Starting association for "
207 << syncer::ModelTypeSetToString(types_to_associate);
208 state_ = ASSOCIATING;
210 association_start_time_ = base::TimeTicks::Now();
212 requested_types_ = types_to_associate;
214 associating_types_ = types_to_associate;
215 associating_types_.RetainAll(desired_types_);
216 associating_types_.RemoveAll(associated_types_);
218 // Assume success.
219 configure_status_ = DataTypeManager::OK;
221 // Done if no types to associate.
222 if (associating_types_.Empty()) {
223 ModelAssociationDone(INITIALIZED);
224 return;
227 timer_.Start(FROM_HERE,
228 base::TimeDelta::FromSeconds(kAssociationTimeOutInSeconds),
229 base::Bind(&ModelAssociationManager::ModelAssociationDone,
230 weak_ptr_factory_.GetWeakPtr(),
231 INITIALIZED));
233 // Start association of types that are loaded in specified order.
234 for (size_t i = 0; i < arraysize(kStartOrder); i++) {
235 syncer::ModelType type = kStartOrder[i];
236 if (!associating_types_.Has(type) || !loaded_types_.Has(type))
237 continue;
239 DataTypeController* dtc = controllers_->find(type)->second.get();
240 DCHECK(DataTypeController::MODEL_LOADED == dtc->state() ||
241 DataTypeController::ASSOCIATING == dtc->state());
242 if (dtc->state() == DataTypeController::MODEL_LOADED) {
243 TRACE_EVENT_ASYNC_BEGIN1("sync", "ModelAssociation",
244 dtc,
245 "DataType",
246 ModelTypeToString(type));
248 dtc->StartAssociating(
249 base::Bind(&ModelAssociationManager::TypeStartCallback,
250 weak_ptr_factory_.GetWeakPtr(),
251 type, base::TimeTicks::Now()));
256 void ModelAssociationManager::Stop() {
257 // Ignore callbacks from controllers.
258 weak_ptr_factory_.InvalidateWeakPtrs();
260 // Stop started data types.
261 for (DataTypeController::TypeMap::const_iterator it = controllers_->begin();
262 it != controllers_->end(); ++it) {
263 DataTypeController* dtc = (*it).second.get();
264 if (dtc->state() != DataTypeController::NOT_RUNNING) {
265 StopDatatype(syncer::SyncError(), dtc);
266 DVLOG(1) << "ModelAssociationManager: Stopped " << dtc->name();
270 desired_types_.Clear();
271 loaded_types_.Clear();
272 associated_types_.Clear();
274 if (state_ == ASSOCIATING) {
275 if (configure_status_ == DataTypeManager::OK)
276 configure_status_ = DataTypeManager::ABORTED;
277 DVLOG(1) << "ModelAssociationManager: Calling OnModelAssociationDone";
278 ModelAssociationDone(IDLE);
279 } else {
280 DCHECK(associating_types_.Empty());
281 DCHECK(requested_types_.Empty());
282 state_ = IDLE;
286 void ModelAssociationManager::ModelLoadCallback(syncer::ModelType type,
287 syncer::SyncError error) {
288 DVLOG(1) << "ModelAssociationManager: ModelLoadCallback for "
289 << syncer::ModelTypeToString(type);
291 if (error.IsSet()) {
292 syncer::SyncMergeResult local_merge_result(type);
293 local_merge_result.set_error(error);
294 TypeStartCallback(type,
295 base::TimeTicks::Now(),
296 DataTypeController::ASSOCIATION_FAILED,
297 local_merge_result,
298 syncer::SyncMergeResult(type));
299 return;
302 // This happens when slow loading type is disabled by new configuration.
303 if (!desired_types_.Has(type))
304 return;
306 DCHECK(!loaded_types_.Has(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
339 // is complete.
340 if (state_ == ASSOCIATING && associating_types_.Empty())
341 ModelAssociationDone(INITIALIZED);
342 return;
345 DCHECK(!associated_types_.Has(type));
346 DCHECK(DataTypeController::IsSuccessfulResult(start_result));
347 associated_types_.Put(type);
349 if (state_ != ASSOCIATING)
350 return;
352 TRACE_EVENT_ASYNC_END1("sync", "ModelAssociation",
353 controllers_->find(type)->second.get(),
354 "DataType",
355 ModelTypeToString(type));
357 // Track the merge results if we succeeded or an association failure
358 // occurred.
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,
366 syncer_merge_result,
367 association_wait_time,
368 association_time);
369 delegate_->OnSingleDataTypeAssociationDone(type, stats);
372 associating_types_.Remove(type);
374 if (associating_types_.Empty())
375 ModelAssociationDone(INITIALIZED);
378 void ModelAssociationManager::ModelAssociationDone(State new_state) {
379 DCHECK_NE(IDLE, state_);
381 if (state_ == INITIALIZED) {
382 // No associations are currently happening. Just reset the state.
383 state_ = new_state;
384 return;
387 DVLOG(1) << "Model association complete for "
388 << syncer::ModelTypeSetToString(requested_types_);
390 timer_.Stop();
392 // Treat any unfinished types as having errors.
393 desired_types_.RemoveAll(associating_types_);
394 for (DataTypeController::TypeMap::const_iterator it = controllers_->begin();
395 it != controllers_->end(); ++it) {
396 DataTypeController* dtc = (*it).second.get();
397 if (associating_types_.Has(dtc->type()) &&
398 dtc->state() != DataTypeController::NOT_RUNNING) {
399 UMA_HISTOGRAM_ENUMERATION("Sync.ConfigureFailed",
400 ModelTypeToHistogramInt(dtc->type()),
401 syncer::MODEL_TYPE_COUNT);
402 StopDatatype(syncer::SyncError(FROM_HERE,
403 syncer::SyncError::DATATYPE_ERROR,
404 "Association timed out.",
405 dtc->type()),
406 dtc);
410 DataTypeManager::ConfigureResult result(configure_status_,
411 requested_types_);
413 // Need to reset state before invoking delegate in order to avoid re-entrancy
414 // issues (delegate may trigger a reconfiguration).
415 associating_types_.Clear();
416 requested_types_.Clear();
417 state_ = new_state;
419 delegate_->OnModelAssociationDone(result);
422 base::OneShotTimer<ModelAssociationManager>*
423 ModelAssociationManager::GetTimerForTesting() {
424 return &timer_;
427 } // namespace sync_driver