• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.
4 
5 #include "components/sync_driver/model_association_manager.h"
6 
7 #include <algorithm>
8 #include <functional>
9 
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"
15 
16 using syncer::ModelTypeSet;
17 
18 namespace sync_driver {
19 
20 namespace {
21 
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.
27 
28   // Kick off the association of the non-UI types first so they can associate
29   // in parallel with the UI types.
30   syncer::PASSWORDS,
31   syncer::AUTOFILL,
32   syncer::AUTOFILL_PROFILE,
33   syncer::EXTENSION_SETTINGS,
34   syncer::APP_SETTINGS,
35   syncer::TYPED_URLS,
36   syncer::HISTORY_DELETE_DIRECTIVES,
37   syncer::SYNCED_NOTIFICATIONS,
38   syncer::SYNCED_NOTIFICATION_APP_INFO,
39 
40   // UI thread data types.
41   syncer::BOOKMARKS,
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.
45   syncer::PREFERENCES,
46   syncer::PRIORITY_PREFERENCES,
47   syncer::EXTENSIONS,
48   syncer::APPS,
49   syncer::APP_LIST,
50   syncer::THEMES,
51   syncer::SEARCH_ENGINES,
52   syncer::SESSIONS,
53   syncer::APP_NOTIFICATIONS,
54   syncer::DICTIONARY,
55   syncer::FAVICON_IMAGES,
56   syncer::FAVICON_TRACKING,
57   syncer::SUPERVISED_USER_SETTINGS,
58   syncer::SUPERVISED_USER_SHARED_SETTINGS,
59   syncer::ARTICLES,
60 };
61 
62 COMPILE_ASSERT(arraysize(kStartOrder) ==
63                syncer::MODEL_TYPE_COUNT - syncer::FIRST_REAL_MODEL_TYPE,
64                kStartOrder_IncorrectSize);
65 
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
68 // unfinished types.
69 const int64 kAssociationTimeOutInSeconds = 600;
70 
BuildAssociationStatsFromMergeResults(const syncer::SyncMergeResult & local_merge_result,const syncer::SyncMergeResult & syncer_merge_result,const base::TimeDelta & association_wait_time,const base::TimeDelta & association_time)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;
106   return stats;
107 }
108 
109 }  // namespace
110 
ModelAssociationManager(const DataTypeController::TypeMap * controllers,ModelAssociationManagerDelegate * processor)111 ModelAssociationManager::ModelAssociationManager(
112     const DataTypeController::TypeMap* controllers,
113     ModelAssociationManagerDelegate* processor)
114     : state_(IDLE),
115       controllers_(controllers),
116       delegate_(processor),
117       configure_status_(DataTypeManager::UNKNOWN),
118       weak_ptr_factory_(this) {
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());
123   }
124 }
125 
~ModelAssociationManager()126 ModelAssociationManager::~ModelAssociationManager() {
127 }
128 
Initialize(syncer::ModelTypeSet desired_types)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_);
134 
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());
141   }
142 
143   DVLOG(1) << "ModelAssociationManager: Initializing for "
144            << syncer::ModelTypeSetToString(desired_types_);
145 
146   state_ = INITIALIZED_TO_CONFIGURE;
147 
148   StopDisabledTypes();
149   LoadEnabledTypes();
150 }
151 
StopDatatype(const syncer::SyncError & error,DataTypeController * dtc)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());
158 
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);
162     dtc->Stop();
163   }
164 }
165 
StopDisabledTypes()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);
175     }
176   }
177 }
178 
LoadEnabledTypes()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))
184       continue;
185 
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()));
193     }
194   }
195 }
196 
StartAssociationAsync(const syncer::ModelTypeSet & types_to_associate)197 void ModelAssociationManager::StartAssociationAsync(
198     const syncer::ModelTypeSet& types_to_associate) {
199   DCHECK_NE(CONFIGURING, state_);
200   state_ = CONFIGURING;
201 
202   association_start_time_ = base::TimeTicks::Now();
203 
204   requested_types_ = types_to_associate;
205 
206   associating_types_ = types_to_associate;
207   associating_types_.RetainAll(desired_types_);
208   associating_types_.RemoveAll(associated_types_);
209 
210   // Assume success.
211   configure_status_ = DataTypeManager::OK;
212 
213   // Done if no types to associate.
214   if (associating_types_.Empty()) {
215     ModelAssociationDone();
216     return;
217   }
218 
219   timer_.Start(FROM_HERE,
220                base::TimeDelta::FromSeconds(kAssociationTimeOutInSeconds),
221                this,
222                &ModelAssociationManager::ModelAssociationDone);
223 
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))
228       continue;
229 
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",
235                                dtc,
236                                "DataType",
237                                ModelTypeToString(type));
238 
239       dtc->StartAssociating(
240           base::Bind(&ModelAssociationManager::TypeStartCallback,
241                      weak_ptr_factory_.GetWeakPtr(),
242                      type, base::TimeTicks::Now()));
243     }
244   }
245 }
246 
ResetForNextAssociation()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();
253 }
254 
Stop()255 void ModelAssociationManager::Stop() {
256   // Ignore callbacks from controllers.
257   weak_ptr_factory_.InvalidateWeakPtrs();
258 
259   desired_types_.Clear();
260   loaded_types_.Clear();
261   associated_types_.Clear();
262   associating_types_.Clear();
263 
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();
271     }
272   }
273 
274   if (state_ == CONFIGURING) {
275     if (configure_status_ == DataTypeManager::OK)
276       configure_status_ = DataTypeManager::ABORTED;
277     DVLOG(1) << "ModelAssociationManager: Calling OnModelAssociationDone";
278     ModelAssociationDone();
279   }
280 
281   ResetForNextAssociation();
282 
283   state_ = IDLE;
284 }
285 
ModelLoadCallback(syncer::ModelType type,syncer::SyncError error)286 void ModelAssociationManager::ModelLoadCallback(syncer::ModelType type,
287                                                 syncer::SyncError error) {
288   DVLOG(1) << "ModelAssociationManager: ModelLoadCallback for "
289       << syncer::ModelTypeToString(type);
290 
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;
300   }
301 
302   // This happens when slow loading type is disabled by new configuration.
303   if (!desired_types_.Has(type))
304     return;
305 
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()));
314   }
315 }
316 
TypeStartCallback(syncer::ModelType type,base::TimeTicks type_start_time,DataTypeController::ConfigureResult start_result,const syncer::SyncMergeResult & local_merge_result,const syncer::SyncMergeResult & syncer_merge_result)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);
329 
330     // Update configuration result.
331     if (start_result == DataTypeController::UNRECOVERABLE_ERROR)
332       configure_status_ = DataTypeManager::UNRECOVERABLE_ERROR;
333   }
334 
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_ == CONFIGURING && associating_types_.Empty())
341         ModelAssociationDone();
342       return;
343   }
344 
345   DCHECK(!associated_types_.Has(type));
346   DCHECK(DataTypeController::IsSuccessfulResult(start_result));
347   associated_types_.Put(type);
348 
349   if (state_ != CONFIGURING)
350     return;
351 
352   TRACE_EVENT_ASYNC_END1("sync", "ModelAssociation",
353                          controllers_->find(type)->second.get(),
354                          "DataType",
355                          ModelTypeToString(type));
356 
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);
370   }
371 
372   associating_types_.Remove(type);
373 
374   if (associating_types_.Empty())
375     ModelAssociationDone();
376 }
377 
ModelAssociationDone()378 void ModelAssociationManager::ModelAssociationDone() {
379   CHECK_EQ(CONFIGURING, state_);
380 
381   timer_.Stop();
382 
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.",
396                                      dtc->type()),
397                    dtc);
398     }
399   }
400 
401   DataTypeManager::ConfigureResult result(configure_status_,
402                                           requested_types_);
403 
404   // Reset state before notifying |delegate_| because that might
405   // trigger a new round of configuration.
406   ResetForNextAssociation();
407   state_ = IDLE;
408 
409   delegate_->OnModelAssociationDone(result);
410 }
411 
412 base::OneShotTimer<ModelAssociationManager>*
GetTimerForTesting()413     ModelAssociationManager::GetTimerForTesting() {
414   return &timer_;
415 }
416 
417 }  // namespace sync_driver
418