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

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

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

Powered by Google App Engine
This is Rietveld 408576698