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

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

Issue 10832286: sync: Introduce control data types (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Add ToFullModelTypeSet() function Created 8 years, 3 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 <algorithm> 7 #include <algorithm>
8 #include <list> 8 #include <list>
9 #include <map> 9 #include <map>
10 #include <set> 10 #include <set>
11 11
12 #include "base/location.h" 12 #include "base/location.h"
13 #include "base/metrics/histogram.h" 13 #include "base/metrics/histogram.h"
14 #include "sync/engine/conflict_util.h"
14 #include "sync/engine/syncer.h" 15 #include "sync/engine/syncer.h"
15 #include "sync/engine/syncer_util.h" 16 #include "sync/engine/syncer_util.h"
16 #include "sync/protocol/nigori_specifics.pb.h"
17 #include "sync/sessions/status_controller.h" 17 #include "sync/sessions/status_controller.h"
18 #include "sync/syncable/directory.h" 18 #include "sync/syncable/directory.h"
19 #include "sync/syncable/mutable_entry.h" 19 #include "sync/syncable/mutable_entry.h"
20 #include "sync/syncable/nigori_handler.h" 20 #include "sync/syncable/nigori_handler.h"
21 #include "sync/syncable/write_transaction.h" 21 #include "sync/syncable/write_transaction.h"
22 #include "sync/util/cryptographer.h" 22 #include "sync/util/cryptographer.h"
23 23
24 using std::list; 24 using std::list;
25 using std::map; 25 using std::map;
26 using std::set; 26 using std::set;
(...skipping 14 matching lines...) Expand all
41 const int SYNC_CYCLES_BEFORE_ADMITTING_DEFEAT = 8; 41 const int SYNC_CYCLES_BEFORE_ADMITTING_DEFEAT = 8;
42 42
43 } // namespace 43 } // namespace
44 44
45 ConflictResolver::ConflictResolver() { 45 ConflictResolver::ConflictResolver() {
46 } 46 }
47 47
48 ConflictResolver::~ConflictResolver() { 48 ConflictResolver::~ConflictResolver() {
49 } 49 }
50 50
51 void ConflictResolver::IgnoreLocalChanges(MutableEntry* entry) {
52 // An update matches local actions, merge the changes.
53 // This is a little fishy because we don't actually merge them.
54 // In the future we should do a 3-way merge.
55 // With IS_UNSYNCED false, changes should be merged.
56 entry->Put(syncable::IS_UNSYNCED, false);
57 }
58
59 void ConflictResolver::OverwriteServerChanges(WriteTransaction* trans,
60 MutableEntry * entry) {
61 // This is similar to an overwrite from the old client.
62 // This is equivalent to a scenario where we got the update before we'd
63 // made our local client changes.
64 // TODO(chron): This is really a general property clobber. We clobber
65 // the server side property. Perhaps we should actually do property merging.
66 entry->Put(syncable::BASE_VERSION, entry->Get(syncable::SERVER_VERSION));
67 entry->Put(syncable::IS_UNAPPLIED_UPDATE, false);
68 }
69
70 ConflictResolver::ProcessSimpleConflictResult 51 ConflictResolver::ProcessSimpleConflictResult
71 ConflictResolver::ProcessSimpleConflict(WriteTransaction* trans, 52 ConflictResolver::ProcessSimpleConflict(WriteTransaction* trans,
72 const Id& id, 53 const Id& id,
73 const Cryptographer* cryptographer, 54 const Cryptographer* cryptographer,
74 StatusController* status) { 55 StatusController* status) {
75 MutableEntry entry(trans, syncable::GET_BY_ID, id); 56 MutableEntry entry(trans, syncable::GET_BY_ID, id);
76 // Must be good as the entry won't have been cleaned up. 57 // Must be good as the entry won't have been cleaned up.
77 CHECK(entry.good()); 58 CHECK(entry.good());
78 59
79 // This function can only resolve simple conflicts. Simple conflicts have 60 // This function can only resolve simple conflicts. Simple conflicts have
(...skipping 134 matching lines...) Expand 10 before | Expand all | Expand 10 after
214 decrypted_base_server_specifics = 195 decrypted_base_server_specifics =
215 base_server_specifics.SerializeAsString(); 196 base_server_specifics.SerializeAsString();
216 } else { 197 } else {
217 decrypted_base_server_specifics = cryptographer->DecryptToString( 198 decrypted_base_server_specifics = cryptographer->DecryptToString(
218 base_server_specifics.encrypted()); 199 base_server_specifics.encrypted());
219 } 200 }
220 if (decrypted_server_specifics == decrypted_base_server_specifics) 201 if (decrypted_server_specifics == decrypted_base_server_specifics)
221 base_server_specifics_match = true; 202 base_server_specifics_match = true;
222 } 203 }
223 204
224 // We manually merge nigori data. 205 if (!entry_deleted && name_matches && parent_matches && specifics_match &&
225 if (entry.GetModelType() == NIGORI) { 206 !needs_reinsertion) {
226 // Create a new set of specifics based on the server specifics (which
227 // preserves their encryption keys).
228 sync_pb::EntitySpecifics specifics =
229 entry.Get(syncable::SERVER_SPECIFICS);
230 sync_pb::NigoriSpecifics* server_nigori = specifics.mutable_nigori();
231 // Store the merged set of encrypted types (cryptographer->Update(..) will
232 // have merged the local types already).
233 trans->directory()->GetNigoriHandler()->UpdateNigoriFromEncryptedTypes(
234 server_nigori,
235 trans);
236 // The cryptographer has the both the local and remote encryption keys
237 // (added at cryptographer->Update(..) time).
238 // If the cryptographer is ready, then it already merged both sets of keys
239 // and we can store them back in. In that case, the remote key was already
240 // part of the local keybag, so we preserve the local key as the default
241 // (including whether it's an explicit key).
242 // If the cryptographer is not ready, then the user will have to provide
243 // the passphrase to decrypt the pending keys. When they do so, the
244 // SetDecryptionPassphrase code will act based on whether the server
245 // update has an explicit passphrase or not.
246 // - If the server had an explicit passphrase, that explicit passphrase
247 // will be preserved as the default encryption key.
248 // - If the server did not have an explicit passphrase, we assume the
249 // local passphrase is the most up to date and preserve the local
250 // default encryption key marked as an implicit passphrase.
251 // This works fine except for the case where we had locally set an
252 // explicit passphrase. In that case the nigori node will have the default
253 // key based on the local explicit passphassphrase, but will not have it
254 // marked as explicit. To fix this we'd have to track whether we have a
255 // explicit passphrase or not separate from the nigori, which would
256 // introduce even more complexity, so we leave it up to the user to
257 // reset that passphrase as an explicit one via settings. The goal here
258 // is to ensure both sets of encryption keys are preserved.
259 if (cryptographer->is_ready()) {
260 cryptographer->GetKeys(server_nigori->mutable_encrypted());
261 server_nigori->set_using_explicit_passphrase(
262 entry.Get(syncable::SPECIFICS).nigori().
263 using_explicit_passphrase());
264 }
265 // We deliberately leave the server's device information. This client will
266 // add its own device information on restart.
267 entry.Put(syncable::SPECIFICS, specifics);
268 DVLOG(1) << "Resolving simple conflict, merging nigori nodes: " << entry;
269 status->increment_num_server_overwrites();
270 OverwriteServerChanges(trans, &entry);
271 UMA_HISTOGRAM_ENUMERATION("Sync.ResolveSimpleConflict",
272 NIGORI_MERGE,
273 CONFLICT_RESOLUTION_SIZE);
274 } else if (!entry_deleted && name_matches && parent_matches &&
275 specifics_match && !needs_reinsertion) {
276 DVLOG(1) << "Resolving simple conflict, everything matches, ignoring " 207 DVLOG(1) << "Resolving simple conflict, everything matches, ignoring "
277 << "changes for: " << entry; 208 << "changes for: " << entry;
278 // This unsets both IS_UNSYNCED and IS_UNAPPLIED_UPDATE, and sets the 209 IgnoreConflict(&entry);
279 // BASE_VERSION to match the SERVER_VERSION. If we didn't also unset
280 // IS_UNAPPLIED_UPDATE, then we would lose unsynced positional data from
281 // adjacent entries when the server update gets applied and the item is
282 // re-inserted into the PREV_ID/NEXT_ID linked list. This is primarily
283 // an issue because we commit after applying updates, and is most
284 // commonly seen when positional changes are made while a passphrase
285 // is required (and hence there will be many encryption conflicts).
286 OverwriteServerChanges(trans, &entry);
287 IgnoreLocalChanges(&entry);
288 UMA_HISTOGRAM_ENUMERATION("Sync.ResolveSimpleConflict", 210 UMA_HISTOGRAM_ENUMERATION("Sync.ResolveSimpleConflict",
289 CHANGES_MATCH, 211 CHANGES_MATCH,
290 CONFLICT_RESOLUTION_SIZE); 212 CONFLICT_RESOLUTION_SIZE);
291 } else if (base_server_specifics_match) { 213 } else if (base_server_specifics_match) {
292 DVLOG(1) << "Resolving simple conflict, ignoring server encryption " 214 DVLOG(1) << "Resolving simple conflict, ignoring server encryption "
293 << " changes for: " << entry; 215 << " changes for: " << entry;
294 status->increment_num_server_overwrites(); 216 status->increment_num_server_overwrites();
295 OverwriteServerChanges(trans, &entry); 217 OverwriteServerChanges(&entry);
296 UMA_HISTOGRAM_ENUMERATION("Sync.ResolveSimpleConflict", 218 UMA_HISTOGRAM_ENUMERATION("Sync.ResolveSimpleConflict",
297 IGNORE_ENCRYPTION, 219 IGNORE_ENCRYPTION,
298 CONFLICT_RESOLUTION_SIZE); 220 CONFLICT_RESOLUTION_SIZE);
299 } else if (entry_deleted || !name_matches || !parent_matches) { 221 } else if (entry_deleted || !name_matches || !parent_matches) {
300 OverwriteServerChanges(trans, &entry); 222 OverwriteServerChanges(&entry);
301 status->increment_num_server_overwrites(); 223 status->increment_num_server_overwrites();
302 DVLOG(1) << "Resolving simple conflict, overwriting server changes " 224 DVLOG(1) << "Resolving simple conflict, overwriting server changes "
303 << "for: " << entry; 225 << "for: " << entry;
304 UMA_HISTOGRAM_ENUMERATION("Sync.ResolveSimpleConflict", 226 UMA_HISTOGRAM_ENUMERATION("Sync.ResolveSimpleConflict",
305 OVERWRITE_SERVER, 227 OVERWRITE_SERVER,
306 CONFLICT_RESOLUTION_SIZE); 228 CONFLICT_RESOLUTION_SIZE);
307 } else { 229 } else {
308 DVLOG(1) << "Resolving simple conflict, ignoring local changes for: " 230 DVLOG(1) << "Resolving simple conflict, ignoring local changes for: "
309 << entry; 231 << entry;
310 IgnoreLocalChanges(&entry); 232 IgnoreLocalChanges(&entry);
(...skipping 23 matching lines...) Expand all
334 } 256 }
335 } 257 }
336 258
337 // The entry is deleted on the server but still exists locally. 259 // The entry is deleted on the server but still exists locally.
338 if (!entry.Get(syncable::UNIQUE_CLIENT_TAG).empty()) { 260 if (!entry.Get(syncable::UNIQUE_CLIENT_TAG).empty()) {
339 // If we've got a client-unique tag, we can undelete while retaining 261 // If we've got a client-unique tag, we can undelete while retaining
340 // our present ID. 262 // our present ID.
341 DCHECK_EQ(entry.Get(syncable::SERVER_VERSION), 0) << "For the server to " 263 DCHECK_EQ(entry.Get(syncable::SERVER_VERSION), 0) << "For the server to "
342 "know to re-create, client-tagged items should revert to version 0 " 264 "know to re-create, client-tagged items should revert to version 0 "
343 "when server-deleted."; 265 "when server-deleted.";
344 OverwriteServerChanges(trans, &entry); 266 OverwriteServerChanges(&entry);
345 status->increment_num_server_overwrites(); 267 status->increment_num_server_overwrites();
346 DVLOG(1) << "Resolving simple conflict, undeleting server entry: " 268 DVLOG(1) << "Resolving simple conflict, undeleting server entry: "
347 << entry; 269 << entry;
348 UMA_HISTOGRAM_ENUMERATION("Sync.ResolveSimpleConflict", 270 UMA_HISTOGRAM_ENUMERATION("Sync.ResolveSimpleConflict",
349 OVERWRITE_SERVER, 271 OVERWRITE_SERVER,
350 CONFLICT_RESOLUTION_SIZE); 272 CONFLICT_RESOLUTION_SIZE);
351 // Clobber the versions, just in case the above DCHECK is violated. 273 // Clobber the versions, just in case the above DCHECK is violated.
352 entry.Put(syncable::SERVER_VERSION, 0); 274 entry.Put(syncable::SERVER_VERSION, 0);
353 entry.Put(syncable::BASE_VERSION, 0); 275 entry.Put(syncable::BASE_VERSION, 0);
354 } else { 276 } else {
(...skipping 22 matching lines...) Expand all
377 // Iterate over simple conflict items. 299 // Iterate over simple conflict items.
378 set<Id>::const_iterator conflicting_item_it; 300 set<Id>::const_iterator conflicting_item_it;
379 set<Id> processed_items; 301 set<Id> processed_items;
380 for (conflicting_item_it = progress.SimpleConflictingItemsBegin(); 302 for (conflicting_item_it = progress.SimpleConflictingItemsBegin();
381 conflicting_item_it != progress.SimpleConflictingItemsEnd(); 303 conflicting_item_it != progress.SimpleConflictingItemsEnd();
382 ++conflicting_item_it) { 304 ++conflicting_item_it) {
383 Id id = *conflicting_item_it; 305 Id id = *conflicting_item_it;
384 if (processed_items.count(id) > 0) 306 if (processed_items.count(id) > 0)
385 continue; 307 continue;
386 308
309 // We don't resolve conflicts for control types here.
310 Entry conflicting_node(trans, syncable::GET_BY_ID, id);
311 CHECK(conflicting_node.good());
312 if (IsControlType(
313 GetModelTypeFromSpecifics(conflicting_node.Get(syncable::SPECIFICS)))) {
314 continue;
315 }
316
387 // We have a simple conflict. In order check if positions have changed, 317 // We have a simple conflict. In order check if positions have changed,
388 // we need to process conflicting predecessors before successors. Traverse 318 // we need to process conflicting predecessors before successors. Traverse
389 // backwards through all continuous conflicting predecessors, building a 319 // backwards through all continuous conflicting predecessors, building a
390 // stack of items to resolve in predecessor->successor order, then process 320 // stack of items to resolve in predecessor->successor order, then process
391 // each item individually. 321 // each item individually.
392 list<Id> predecessors; 322 list<Id> predecessors;
393 Id prev_id = id; 323 Id prev_id = id;
394 do { 324 do {
395 predecessors.push_back(prev_id); 325 predecessors.push_back(prev_id);
396 Entry entry(trans, syncable::GET_BY_ID, prev_id); 326 Entry entry(trans, syncable::GET_BY_ID, prev_id);
(...skipping 15 matching lines...) Expand all
412 forward_progress = true; 342 forward_progress = true;
413 break; 343 break;
414 } 344 }
415 processed_items.insert(id); 345 processed_items.insert(id);
416 } 346 }
417 } 347 }
418 return forward_progress; 348 return forward_progress;
419 } 349 }
420 350
421 } // namespace syncer 351 } // namespace syncer
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698