1 // Copyright (c) 2006-2009 The Chromium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #include "chrome/browser/sync/engine/process_updates_command.h" 6 7 #include <vector> 8 9 #include "base/basictypes.h" 10 #include "chrome/browser/sync/engine/syncer.h" 11 #include "chrome/browser/sync/engine/syncer_proto_util.h" 12 #include "chrome/browser/sync/engine/syncer_util.h" 13 #include "chrome/browser/sync/engine/syncproto.h" 14 #include "chrome/browser/sync/sessions/sync_session.h" 15 #include "chrome/browser/sync/syncable/directory_manager.h" 16 #include "chrome/browser/sync/syncable/syncable.h" 17 18 using std::vector; 19 20 namespace browser_sync { 21 22 using sessions::SyncSession; 23 using sessions::StatusController; 24 25 ProcessUpdatesCommand::ProcessUpdatesCommand() {} 26 ProcessUpdatesCommand::~ProcessUpdatesCommand() {} 27 28 bool ProcessUpdatesCommand::ModelNeutralExecuteImpl(SyncSession* session) { 29 const GetUpdatesResponse& updates = 30 session->status_controller()->updates_response().get_updates(); 31 const int update_count = updates.entries_size(); 32 33 // Don't bother processing updates if there were none. 34 return update_count != 0; 35 } 36 37 void ProcessUpdatesCommand::ModelChangingExecuteImpl(SyncSession* session) { 38 syncable::ScopedDirLookup dir(session->context()->directory_manager(), 39 session->context()->account_name()); 40 if (!dir.good()) { 41 LOG(ERROR) << "Scoped dir lookup failed!"; 42 return; 43 } 44 45 StatusController* status = session->status_controller(); 46 47 const sessions::UpdateProgress& progress(status->update_progress()); 48 vector<sessions::VerifiedUpdate>::const_iterator it; 49 for (it = progress.VerifiedUpdatesBegin(); 50 it != progress.VerifiedUpdatesEnd(); 51 ++it) { 52 const sync_pb::SyncEntity& update = it->second; 53 54 if (it->first != VERIFY_SUCCESS && it->first != VERIFY_UNDELETE) 55 continue; 56 switch (ProcessUpdate(dir, update)) { 57 case SUCCESS_PROCESSED: 58 case SUCCESS_STORED: 59 break; 60 default: 61 NOTREACHED(); 62 break; 63 } 64 } 65 66 status->set_num_consecutive_errors(0); 67 68 // TODO(nick): The following line makes no sense to me. 69 status->set_syncing(true); 70 return; 71 } 72 73 namespace { 74 // Returns true if the entry is still ok to process. 75 bool ReverifyEntry(syncable::WriteTransaction* trans, const SyncEntity& entry, 76 syncable::MutableEntry* same_id) { 77 78 const bool deleted = entry.has_deleted() && entry.deleted(); 79 const bool is_directory = entry.IsFolder(); 80 const syncable::ModelType model_type = entry.GetModelType(); 81 82 return VERIFY_SUCCESS == SyncerUtil::VerifyUpdateConsistency(trans, 83 entry, 84 same_id, 85 deleted, 86 is_directory, 87 model_type); 88 } 89 } // namespace 90 91 // Process a single update. Will avoid touching global state. 92 ServerUpdateProcessingResult ProcessUpdatesCommand::ProcessUpdate( 93 const syncable::ScopedDirLookup& dir, 94 const sync_pb::SyncEntity& proto_update) { 95 96 const SyncEntity& update = *static_cast<const SyncEntity*>(&proto_update); 97 using namespace syncable; 98 syncable::Id server_id = update.id(); 99 const std::string name = SyncerProtoUtil::NameFromSyncEntity(update); 100 101 WriteTransaction trans(dir, SYNCER, __FILE__, __LINE__); 102 103 // Look to see if there's a local item that should recieve this update, 104 // maybe due to a duplicate client tag or a lost commit response. 105 syncable::Id local_id = SyncerUtil::FindLocalIdToUpdate(&trans, update); 106 107 // FindLocalEntryToUpdate has veto power. 108 if (local_id.IsNull()) { 109 return SUCCESS_PROCESSED; // The entry has become irrelevant. 110 } 111 112 SyncerUtil::CreateNewEntry(&trans, local_id); 113 114 // We take a two step approach. First we store the entries data in the 115 // server fields of a local entry and then move the data to the local fields 116 MutableEntry target_entry(&trans, GET_BY_ID, local_id); 117 118 // We need to run the Verify checks again; the world could have changed 119 // since VerifyUpdatesCommand. 120 if (!ReverifyEntry(&trans, update, &target_entry)) { 121 return SUCCESS_PROCESSED; // The entry has become irrelevant. 122 } 123 124 // If we're repurposing an existing local entry with a new server ID, 125 // change the ID now, after we're sure that the update can succeed. 126 if (local_id != server_id) { 127 DCHECK(!update.deleted()); 128 SyncerUtil::ChangeEntryIDAndUpdateChildren(&trans, &target_entry, 129 server_id); 130 // When IDs change, versions become irrelevant. Forcing BASE_VERSION 131 // to zero would ensure that this update gets applied, but would indicate 132 // creation or undeletion if it were committed that way. Instead, prefer 133 // forcing BASE_VERSION to entry.version() while also forcing 134 // IS_UNAPPLIED_UPDATE to true. If the item is UNSYNCED, it's committable 135 // from the new state; it may commit before the conflict resolver gets 136 // a crack at it. 137 if (target_entry.Get(IS_UNSYNCED) || target_entry.Get(BASE_VERSION) > 0) { 138 // If either of these conditions are met, then we can expect valid client 139 // fields for this entry. When BASE_VERSION is positive, consistency is 140 // enforced on the client fields at update-application time. Otherwise, 141 // we leave the BASE_VERSION field alone; it'll get updated the first time 142 // we successfully apply this update. 143 target_entry.Put(BASE_VERSION, update.version()); 144 } 145 // Force application of this update, no matter what. 146 target_entry.Put(IS_UNAPPLIED_UPDATE, true); 147 } 148 149 SyncerUtil::UpdateServerFieldsFromUpdate(&target_entry, update, name); 150 151 if (target_entry.Get(SERVER_VERSION) == target_entry.Get(BASE_VERSION) && 152 !target_entry.Get(IS_UNSYNCED) && 153 !target_entry.Get(IS_UNAPPLIED_UPDATE)) { 154 // If these don't match, it means that we have a different view of the 155 // truth from other clients. That's a sync bug, though we may be able 156 // to recover the next time this item commits. 157 LOG_IF(ERROR, !SyncerUtil::ServerAndLocalEntriesMatch(&target_entry)) 158 << target_entry; 159 } 160 return SUCCESS_PROCESSED; 161 } 162 163 } // namespace browser_sync 164