1ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen// Copyright (c) 2011 The Chromium Authors. All rights reserved. 2c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// Use of this source code is governed by a BSD-style license that can be 3c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// found in the LICENSE file. 4c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 5c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "chrome/browser/sync/engine/build_and_process_conflict_sets_command.h" 6c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 7ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen#include <set> 8c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include <string> 9c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include <sstream> 10c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include <vector> 11c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 12c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "base/basictypes.h" 13c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "base/format_macros.h" 14c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "chrome/browser/sync/engine/syncer_util.h" 15c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "chrome/browser/sync/engine/update_applicator.h" 16c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "chrome/browser/sync/sessions/sync_session.h" 17c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "chrome/browser/sync/syncable/directory_manager.h" 18c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 19c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochnamespace browser_sync { 20c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 21c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochusing sessions::ConflictProgress; 22c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochusing sessions::StatusController; 23c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochusing sessions::SyncSession; 24c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochusing sessions::UpdateProgress; 25c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochusing std::set; 26c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochusing std::string; 27c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochusing std::vector; 28c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 29c407dc5cd9bdc5668497f21b26b09d988ab439deBen MurdochBuildAndProcessConflictSetsCommand::BuildAndProcessConflictSetsCommand() {} 30c407dc5cd9bdc5668497f21b26b09d988ab439deBen MurdochBuildAndProcessConflictSetsCommand::~BuildAndProcessConflictSetsCommand() {} 31c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 32c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid BuildAndProcessConflictSetsCommand::ModelChangingExecuteImpl( 33c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch SyncSession* session) { 34c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch session->status_controller()->update_conflict_sets_built( 35c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch BuildAndProcessConflictSets(session)); 36c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} 37c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 38c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochbool BuildAndProcessConflictSetsCommand::BuildAndProcessConflictSets( 39c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch SyncSession* session) { 40c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch syncable::ScopedDirLookup dir(session->context()->directory_manager(), 41c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch session->context()->account_name()); 42c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (!dir.good()) 43c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch return false; 44c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch bool had_single_direction_sets = false; 45c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch { // Scope for transaction. 46c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch syncable::WriteTransaction trans(dir, syncable::SYNCER, __FILE__, __LINE__); 47c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch BuildConflictSets(&trans, 48c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch session->status_controller()->mutable_conflict_progress()); 49c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch had_single_direction_sets = ProcessSingleDirectionConflictSets(&trans, 50c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch session->context()->resolver(), 51ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen session->context()->directory_manager()->GetCryptographer(&trans), 52c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch session->status_controller(), session->routing_info()); 53c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // We applied some updates transactionally, lets try syncing again. 54c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (had_single_direction_sets) 55c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch return true; 56c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 57c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch return false; 58c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} 59c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 60c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochbool BuildAndProcessConflictSetsCommand::ProcessSingleDirectionConflictSets( 61c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch syncable::WriteTransaction* trans, ConflictResolver* resolver, 62c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch Cryptographer* cryptographer, StatusController* status, 63c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch const ModelSafeRoutingInfo& routes) { 64c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch bool rv = false; 65c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch set<ConflictSet*>::const_iterator all_sets_iterator; 66c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch for (all_sets_iterator = status->conflict_progress().ConflictSetsBegin(); 67c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch all_sets_iterator != status->conflict_progress().ConflictSetsEnd();) { 68c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch const ConflictSet* conflict_set = *all_sets_iterator; 69ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen CHECK_GE(conflict_set->size(), 2U); 70c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // We scan the set to see if it consists of changes of only one type. 71c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch ConflictSet::const_iterator i; 72c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch size_t unsynced_count = 0, unapplied_count = 0; 73c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch for (i = conflict_set->begin(); i != conflict_set->end(); ++i) { 74c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch syncable::Entry entry(trans, syncable::GET_BY_ID, *i); 75c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch CHECK(entry.good()); 76c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (entry.Get(syncable::IS_UNSYNCED)) 77c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch unsynced_count++; 78c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (entry.Get(syncable::IS_UNAPPLIED_UPDATE)) 79c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch unapplied_count++; 80c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 81c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (conflict_set->size() == unsynced_count && 0 == unapplied_count) { 82731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick VLOG(1) << "Skipped transactional commit attempt."; 83c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } else if (conflict_set->size() == unapplied_count && 0 == unsynced_count && 84c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch ApplyUpdatesTransactionally(trans, conflict_set, resolver, 85c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch cryptographer, routes, status)) { 86c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch rv = true; 87c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 88c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch ++all_sets_iterator; 89c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 90c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch return rv; 91c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} 92c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 93c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochnamespace { 94c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 95c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid StoreLocalDataForUpdateRollback(syncable::Entry* entry, 96c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch syncable::EntryKernel* backup) { 97c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch CHECK(!entry->Get(syncable::IS_UNSYNCED)) << " Storing Rollback data for " 98c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch "entry that's unsynced." << *entry; 99c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch CHECK(entry->Get(syncable::IS_UNAPPLIED_UPDATE)) << " Storing Rollback data " 100c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch "for entry that's not an unapplied update." << *entry; 101c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch *backup = entry->GetKernelCopy(); 102c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} 103c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 104c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 105c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochbool RollbackEntry(syncable::WriteTransaction* trans, 106c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch syncable::EntryKernel* backup) { 107c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch syncable::MutableEntry entry(trans, syncable::GET_BY_HANDLE, 108c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch backup->ref(syncable::META_HANDLE)); 109c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch CHECK(entry.good()); 110c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 111c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (!entry.Put(syncable::IS_DEL, backup->ref(syncable::IS_DEL))) 112c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch return false; 113c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 114c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch entry.Put(syncable::NON_UNIQUE_NAME, backup->ref(syncable::NON_UNIQUE_NAME)); 115c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch entry.Put(syncable::PARENT_ID, backup->ref(syncable::PARENT_ID)); 116c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 117c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (!backup->ref(syncable::IS_DEL)) { 118c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (!entry.PutPredecessor(backup->ref(syncable::PREV_ID))) 119c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch return false; 120c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 121c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 122c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (backup->ref(syncable::PREV_ID) != entry.Get(syncable::PREV_ID)) 123c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch return false; 124c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 125c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch entry.Put(syncable::CTIME, backup->ref(syncable::CTIME)); 126c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch entry.Put(syncable::MTIME, backup->ref(syncable::MTIME)); 127c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch entry.Put(syncable::BASE_VERSION, backup->ref(syncable::BASE_VERSION)); 128c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch entry.Put(syncable::IS_DIR, backup->ref(syncable::IS_DIR)); 129c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch entry.Put(syncable::IS_DEL, backup->ref(syncable::IS_DEL)); 130c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch entry.Put(syncable::ID, backup->ref(syncable::ID)); 131c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch entry.Put(syncable::IS_UNAPPLIED_UPDATE, 132c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch backup->ref(syncable::IS_UNAPPLIED_UPDATE)); 133c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch return true; 134c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} 135c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 136c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid PlaceEntriesAtRoot(syncable::WriteTransaction* trans, 137c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch const vector<syncable::Id>* ids) { 138c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch vector<syncable::Id>::const_iterator it; 139c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch for (it = ids->begin(); it != ids->end(); ++it) { 140c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch syncable::MutableEntry entry(trans, syncable::GET_BY_ID, *it); 141c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch entry.Put(syncable::PARENT_ID, trans->root_id()); 142c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 143c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 144c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 145c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} // namespace 146c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 147c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochbool BuildAndProcessConflictSetsCommand::ApplyUpdatesTransactionally( 148c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch syncable::WriteTransaction* trans, 149c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch const vector<syncable::Id>* const update_set, 150c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch ConflictResolver* resolver, 151c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch Cryptographer* cryptographer, 152c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch const ModelSafeRoutingInfo& routes, 153c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch StatusController* status) { 154c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // The handles in the |update_set| order. 155c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch vector<int64> handles; 156c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 157c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // Holds the same Ids as update_set, but sorted so that runs of adjacent 158c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // nodes appear in order. 159c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch vector<syncable::Id> rollback_ids; 160c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch rollback_ids.reserve(update_set->size()); 161c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 162c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // Tracks what's added to |rollback_ids|. 163c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch syncable::MetahandleSet rollback_ids_inserted_items; 164c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch vector<syncable::Id>::const_iterator it; 165c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 166c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // 1. Build |rollback_ids| in the order required for successful rollback. 167c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // Specifically, for positions to come out right, restoring an item 168c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // requires that its predecessor in the sibling order is properly 169c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // restored first. 170c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // 2. Build |handles|, the list of handles for ApplyUpdates. 171c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch for (it = update_set->begin(); it != update_set->end(); ++it) { 172c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch syncable::Entry entry(trans, syncable::GET_BY_ID, *it); 173c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch SyncerUtil::AddPredecessorsThenItem(trans, &entry, 174c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch syncable::IS_UNAPPLIED_UPDATE, &rollback_ids_inserted_items, 175c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch &rollback_ids); 176c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch handles.push_back(entry.Get(syncable::META_HANDLE)); 177c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 178c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch DCHECK_EQ(rollback_ids.size(), update_set->size()); 179c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch DCHECK_EQ(rollback_ids_inserted_items.size(), update_set->size()); 180c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 181c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // 3. Store the information needed to rollback if the transaction fails. 182c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // Do this before modifying anything to keep the next/prev values intact. 183c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch vector<syncable::EntryKernel> rollback_data(rollback_ids.size()); 184c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch for (size_t i = 0; i < rollback_ids.size(); ++i) { 185c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch syncable::Entry entry(trans, syncable::GET_BY_ID, rollback_ids[i]); 186c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch StoreLocalDataForUpdateRollback(&entry, &rollback_data[i]); 187c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 188c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 189c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // 4. Use the preparer to move things to an initial starting state where 190c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // nothing in the set is a child of anything else. If 191c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // we've correctly calculated the set, the server tree is valid and no 192c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // changes have occurred locally we should be able to apply updates from this 193c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // state. 194c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch PlaceEntriesAtRoot(trans, update_set); 195c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 196c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // 5. Use the usual apply updates from the special start state we've just 197c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // prepared. 198c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch UpdateApplicator applicator(resolver, cryptographer, 199c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch handles.begin(), handles.end(), 200c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch routes, status->group_restriction()); 201c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch while (applicator.AttemptOneApplication(trans)) { 202c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // Keep going till all updates are applied. 203c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 204c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (!applicator.AllUpdatesApplied()) { 205c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch LOG(ERROR) << "Transactional Apply Failed, Rolling back."; 206c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // We have to move entries into the temp dir again. e.g. if a swap was in a 207c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // set with other failing updates, the swap may have gone through, meaning 208c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // the roll back needs to be transactional. But as we're going to a known 209c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // good state we should always succeed. 210c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch PlaceEntriesAtRoot(trans, update_set); 211c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 212c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // Rollback all entries. 213c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch for (size_t i = 0; i < rollback_data.size(); ++i) { 214c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch CHECK(RollbackEntry(trans, &rollback_data[i])); 215c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 216c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch return false; // Don't save progress -- we just undid it. 217c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 218c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch applicator.SaveProgressIntoSessionState(status->mutable_conflict_progress(), 219c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch status->mutable_update_progress()); 220c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch return true; 221c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} 222c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 223c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid BuildAndProcessConflictSetsCommand::BuildConflictSets( 224c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch syncable::BaseTransaction* trans, 225c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch ConflictProgress* conflict_progress) { 226c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch conflict_progress->CleanupSets(); 227c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch set<syncable::Id>::iterator i = conflict_progress->ConflictingItemsBegin(); 228c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch while (i != conflict_progress->ConflictingItemsEnd()) { 229c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch syncable::Entry entry(trans, syncable::GET_BY_ID, *i); 2303345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick if (!entry.good() || 2313345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick (!entry.Get(syncable::IS_UNSYNCED) && 2323345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick !entry.Get(syncable::IS_UNAPPLIED_UPDATE))) { 233c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // This can happen very rarely. It means we had a simply conflicting item 2343345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick // that randomly committed; its ID could have changed during the commit. 2353345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick // We drop the entry as it's no longer conflicting. 236c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch conflict_progress->EraseConflictingItemById(*(i++)); 237c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch continue; 238c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 239c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (entry.ExistsOnClientBecauseNameIsNonEmpty() && 240c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch (entry.Get(syncable::IS_DEL) || entry.Get(syncable::SERVER_IS_DEL))) { 241c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // If we're deleted on client or server we can't be in a complex set. 242c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch ++i; 243c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch continue; 244c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 245c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch bool new_parent = 246c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch entry.Get(syncable::PARENT_ID) != entry.Get(syncable::SERVER_PARENT_ID); 247c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (new_parent) 248c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch MergeSetsForIntroducedLoops(trans, &entry, conflict_progress); 249c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch MergeSetsForNonEmptyDirectories(trans, &entry, conflict_progress); 250c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch ++i; 251c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 252c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} 253c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 254c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid BuildAndProcessConflictSetsCommand::MergeSetsForIntroducedLoops( 255c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch syncable::BaseTransaction* trans, syncable::Entry* entry, 256c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch ConflictProgress* conflict_progress) { 257c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // This code crawls up from the item in question until it gets to the root 258c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // or itself. If it gets to the root it does nothing. If it finds a loop all 259c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // moved unsynced entries in the list of crawled entries have their sets 260c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // merged with the entry. 261c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // TODO(sync): Build test cases to cover this function when the argument list 262c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // has settled. 263c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch syncable::Id parent_id = entry->Get(syncable::SERVER_PARENT_ID); 264c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch syncable::Entry parent(trans, syncable::GET_BY_ID, parent_id); 265c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (!parent.good()) { 266c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch return; 267c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 268c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // Don't check for loop if the server parent is deleted. 269c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (parent.Get(syncable::IS_DEL)) 270c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch return; 271c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch vector<syncable::Id> conflicting_entries; 272c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch while (!parent_id.IsRoot()) { 273c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch syncable::Entry parent(trans, syncable::GET_BY_ID, parent_id); 274c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (!parent.good()) { 275731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick VLOG(1) << "Bad parent in loop check, skipping. Bad parent id: " 276731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick << parent_id << " entry: " << *entry; 277c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch return; 278c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 279c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (parent.Get(syncable::IS_UNSYNCED) && 280c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch entry->Get(syncable::PARENT_ID) != 281c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch entry->Get(syncable::SERVER_PARENT_ID)) 282c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch conflicting_entries.push_back(parent_id); 283c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch parent_id = parent.Get(syncable::PARENT_ID); 284c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (parent_id == entry->Get(syncable::ID)) 285c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch break; 286c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 287c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (parent_id.IsRoot()) 288c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch return; 289c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch for (size_t i = 0; i < conflicting_entries.size(); i++) { 290c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch conflict_progress->MergeSets(entry->Get(syncable::ID), 291c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch conflicting_entries[i]); 292c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 293c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} 294c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 295c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochnamespace { 296c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 297c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochclass ServerDeletedPathChecker { 298c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch public: 299c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch static bool CausingConflict(const syncable::Entry& e, 300c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch const syncable::Entry& log_entry) { 301c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch CHECK(e.good()) << "Missing parent in path of: " << log_entry; 302c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (e.Get(syncable::IS_UNAPPLIED_UPDATE) && 303c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch e.Get(syncable::SERVER_IS_DEL)) { 304c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch CHECK(!e.Get(syncable::IS_DEL)) << " Inconsistency in local tree. " 305c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch "syncable::Entry: " << e << " Leaf: " << log_entry; 306c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch return true; 307c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } else { 308c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch CHECK(!e.Get(syncable::IS_DEL)) << " Deleted entry has children. " 309c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch "syncable::Entry: " << e << " Leaf: " << log_entry; 310c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch return false; 311c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 312c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 313c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 314c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // returns 0 if we should stop investigating the path. 315c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch static syncable::Id GetAndExamineParent(syncable::BaseTransaction* trans, 316ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen const syncable::Id& id, 317ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen const syncable::Id& check_id, 318c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch const syncable::Entry& log_entry) { 319c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch syncable::Entry parent(trans, syncable::GET_BY_ID, id); 320c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch CHECK(parent.good()) << "Tree inconsitency, missing id" << id << " " 321c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch << log_entry; 322c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch syncable::Id parent_id = parent.Get(syncable::PARENT_ID); 323c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch CHECK(parent_id != check_id) << "Loop in dir tree! " 324c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch << log_entry << " " << parent; 325c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch return parent_id; 326c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 327c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}; 328c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 329c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochclass LocallyDeletedPathChecker { 330c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch public: 331c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch static bool CausingConflict(const syncable::Entry& e, 332c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch const syncable::Entry& log_entry) { 333c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch return e.good() && e.Get(syncable::IS_DEL) && e.Get(syncable::IS_UNSYNCED); 334c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 335c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 336c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // returns 0 if we should stop investigating the path. 337c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch static syncable::Id GetAndExamineParent(syncable::BaseTransaction* trans, 338ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen const syncable::Id& id, 339ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen const syncable::Id& check_id, 340c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch const syncable::Entry& log_entry) { 341c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch syncable::Entry parent(trans, syncable::GET_BY_ID, id); 342c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (!parent.good()) 343c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch return syncable::kNullId; 344c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch syncable::Id parent_id = parent.Get(syncable::PARENT_ID); 345c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (parent_id == check_id) 346c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch return syncable::kNullId; 347c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch return parent_id; 348c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 349c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}; 350c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 351c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochtemplate <typename Checker> 352c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid CrawlDeletedTreeMergingSets(syncable::BaseTransaction* trans, 353c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch const syncable::Entry& entry, 354c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch ConflictProgress* conflict_progress, 355c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch Checker checker) { 356c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch syncable::Id parent_id = entry.Get(syncable::PARENT_ID); 357c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch syncable::Id double_step_parent_id = parent_id; 358c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // This block builds sets where we've got an entry in a directory the server 359c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // wants to delete. 360c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // 361c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // Here we're walking up the tree to find all entries that the pass checks 362c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // deleted. We can be extremely strict here as anything unexpected means 363c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // invariants in the local hierarchy have been broken. 364c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch while (!parent_id.IsRoot()) { 365c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (!double_step_parent_id.IsRoot()) { 366c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // Checks to ensure we don't loop. 367c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch double_step_parent_id = checker.GetAndExamineParent( 368c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch trans, double_step_parent_id, parent_id, entry); 369c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch double_step_parent_id = checker.GetAndExamineParent( 370c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch trans, double_step_parent_id, parent_id, entry); 371c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 372c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch syncable::Entry parent(trans, syncable::GET_BY_ID, parent_id); 373c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (checker.CausingConflict(parent, entry)) { 374c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch conflict_progress->MergeSets(entry.Get(syncable::ID), 375c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch parent.Get(syncable::ID)); 376c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } else { 377c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch break; 378c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 379c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch parent_id = parent.Get(syncable::PARENT_ID); 380c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 381c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} 382c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 383c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} // namespace 384c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 385c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid BuildAndProcessConflictSetsCommand::MergeSetsForNonEmptyDirectories( 386c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch syncable::BaseTransaction* trans, syncable::Entry* entry, 387c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch ConflictProgress* conflict_progress) { 388c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (entry->Get(syncable::IS_UNSYNCED) && !entry->Get(syncable::IS_DEL)) { 389c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch ServerDeletedPathChecker checker; 390c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch CrawlDeletedTreeMergingSets(trans, *entry, conflict_progress, checker); 391c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 392c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (entry->Get(syncable::IS_UNAPPLIED_UPDATE) && 393c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch !entry->Get(syncable::SERVER_IS_DEL)) { 394c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch syncable::Entry parent(trans, syncable::GET_BY_ID, 395c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch entry->Get(syncable::SERVER_PARENT_ID)); 396c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch syncable::Id parent_id = entry->Get(syncable::SERVER_PARENT_ID); 397c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (!parent.good()) 398c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch return; 399c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch LocallyDeletedPathChecker checker; 400c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (!checker.CausingConflict(parent, *entry)) 401c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch return; 402c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch conflict_progress->MergeSets(entry->Get(syncable::ID), 403c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch parent.Get(syncable::ID)); 404c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch CrawlDeletedTreeMergingSets(trans, parent, conflict_progress, checker); 405c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 406c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} 407c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 408c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} // namespace browser_sync 409