Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(1)

Side by Side Diff: sync/engine/conflict_resolver.cc

Issue 11636006: WIP: The Bookmark Position Megapatch (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Various updates, including switch suffix to unique_client_tag style Created 8 years ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « sync/engine/build_commit_command_unittest.cc ('k') | sync/engine/get_commit_ids_command.cc » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
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
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
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
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
OLDNEW
« no previous file with comments | « sync/engine/build_commit_command_unittest.cc ('k') | sync/engine/get_commit_ids_command.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698