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

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

Issue 11192071: sync: Merge apply updates and resolve conflicts (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Created 8 years, 2 months 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
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 17 matching lines...) Expand all
28 using syncable::Id; 28 using syncable::Id;
29 using syncable::MutableEntry; 29 using syncable::MutableEntry;
30 using syncable::WriteTransaction; 30 using syncable::WriteTransaction;
31 31
32 ConflictResolver::ConflictResolver() { 32 ConflictResolver::ConflictResolver() {
33 } 33 }
34 34
35 ConflictResolver::~ConflictResolver() { 35 ConflictResolver::~ConflictResolver() {
36 } 36 }
37 37
38 ConflictResolver::ProcessSimpleConflictResult 38 void ConflictResolver::ProcessSimpleConflict(WriteTransaction* trans,
39 ConflictResolver::ProcessSimpleConflict(WriteTransaction* trans, 39 const Id& id,
40 const Id& id, 40 const Cryptographer* cryptographer,
41 const Cryptographer* cryptographer, 41 StatusController* status) {
42 StatusController* status) {
43 MutableEntry entry(trans, syncable::GET_BY_ID, id); 42 MutableEntry entry(trans, syncable::GET_BY_ID, id);
44 // Must be good as the entry won't have been cleaned up. 43 // Must be good as the entry won't have been cleaned up.
45 CHECK(entry.good()); 44 CHECK(entry.good());
46 45
47 // This function can only resolve simple conflicts. Simple conflicts have 46 // This function can only resolve simple conflicts. Simple conflicts have
48 // both IS_UNSYNCED and IS_UNAPPLIED_UDPATE set. 47 // both IS_UNSYNCED and IS_UNAPPLIED_UDPATE set.
49 if (!entry.Get(syncable::IS_UNAPPLIED_UPDATE) || 48 if (!entry.Get(syncable::IS_UNAPPLIED_UPDATE) ||
50 !entry.Get(syncable::IS_UNSYNCED)) { 49 !entry.Get(syncable::IS_UNSYNCED)) {
51 // This is very unusual, but it can happen in tests. We may be able to 50 // This is very unusual, but it can happen in tests. We may be able to
52 // assert NOTREACHED() here when those tests are updated. 51 // assert NOTREACHED() here when those tests are updated.
53 return NO_SYNC_PROGRESS; 52 return;
54 } 53 }
55 54
56 if (entry.Get(syncable::IS_DEL) && entry.Get(syncable::SERVER_IS_DEL)) { 55 if (entry.Get(syncable::IS_DEL) && entry.Get(syncable::SERVER_IS_DEL)) {
57 // we've both deleted it, so lets just drop the need to commit/update this 56 // we've both deleted it, so lets just drop the need to commit/update this
58 // entry. 57 // entry.
59 entry.Put(syncable::IS_UNSYNCED, false); 58 entry.Put(syncable::IS_UNSYNCED, false);
60 entry.Put(syncable::IS_UNAPPLIED_UPDATE, false); 59 entry.Put(syncable::IS_UNAPPLIED_UPDATE, false);
61 // we've made changes, but they won't help syncing progress. 60 // we've made changes, but they won't help syncing progress.
62 // METRIC simple conflict resolved by merge. 61 // METRIC simple conflict resolved by merge.
63 return NO_SYNC_PROGRESS; 62 return;
64 } 63 }
65 64
66 // This logic determines "client wins" vs. "server wins" strategy picking. 65 // This logic determines "client wins" vs. "server wins" strategy picking.
67 // By the time we get to this point, we rely on the following to be true: 66 // By the time we get to this point, we rely on the following to be true:
68 // a) We can decrypt both the local and server data (else we'd be in 67 // a) We can decrypt both the local and server data (else we'd be in
69 // conflict encryption and not attempting to resolve). 68 // conflict encryption and not attempting to resolve).
70 // b) All unsynced changes have been re-encrypted with the default key ( 69 // b) All unsynced changes have been re-encrypted with the default key (
71 // occurs either in AttemptToUpdateEntry, SetEncryptionPassphrase, 70 // occurs either in AttemptToUpdateEntry, SetEncryptionPassphrase,
72 // SetDecryptionPassphrase, or RefreshEncryption). 71 // SetDecryptionPassphrase, or RefreshEncryption).
73 // c) Base_server_specifics having a valid datatype means that we received 72 // c) Base_server_specifics having a valid datatype means that we received
(...skipping 125 matching lines...) Expand 10 before | Expand all | Expand 10 after
199 CONFLICT_RESOLUTION_SIZE); 198 CONFLICT_RESOLUTION_SIZE);
200 } else if (base_server_specifics_match) { 199 } else if (base_server_specifics_match) {
201 DVLOG(1) << "Resolving simple conflict, ignoring server encryption " 200 DVLOG(1) << "Resolving simple conflict, ignoring server encryption "
202 << " changes for: " << entry; 201 << " changes for: " << entry;
203 status->increment_num_server_overwrites(); 202 status->increment_num_server_overwrites();
204 OverwriteServerChanges(&entry); 203 OverwriteServerChanges(&entry);
205 UMA_HISTOGRAM_ENUMERATION("Sync.ResolveSimpleConflict", 204 UMA_HISTOGRAM_ENUMERATION("Sync.ResolveSimpleConflict",
206 IGNORE_ENCRYPTION, 205 IGNORE_ENCRYPTION,
207 CONFLICT_RESOLUTION_SIZE); 206 CONFLICT_RESOLUTION_SIZE);
208 } else if (entry_deleted || !name_matches || !parent_matches) { 207 } else if (entry_deleted || !name_matches || !parent_matches) {
208 // NOTE: The update application logic assumes that conflict resolution
209 // will never result in changes to the local hierarchy. The entry_deleted
210 // and !paremt_matches cases here are critical to maintaining that
tim (not reviewing) 2012/10/24 20:29:08 nit - parent_matches
rlarocque 2012/10/24 21:21:22 Good catch. Fixed.
211 // assumption.
209 OverwriteServerChanges(&entry); 212 OverwriteServerChanges(&entry);
210 status->increment_num_server_overwrites(); 213 status->increment_num_server_overwrites();
211 DVLOG(1) << "Resolving simple conflict, overwriting server changes " 214 DVLOG(1) << "Resolving simple conflict, overwriting server changes "
212 << "for: " << entry; 215 << "for: " << entry;
213 UMA_HISTOGRAM_ENUMERATION("Sync.ResolveSimpleConflict", 216 UMA_HISTOGRAM_ENUMERATION("Sync.ResolveSimpleConflict",
214 OVERWRITE_SERVER, 217 OVERWRITE_SERVER,
215 CONFLICT_RESOLUTION_SIZE); 218 CONFLICT_RESOLUTION_SIZE);
216 } else { 219 } else {
217 DVLOG(1) << "Resolving simple conflict, ignoring local changes for: " 220 DVLOG(1) << "Resolving simple conflict, ignoring local changes for: "
218 << entry; 221 << entry;
219 IgnoreLocalChanges(&entry); 222 IgnoreLocalChanges(&entry);
220 status->increment_num_local_overwrites(); 223 status->increment_num_local_overwrites();
221 UMA_HISTOGRAM_ENUMERATION("Sync.ResolveSimpleConflict", 224 UMA_HISTOGRAM_ENUMERATION("Sync.ResolveSimpleConflict",
222 OVERWRITE_LOCAL, 225 OVERWRITE_LOCAL,
223 CONFLICT_RESOLUTION_SIZE); 226 CONFLICT_RESOLUTION_SIZE);
224 } 227 }
225 // Now that we've resolved the conflict, clear the prev server 228 // Now that we've resolved the conflict, clear the prev server
226 // specifics. 229 // specifics.
227 entry.Put(syncable::BASE_SERVER_SPECIFICS, sync_pb::EntitySpecifics()); 230 entry.Put(syncable::BASE_SERVER_SPECIFICS, sync_pb::EntitySpecifics());
228 return SYNC_PROGRESS;
229 } else { // SERVER_IS_DEL is true 231 } else { // SERVER_IS_DEL is true
230 // If a server deleted folder has local contents it should be a hierarchy
231 // conflict. Hierarchy conflicts should not be processed by this function.
232 // We could end up here if a change was made since we last tried to detect
233 // conflicts, which was during update application.
234 if (entry.Get(syncable::IS_DIR)) { 232 if (entry.Get(syncable::IS_DIR)) {
235 Directory::ChildHandles children; 233 Directory::ChildHandles children;
236 trans->directory()->GetChildHandlesById(trans, 234 trans->directory()->GetChildHandlesById(trans,
237 entry.Get(syncable::ID), 235 entry.Get(syncable::ID),
238 &children); 236 &children);
239 if (0 != children.size()) { 237 // If a server deleted folder has local contents it should be a hierarchy
240 DVLOG(1) << "Entry is a server deleted directory with local contents, " 238 // conflict. Hierarchy conflicts should not be processed by this
241 << "should be a hierarchy conflict. (race condition)."; 239 // function.
242 return NO_SYNC_PROGRESS; 240 DCHECK(children.empty());
243 }
244 } 241 }
245 242
246 // The entry is deleted on the server but still exists locally. 243 // The entry is deleted on the server but still exists locally.
247 if (!entry.Get(syncable::UNIQUE_CLIENT_TAG).empty()) { 244 if (!entry.Get(syncable::UNIQUE_CLIENT_TAG).empty()) {
248 // If we've got a client-unique tag, we can undelete while retaining 245 // If we've got a client-unique tag, we can undelete while retaining
249 // our present ID. 246 // our present ID.
250 DCHECK_EQ(entry.Get(syncable::SERVER_VERSION), 0) << "For the server to " 247 DCHECK_EQ(entry.Get(syncable::SERVER_VERSION), 0) << "For the server to "
251 "know to re-create, client-tagged items should revert to version 0 " 248 "know to re-create, client-tagged items should revert to version 0 "
252 "when server-deleted."; 249 "when server-deleted.";
253 OverwriteServerChanges(&entry); 250 OverwriteServerChanges(&entry);
(...skipping 13 matching lines...) Expand all
267 264
268 MutableEntry server_update(trans, syncable::GET_BY_ID, id); 265 MutableEntry server_update(trans, syncable::GET_BY_ID, id);
269 CHECK(server_update.good()); 266 CHECK(server_update.good());
270 CHECK(server_update.Get(syncable::META_HANDLE) != 267 CHECK(server_update.Get(syncable::META_HANDLE) !=
271 entry.Get(syncable::META_HANDLE)) 268 entry.Get(syncable::META_HANDLE))
272 << server_update << entry; 269 << server_update << entry;
273 UMA_HISTOGRAM_ENUMERATION("Sync.ResolveSimpleConflict", 270 UMA_HISTOGRAM_ENUMERATION("Sync.ResolveSimpleConflict",
274 UNDELETE, 271 UNDELETE,
275 CONFLICT_RESOLUTION_SIZE); 272 CONFLICT_RESOLUTION_SIZE);
276 } 273 }
277 return SYNC_PROGRESS;
278 } 274 }
279 } 275 }
280 276
281 bool ConflictResolver::ResolveConflicts( 277 void ConflictResolver::ResolveConflicts(
282 syncable::WriteTransaction* trans, 278 syncable::WriteTransaction* trans,
283 const Cryptographer* cryptographer, 279 const Cryptographer* cryptographer,
284 const std::set<syncable::Id>& simple_conflict_ids, 280 const std::set<syncable::Id>& simple_conflict_ids,
285 sessions::StatusController* status) { 281 sessions::StatusController* status) {
286 bool forward_progress = false;
287 // Iterate over simple conflict items. 282 // Iterate over simple conflict items.
288 set<Id>::const_iterator conflicting_item_it; 283 set<Id>::const_iterator conflicting_item_it;
289 set<Id> processed_items; 284 set<Id> processed_items;
290 for (conflicting_item_it = simple_conflict_ids.begin(); 285 for (conflicting_item_it = simple_conflict_ids.begin();
291 conflicting_item_it != simple_conflict_ids.end(); 286 conflicting_item_it != simple_conflict_ids.end();
292 ++conflicting_item_it) { 287 ++conflicting_item_it) {
293 Id id = *conflicting_item_it; 288 Id id = *conflicting_item_it;
294 if (processed_items.count(id) > 0) 289 if (processed_items.count(id) > 0)
295 continue; 290 continue;
296 291
(...skipping 19 matching lines...) Expand all
316 CHECK(entry.good()); 311 CHECK(entry.good());
317 Id new_prev_id = entry.Get(syncable::PREV_ID); 312 Id new_prev_id = entry.Get(syncable::PREV_ID);
318 if (new_prev_id == prev_id) 313 if (new_prev_id == prev_id)
319 break; 314 break;
320 prev_id = new_prev_id; 315 prev_id = new_prev_id;
321 } while (processed_items.count(prev_id) == 0 && 316 } while (processed_items.count(prev_id) == 0 &&
322 simple_conflict_ids.count(prev_id) > 0); // Excludes root. 317 simple_conflict_ids.count(prev_id) > 0); // Excludes root.
323 while (!predecessors.empty()) { 318 while (!predecessors.empty()) {
324 id = predecessors.back(); 319 id = predecessors.back();
325 predecessors.pop_back(); 320 predecessors.pop_back();
326 switch (ProcessSimpleConflict(trans, id, cryptographer, status)) { 321 ProcessSimpleConflict(trans, id, cryptographer, status);
327 case NO_SYNC_PROGRESS:
328 break;
329 case SYNC_PROGRESS:
330 forward_progress = true;
331 break;
332 }
333 processed_items.insert(id); 322 processed_items.insert(id);
334 } 323 }
335 } 324 }
336 return forward_progress; 325 return;
337 } 326 }
338 327
339 } // namespace syncer 328 } // namespace syncer
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698