| OLD | NEW |
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "sync/engine/update_applicator.h" | 5 #include "sync/engine/update_applicator.h" |
| 6 | 6 |
| 7 #include <vector> | 7 #include <vector> |
| 8 | 8 |
| 9 #include "base/logging.h" | 9 #include "base/logging.h" |
| 10 #include "sync/engine/syncer_util.h" | 10 #include "sync/engine/syncer_util.h" |
| 11 #include "sync/sessions/session_state.h" | |
| 12 #include "sync/syncable/entry.h" | 11 #include "sync/syncable/entry.h" |
| 13 #include "sync/syncable/mutable_entry.h" | 12 #include "sync/syncable/mutable_entry.h" |
| 14 #include "sync/syncable/syncable_id.h" | 13 #include "sync/syncable/syncable_id.h" |
| 15 #include "sync/syncable/write_transaction.h" | 14 #include "sync/syncable/write_transaction.h" |
| 16 | 15 |
| 17 using std::vector; | 16 using std::vector; |
| 18 | 17 |
| 19 namespace syncer { | 18 namespace syncer { |
| 20 | 19 |
| 21 UpdateApplicator::UpdateApplicator(ConflictResolver* resolver, | 20 using syncable::ID; |
| 22 Cryptographer* cryptographer, | 21 |
| 23 const UpdateIterator& begin, | 22 UpdateApplicator::UpdateApplicator(Cryptographer* cryptographer, |
| 24 const UpdateIterator& end, | |
| 25 const ModelSafeRoutingInfo& routes, | 23 const ModelSafeRoutingInfo& routes, |
| 26 ModelSafeGroup group_filter) | 24 ModelSafeGroup group_filter) |
| 27 : resolver_(resolver), | 25 : cryptographer_(cryptographer), |
| 28 cryptographer_(cryptographer), | |
| 29 begin_(begin), | |
| 30 end_(end), | |
| 31 pointer_(begin), | |
| 32 group_filter_(group_filter), | 26 group_filter_(group_filter), |
| 33 progress_(false), | 27 routing_info_(routes) { |
| 34 routing_info_(routes), | |
| 35 application_results_(end - begin) { | |
| 36 size_t item_count = end - begin; | |
| 37 DVLOG(1) << "UpdateApplicator created for " << item_count << " items."; | |
| 38 } | 28 } |
| 39 | 29 |
| 40 UpdateApplicator::~UpdateApplicator() { | 30 UpdateApplicator::~UpdateApplicator() { |
| 41 } | 31 } |
| 42 | 32 |
| 43 // Returns true if there's more to do. | 33 // Attempt to apply all updates, using multiple passes if necessary. |
| 44 bool UpdateApplicator::AttemptOneApplication( | 34 // |
| 45 syncable::WriteTransaction* trans) { | 35 // Some updates must be applied in order. For example, children must be created |
| 46 // If there are no updates left to consider, we're done. | 36 // after their parent folder is created. This function runs an O(n^2) algorithm |
| 47 if (end_ == begin_) | 37 // that will keep trying until there is nothing left to apply, or it stops |
| 48 return false; | 38 // making progress, which would indicate that the hierarchy is invalid. |
| 49 if (pointer_ == end_) { | 39 // |
| 50 if (!progress_) | 40 // The update applicator also has to deal with simple conflicts, which occur |
| 51 return false; | 41 // when an item is modified on both the server and the local model, and |
| 42 // encryption conflicts. There's not much we can do about them here, so we |
| 43 // don't bother re-processing them on subsequent passes. |
| 44 void UpdateApplicator::AttemptApplications( |
| 45 syncable::WriteTransaction* trans, |
| 46 std::vector<int64> to_apply, |
| 47 std::set<syncable::Id>* simple_conflict_ids) { |
| 52 | 48 |
| 53 DVLOG(1) << "UpdateApplicator doing additional pass."; | 49 // This function may be called more than once. If that happens, we will |
| 54 pointer_ = begin_; | 50 // re-visit old encryption and hierarchy conflicts. We clear those counts |
| 55 progress_ = false; | 51 // in advance to avoid double-counting them. |
| 52 encryption_conflicts_ = 0; |
| 53 hierarchy_conflicts_ = 0; |
| 56 | 54 |
| 57 // Clear the tracked failures to avoid double-counting. | 55 DVLOG(1) << "UpdateApplicator running over " << to_apply.size() << " items."; |
| 58 application_results_.ClearConflicts(); | 56 while (!to_apply.empty()) { |
| 57 std::vector<int64> to_reapply; |
| 58 |
| 59 for (UpdateIterator i = to_apply.begin(); i != to_apply.end(); ++i) { |
| 60 syncable::Entry read_entry(trans, syncable::GET_BY_HANDLE, *i); |
| 61 if (SkipUpdate(read_entry)) { |
| 62 continue; |
| 63 } |
| 64 |
| 65 syncable::MutableEntry entry(trans, syncable::GET_BY_HANDLE, *i); |
| 66 UpdateAttemptResponse result = AttemptToUpdateEntry( |
| 67 trans, &entry, cryptographer_); |
| 68 |
| 69 switch (result) { |
| 70 case SUCCESS: |
| 71 updates_applied_++; |
| 72 break; |
| 73 case CONFLICT_SIMPLE: |
| 74 simple_conflict_ids->insert(entry.Get(ID)); |
| 75 break; |
| 76 case CONFLICT_ENCRYPTION: |
| 77 encryption_conflicts_++; |
| 78 break; |
| 79 case CONFLICT_HIERARCHY: |
| 80 // It may be a real hierarchy conflict, but odds are we just tried to |
| 81 // apply it in the wrong order. |
| 82 to_reapply.push_back(*i); |
| 83 break; |
| 84 default: |
| 85 NOTREACHED(); |
| 86 break; |
| 87 } |
| 88 } |
| 89 |
| 90 if (to_reapply.size() == to_apply.size()) { |
| 91 // We made no progress. Must be stubborn hierarchy conflicts. |
| 92 hierarchy_conflicts_ = to_apply.size(); |
| 93 break; |
| 94 } |
| 95 |
| 96 to_apply = to_reapply; |
| 59 } | 97 } |
| 60 | |
| 61 syncable::Entry read_only(trans, syncable::GET_BY_HANDLE, *pointer_); | |
| 62 if (SkipUpdate(read_only)) { | |
| 63 Advance(); | |
| 64 return true; | |
| 65 } | |
| 66 | |
| 67 syncable::MutableEntry entry(trans, syncable::GET_BY_HANDLE, *pointer_); | |
| 68 UpdateAttemptResponse updateResponse = AttemptToUpdateEntry( | |
| 69 trans, &entry, resolver_, cryptographer_); | |
| 70 switch (updateResponse) { | |
| 71 case SUCCESS: | |
| 72 Advance(); | |
| 73 progress_ = true; | |
| 74 application_results_.AddSuccess(entry.Get(syncable::ID)); | |
| 75 break; | |
| 76 case CONFLICT_SIMPLE: | |
| 77 pointer_++; | |
| 78 application_results_.AddSimpleConflict(entry.Get(syncable::ID)); | |
| 79 break; | |
| 80 case CONFLICT_ENCRYPTION: | |
| 81 pointer_++; | |
| 82 application_results_.AddEncryptionConflict(entry.Get(syncable::ID)); | |
| 83 break; | |
| 84 case CONFLICT_HIERARCHY: | |
| 85 pointer_++; | |
| 86 application_results_.AddHierarchyConflict(entry.Get(syncable::ID)); | |
| 87 break; | |
| 88 default: | |
| 89 NOTREACHED(); | |
| 90 break; | |
| 91 } | |
| 92 DVLOG(1) << "Apply Status for " << entry.Get(syncable::META_HANDLE) | |
| 93 << " is " << updateResponse; | |
| 94 | |
| 95 return true; | |
| 96 } | |
| 97 | |
| 98 void UpdateApplicator::Advance() { | |
| 99 --end_; | |
| 100 *pointer_ = *end_; | |
| 101 } | 98 } |
| 102 | 99 |
| 103 bool UpdateApplicator::SkipUpdate(const syncable::Entry& entry) { | 100 bool UpdateApplicator::SkipUpdate(const syncable::Entry& entry) { |
| 104 ModelType type = entry.GetServerModelType(); | 101 ModelType type = entry.GetServerModelType(); |
| 105 ModelSafeGroup g = GetGroupForModelType(type, routing_info_); | 102 ModelSafeGroup g = GetGroupForModelType(type, routing_info_); |
| 106 // The set of updates passed to the UpdateApplicator should already | 103 // The set of updates passed to the UpdateApplicator should already |
| 107 // be group-filtered. | 104 // be group-filtered. |
| 108 if (g != group_filter_) { | 105 if (g != group_filter_) { |
| 109 NOTREACHED(); | 106 NOTREACHED(); |
| 110 return true; | 107 return true; |
| 111 } | 108 } |
| 112 if (g == GROUP_PASSIVE && | 109 if (g == GROUP_PASSIVE && |
| 113 !routing_info_.count(type) && | 110 !routing_info_.count(type) && |
| 114 type != UNSPECIFIED && | 111 type != UNSPECIFIED && |
| 115 type != TOP_LEVEL_FOLDER) { | 112 type != TOP_LEVEL_FOLDER) { |
| 116 DVLOG(1) << "Skipping update application, type not permitted."; | 113 DVLOG(1) << "Skipping update application, type not permitted."; |
| 117 return true; | 114 return true; |
| 118 } | 115 } |
| 119 return false; | 116 return false; |
| 120 } | 117 } |
| 121 | 118 |
| 122 bool UpdateApplicator::AllUpdatesApplied() const { | 119 void UpdateApplicator::SaveStats(sessions::StatusController* status) { |
| 123 return application_results_.no_conflicts() && begin_ == end_; | 120 status->increment_num_encryption_conflicts_by(encryption_conflicts_); |
| 124 } | 121 status->increment_num_hierarchy_conflicts_by(hierarchy_conflicts_); |
| 122 status->increment_num_updates_applied_by(updates_applied_); |
| 125 | 123 |
| 126 void UpdateApplicator::SaveProgressIntoSessionState( | |
| 127 sessions::ConflictProgress* conflict_progress, | |
| 128 sessions::UpdateProgress* update_progress) { | |
| 129 DCHECK(begin_ == end_ || ((pointer_ == end_) && !progress_)) | |
| 130 << "SaveProgress called before updates exhausted."; | |
| 131 | |
| 132 application_results_.SaveProgress(conflict_progress, update_progress); | |
| 133 } | |
| 134 | |
| 135 UpdateApplicator::ResultTracker::ResultTracker(size_t num_results) { | |
| 136 successful_ids_.reserve(num_results); | |
| 137 } | |
| 138 | |
| 139 UpdateApplicator::ResultTracker::~ResultTracker() { | |
| 140 } | |
| 141 | |
| 142 void UpdateApplicator::ResultTracker::AddSimpleConflict(syncable::Id id) { | |
| 143 conflicting_ids_.push_back(id); | |
| 144 } | |
| 145 | |
| 146 void UpdateApplicator::ResultTracker::AddEncryptionConflict(syncable::Id id) { | |
| 147 encryption_conflict_ids_.push_back(id); | |
| 148 } | |
| 149 | |
| 150 void UpdateApplicator::ResultTracker::AddHierarchyConflict(syncable::Id id) { | |
| 151 hierarchy_conflict_ids_.push_back(id); | |
| 152 } | |
| 153 | |
| 154 void UpdateApplicator::ResultTracker::AddSuccess(syncable::Id id) { | |
| 155 successful_ids_.push_back(id); | |
| 156 } | |
| 157 | |
| 158 void UpdateApplicator::ResultTracker::SaveProgress( | |
| 159 sessions::ConflictProgress* conflict_progress, | |
| 160 sessions::UpdateProgress* update_progress) { | |
| 161 vector<syncable::Id>::const_iterator i; | |
| 162 for (i = conflicting_ids_.begin(); i != conflicting_ids_.end(); ++i) { | |
| 163 conflict_progress->AddSimpleConflictingItemById(*i); | |
| 164 update_progress->AddAppliedUpdate(CONFLICT_SIMPLE, *i); | |
| 165 } | |
| 166 for (i = encryption_conflict_ids_.begin(); | |
| 167 i != encryption_conflict_ids_.end(); ++i) { | |
| 168 conflict_progress->AddEncryptionConflictingItemById(*i); | |
| 169 update_progress->AddAppliedUpdate(CONFLICT_ENCRYPTION, *i); | |
| 170 } | |
| 171 for (i = hierarchy_conflict_ids_.begin(); | |
| 172 i != hierarchy_conflict_ids_.end(); ++i) { | |
| 173 conflict_progress->AddHierarchyConflictingItemById(*i); | |
| 174 update_progress->AddAppliedUpdate(CONFLICT_HIERARCHY, *i); | |
| 175 } | |
| 176 for (i = successful_ids_.begin(); i != successful_ids_.end(); ++i) { | |
| 177 conflict_progress->EraseSimpleConflictingItemById(*i); | |
| 178 update_progress->AddAppliedUpdate(SUCCESS, *i); | |
| 179 } | |
| 180 } | |
| 181 | |
| 182 void UpdateApplicator::ResultTracker::ClearConflicts() { | |
| 183 conflicting_ids_.clear(); | |
| 184 encryption_conflict_ids_.clear(); | |
| 185 hierarchy_conflict_ids_.clear(); | |
| 186 } | |
| 187 | |
| 188 bool UpdateApplicator::ResultTracker::no_conflicts() const { | |
| 189 return conflicting_ids_.empty(); | |
| 190 } | 124 } |
| 191 | 125 |
| 192 } // namespace syncer | 126 } // namespace syncer |
| OLD | NEW |