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/conflict_resolver.h" | 5 #include "sync/engine/conflict_resolver.h" |
6 | 6 |
7 #include <list> | 7 #include <list> |
8 #include <set> | 8 #include <set> |
9 #include <string> | 9 #include <string> |
10 | 10 |
(...skipping 77 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
88 // be nice if we could route this back to ModelAssociator code to pick one | 88 // be nice if we could route this back to ModelAssociator code to pick one |
89 // of three options: CLIENT, SERVER, or MERGE. Some datatypes (autofill) | 89 // of three options: CLIENT, SERVER, or MERGE. Some datatypes (autofill) |
90 // are easily mergeable. | 90 // are easily mergeable. |
91 // See http://crbug.com/77339. | 91 // See http://crbug.com/77339. |
92 bool name_matches = entry.Get(syncable::NON_UNIQUE_NAME) == | 92 bool name_matches = entry.Get(syncable::NON_UNIQUE_NAME) == |
93 entry.Get(syncable::SERVER_NON_UNIQUE_NAME); | 93 entry.Get(syncable::SERVER_NON_UNIQUE_NAME); |
94 bool parent_matches = entry.Get(syncable::PARENT_ID) == | 94 bool parent_matches = entry.Get(syncable::PARENT_ID) == |
95 entry.Get(syncable::SERVER_PARENT_ID); | 95 entry.Get(syncable::SERVER_PARENT_ID); |
96 bool entry_deleted = entry.Get(syncable::IS_DEL); | 96 bool entry_deleted = entry.Get(syncable::IS_DEL); |
97 | 97 |
98 // This positional check is meant to be necessary but not sufficient. As a | 98 // The position check might fail spuriously if one of the positions was |
99 // result, it may be false even when the position hasn't changed, possibly | 99 // based on a legacy random suffix, rather than a deterministic one based on |
100 // resulting in unnecessary commits, but if it's true the position has | 100 // originator_cache_guid and originator_item_id. If an item is being |
101 // definitely not changed. The check works by verifying that the prev id | 101 // modified regularly, it shouldn't take long for the suffix and position to |
102 // as calculated from the server position (which will ignore any | 102 // be updated, so such false failures shouldn't be a problem for long. |
103 // unsynced/unapplied predecessors and be root for non-bookmark datatypes) | 103 bool position_matches = parent_matches && |
104 // matches the client prev id. Because we traverse chains of conflicting | 104 entry.Get(syncable::SERVER_UNIQUE_POSITION).Equals( |
105 // items in predecessor -> successor order, we don't need to also verify the | 105 entry.Get(syncable::UNIQUE_POSITION)); |
106 // successor matches (If it's in conflict, we'll verify it next. If it's | |
107 // not, then it should be taken into account already in the | |
108 // ComputePrevIdFromServerPosition calculation). This works even when there | |
109 // are chains of conflicting items. | |
110 // | |
111 // Example: Original sequence was abcde. Server changes to aCDbe, while | |
112 // client changes to aDCbe (C and D are in conflict). Locally, D's prev id | |
113 // is a, while C's prev id is D. On the other hand, the server prev id will | |
114 // ignore unsynced/unapplied items, so D's server prev id will also be a, | |
115 // just like C's. Because we traverse in client predecessor->successor | |
116 // order, we evaluate D first. Since prev id and server id match, we | |
117 // consider the position to have remained the same for D, and will unset | |
118 // it's UNSYNCED/UNAPPLIED bits. When we evaluate C though, we'll see that | |
119 // the prev id is D locally while the server's prev id is a. C will | |
120 // therefore count as a positional conflict (and the local data will be | |
121 // overwritten by the server data typically). The final result will be | |
122 // aCDbe (the same as the server's view). Even though both C and D were | |
123 // modified, only one counted as being in actual conflict and was resolved | |
124 // with local/server wins. | |
125 // | |
126 // In general, when there are chains of positional conflicts, only the first | |
127 // item in chain (based on the clients point of view) will have both its | |
128 // server prev id and local prev id match. For all the rest the server prev | |
129 // id will be the predecessor of the first item in the chain, and therefore | |
130 // not match the local prev id. | |
131 // | |
132 // Similarly, chains of conflicts where the server and client info are the | |
133 // same are supported due to the predecessor->successor ordering. In this | |
134 // case, from the first item onward, we unset the UNSYNCED/UNAPPLIED bits as | |
135 // we decide that nothing changed. The subsequent item's server prev id will | |
136 // accurately match the local prev id because the predecessor is no longer | |
137 // UNSYNCED/UNAPPLIED. | |
138 // TODO(zea): simplify all this once we can directly compare server position | |
139 // to client position. | |
140 syncable::Id server_prev_id = entry.ComputePrevIdFromServerPosition( | |
141 entry.Get(syncable::SERVER_PARENT_ID)); | |
142 bool needs_reinsertion = !parent_matches || | |
143 server_prev_id != entry.Get(syncable::PREV_ID); | |
144 DVLOG_IF(1, needs_reinsertion) << "Insertion needed, server prev id " | |
145 << " is " << server_prev_id << ", local prev id is " | |
146 << entry.Get(syncable::PREV_ID); | |
147 const sync_pb::EntitySpecifics& specifics = | 106 const sync_pb::EntitySpecifics& specifics = |
148 entry.Get(syncable::SPECIFICS); | 107 entry.Get(syncable::SPECIFICS); |
149 const sync_pb::EntitySpecifics& server_specifics = | 108 const sync_pb::EntitySpecifics& server_specifics = |
150 entry.Get(syncable::SERVER_SPECIFICS); | 109 entry.Get(syncable::SERVER_SPECIFICS); |
151 const sync_pb::EntitySpecifics& base_server_specifics = | 110 const sync_pb::EntitySpecifics& base_server_specifics = |
152 entry.Get(syncable::BASE_SERVER_SPECIFICS); | 111 entry.Get(syncable::BASE_SERVER_SPECIFICS); |
153 std::string decrypted_specifics, decrypted_server_specifics; | 112 std::string decrypted_specifics, decrypted_server_specifics; |
154 bool specifics_match = false; | 113 bool specifics_match = false; |
155 bool server_encrypted_with_default_key = false; | 114 bool server_encrypted_with_default_key = false; |
156 if (specifics.has_encrypted()) { | 115 if (specifics.has_encrypted()) { |
(...skipping 25 matching lines...) Expand all Loading... |
182 base_server_specifics.SerializeAsString(); | 141 base_server_specifics.SerializeAsString(); |
183 } else { | 142 } else { |
184 decrypted_base_server_specifics = cryptographer->DecryptToString( | 143 decrypted_base_server_specifics = cryptographer->DecryptToString( |
185 base_server_specifics.encrypted()); | 144 base_server_specifics.encrypted()); |
186 } | 145 } |
187 if (decrypted_server_specifics == decrypted_base_server_specifics) | 146 if (decrypted_server_specifics == decrypted_base_server_specifics) |
188 base_server_specifics_match = true; | 147 base_server_specifics_match = true; |
189 } | 148 } |
190 | 149 |
191 if (!entry_deleted && name_matches && parent_matches && specifics_match && | 150 if (!entry_deleted && name_matches && parent_matches && specifics_match && |
192 !needs_reinsertion) { | 151 position_matches) { |
193 DVLOG(1) << "Resolving simple conflict, everything matches, ignoring " | 152 DVLOG(1) << "Resolving simple conflict, everything matches, ignoring " |
194 << "changes for: " << entry; | 153 << "changes for: " << entry; |
195 conflict_util::IgnoreConflict(&entry); | 154 conflict_util::IgnoreConflict(&entry); |
196 UMA_HISTOGRAM_ENUMERATION("Sync.ResolveSimpleConflict", | 155 UMA_HISTOGRAM_ENUMERATION("Sync.ResolveSimpleConflict", |
197 CHANGES_MATCH, | 156 CHANGES_MATCH, |
198 CONFLICT_RESOLUTION_SIZE); | 157 CONFLICT_RESOLUTION_SIZE); |
199 } else if (base_server_specifics_match) { | 158 } else if (base_server_specifics_match) { |
200 DVLOG(1) << "Resolving simple conflict, ignoring server encryption " | 159 DVLOG(1) << "Resolving simple conflict, ignoring server encryption " |
201 << " changes for: " << entry; | 160 << " changes for: " << entry; |
202 status->increment_num_server_overwrites(); | 161 status->increment_num_server_overwrites(); |
(...skipping 50 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
253 } | 212 } |
254 } | 213 } |
255 | 214 |
256 void ConflictResolver::ResolveConflicts( | 215 void ConflictResolver::ResolveConflicts( |
257 syncable::WriteTransaction* trans, | 216 syncable::WriteTransaction* trans, |
258 const Cryptographer* cryptographer, | 217 const Cryptographer* cryptographer, |
259 const std::set<syncable::Id>& simple_conflict_ids, | 218 const std::set<syncable::Id>& simple_conflict_ids, |
260 sessions::StatusController* status) { | 219 sessions::StatusController* status) { |
261 // Iterate over simple conflict items. | 220 // Iterate over simple conflict items. |
262 set<Id>::const_iterator conflicting_item_it; | 221 set<Id>::const_iterator conflicting_item_it; |
263 set<Id> processed_items; | |
264 for (conflicting_item_it = simple_conflict_ids.begin(); | 222 for (conflicting_item_it = simple_conflict_ids.begin(); |
265 conflicting_item_it != simple_conflict_ids.end(); | 223 conflicting_item_it != simple_conflict_ids.end(); |
266 ++conflicting_item_it) { | 224 ++conflicting_item_it) { |
267 Id id = *conflicting_item_it; | 225 Id id = *conflicting_item_it; |
268 if (processed_items.count(id) > 0) | |
269 continue; | |
270 | 226 |
271 // We don't resolve conflicts for control types here. | 227 // We don't resolve conflicts for control types here. |
272 Entry conflicting_node(trans, syncable::GET_BY_ID, id); | 228 Entry conflicting_node(trans, syncable::GET_BY_ID, id); |
273 CHECK(conflicting_node.good()); | 229 CHECK(conflicting_node.good()); |
274 if (IsControlType( | 230 if (IsControlType( |
275 GetModelTypeFromSpecifics(conflicting_node.Get(syncable::SPECIFICS)))) { | 231 GetModelTypeFromSpecifics(conflicting_node.Get(syncable::SPECIFICS)))) { |
276 continue; | 232 continue; |
277 } | 233 } |
278 | 234 |
279 // We have a simple conflict. In order check if positions have changed, | 235 ProcessSimpleConflict(trans, id, cryptographer, status); |
280 // we need to process conflicting predecessors before successors. Traverse | |
281 // backwards through all continuous conflicting predecessors, building a | |
282 // stack of items to resolve in predecessor->successor order, then process | |
283 // each item individually. | |
284 list<Id> predecessors; | |
285 Id prev_id = id; | |
286 do { | |
287 predecessors.push_back(prev_id); | |
288 Entry entry(trans, syncable::GET_BY_ID, prev_id); | |
289 // Any entry in conflict must be valid. | |
290 CHECK(entry.good()); | |
291 Id new_prev_id = entry.Get(syncable::PREV_ID); | |
292 if (new_prev_id == prev_id) | |
293 break; | |
294 prev_id = new_prev_id; | |
295 } while (processed_items.count(prev_id) == 0 && | |
296 simple_conflict_ids.count(prev_id) > 0); // Excludes root. | |
297 while (!predecessors.empty()) { | |
298 id = predecessors.back(); | |
299 predecessors.pop_back(); | |
300 ProcessSimpleConflict(trans, id, cryptographer, status); | |
301 processed_items.insert(id); | |
302 } | |
303 } | 236 } |
304 return; | 237 return; |
305 } | 238 } |
306 | 239 |
307 } // namespace syncer | 240 } // namespace syncer |
OLD | NEW |