1 // Copyright 2014 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 "sync/engine/sync_thread_sync_entity.h" 6 7 #include "base/logging.h" 8 #include "sync/engine/non_blocking_sync_common.h" 9 #include "sync/internal_api/public/base/model_type.h" 10 #include "sync/syncable/syncable_util.h" 11 #include "sync/util/time.h" 12 13 namespace syncer { 14 15 SyncThreadSyncEntity* SyncThreadSyncEntity::FromServerUpdate( 16 const std::string& id_string, 17 const std::string& client_tag_hash, 18 int64 received_version) { 19 return new SyncThreadSyncEntity( 20 id_string, client_tag_hash, 0, received_version); 21 } 22 23 SyncThreadSyncEntity* SyncThreadSyncEntity::FromCommitRequest( 24 const std::string& id_string, 25 const std::string& client_tag_hash, 26 int64 sequence_number, 27 int64 base_version, 28 base::Time ctime, 29 base::Time mtime, 30 const std::string& non_unique_name, 31 bool deleted, 32 const sync_pb::EntitySpecifics& specifics) { 33 return new SyncThreadSyncEntity(id_string, 34 client_tag_hash, 35 0, 36 0, 37 true, 38 sequence_number, 39 base_version, 40 ctime, 41 mtime, 42 non_unique_name, 43 deleted, 44 specifics); 45 } 46 47 // Constructor that does not set any pending commit fields. 48 SyncThreadSyncEntity::SyncThreadSyncEntity( 49 const std::string& id, 50 const std::string& client_tag_hash, 51 int64 highest_commit_response_version, 52 int64 highest_gu_response_version) 53 : id_(id), 54 client_tag_hash_(client_tag_hash), 55 highest_commit_response_version_(highest_commit_response_version), 56 highest_gu_response_version_(highest_gu_response_version), 57 is_commit_pending_(false), 58 sequence_number_(0), 59 base_version_(0), 60 deleted_(false) { 61 } 62 63 SyncThreadSyncEntity::SyncThreadSyncEntity( 64 const std::string& id, 65 const std::string& client_tag_hash, 66 int64 highest_commit_response_version, 67 int64 highest_gu_response_version, 68 bool is_commit_pending, 69 int64 sequence_number, 70 int64 base_version, 71 base::Time ctime, 72 base::Time mtime, 73 const std::string& non_unique_name, 74 bool deleted, 75 const sync_pb::EntitySpecifics& specifics) 76 : id_(id), 77 client_tag_hash_(client_tag_hash), 78 highest_commit_response_version_(highest_commit_response_version), 79 highest_gu_response_version_(highest_gu_response_version), 80 is_commit_pending_(is_commit_pending), 81 sequence_number_(sequence_number), 82 base_version_(base_version), 83 ctime_(ctime), 84 mtime_(mtime), 85 non_unique_name_(non_unique_name), 86 deleted_(deleted), 87 specifics_(specifics) { 88 } 89 90 SyncThreadSyncEntity::~SyncThreadSyncEntity() { 91 } 92 93 bool SyncThreadSyncEntity::IsCommitPending() const { 94 return is_commit_pending_; 95 } 96 97 void SyncThreadSyncEntity::PrepareCommitProto( 98 sync_pb::SyncEntity* commit_entity, 99 int64* sequence_number) const { 100 // Set ID if we have a server-assigned ID. Otherwise, it will be up to 101 // our caller to assign a client-unique initial ID. 102 if (base_version_ != kUncommittedVersion) { 103 commit_entity->set_id_string(id_); 104 } 105 106 commit_entity->set_client_defined_unique_tag(client_tag_hash_); 107 commit_entity->set_version(base_version_); 108 commit_entity->set_deleted(deleted_); 109 commit_entity->set_folder(false); 110 commit_entity->set_name(non_unique_name_); 111 if (!deleted_) { 112 commit_entity->set_ctime(TimeToProtoTime(ctime_)); 113 commit_entity->set_mtime(TimeToProtoTime(mtime_)); 114 commit_entity->mutable_specifics()->CopyFrom(specifics_); 115 } 116 117 *sequence_number = sequence_number_; 118 } 119 120 void SyncThreadSyncEntity::RequestCommit( 121 const std::string& id, 122 const std::string& client_tag_hash, 123 int64 sequence_number, 124 int64 base_version, 125 base::Time ctime, 126 base::Time mtime, 127 const std::string& non_unique_name, 128 bool deleted, 129 const sync_pb::EntitySpecifics& specifics) { 130 DCHECK_GE(base_version, base_version_) 131 << "Base version should never decrease"; 132 133 DCHECK_GE(sequence_number, sequence_number_) 134 << "Sequence number should never decrease"; 135 136 // Update our book-keeping counters. 137 base_version_ = base_version; 138 sequence_number_ = sequence_number; 139 140 // Do our counter values indicate a conflict? If so, don't commit. 141 // 142 // There's no need to inform the model thread of the conflict. The 143 // conflicting update has already been posted to its task runner; it will 144 // figure it out as soon as it runs that task. 145 is_commit_pending_ = true; 146 if (IsInConflict()) { 147 ClearPendingCommit(); 148 return; 149 } 150 151 // We don't commit deletions of server-unknown items. 152 if (deleted && !IsServerKnown()) { 153 ClearPendingCommit(); 154 return; 155 } 156 157 // Otherwise, we should store the data associated with this pending commit 158 // so we're ready to commit at the next possible opportunity. 159 160 // We intentionally don't update the id_ here. Good ID values come from the 161 // server and always pass through the sync thread first. There's no way the 162 // model thread could have a better ID value than we do. 163 164 // This entity is identified by its client tag. That value can never change. 165 DCHECK_EQ(client_tag_hash_, client_tag_hash); 166 167 // Set the fields for the pending commit. 168 ctime_ = ctime; 169 mtime_ = mtime; 170 non_unique_name_ = non_unique_name; 171 deleted_ = deleted; 172 specifics_ = specifics; 173 } 174 175 void SyncThreadSyncEntity::ReceiveCommitResponse(const std::string& response_id, 176 int64 response_version, 177 int64 sequence_number) { 178 // Commit responses, especially after the first commit, can update our ID. 179 id_ = response_id; 180 181 DCHECK_GT(response_version, highest_commit_response_version_) 182 << "Had expected higher response version." 183 << " id: " << id_; 184 185 // Commits are synchronous, so there's no reason why the sequence numbers 186 // wouldn't match. 187 DCHECK_EQ(sequence_number_, sequence_number) 188 << "Unexpected sequence number mismatch." 189 << " id: " << id_; 190 191 highest_commit_response_version_ = response_version; 192 193 // Because an in-progress commit blocks the sync thread, we can assume that 194 // the item we just committed successfully is exactly the one we have now. 195 // Nothing changed it while the commit was happening. Since we're now in 196 // sync with the server, we can clear the pending commit. 197 ClearPendingCommit(); 198 } 199 200 void SyncThreadSyncEntity::ReceiveUpdate(int64 version) { 201 highest_gu_response_version_ = 202 std::max(highest_gu_response_version_, version); 203 204 if (IsInConflict()) { 205 // Incoming update clobbers the pending commit on the sync thread. 206 // The model thread can re-request this commit later if it wants to. 207 ClearPendingCommit(); 208 } 209 } 210 211 bool SyncThreadSyncEntity::IsInConflict() const { 212 if (!is_commit_pending_) 213 return false; 214 215 if (highest_gu_response_version_ <= highest_commit_response_version_) { 216 // The most recent server state was created in a commit made by this 217 // client. We're fully up to date, and therefore not in conflict. 218 return false; 219 } else { 220 // The most recent server state was written by someone else. 221 // Did the model thread have the most up to date version when it issued the 222 // commit request? 223 if (base_version_ >= highest_gu_response_version_) { 224 return false; // Yes. 225 } else { 226 return true; // No. 227 } 228 } 229 } 230 231 bool SyncThreadSyncEntity::IsServerKnown() const { 232 return base_version_ != kUncommittedVersion; 233 } 234 235 void SyncThreadSyncEntity::ClearPendingCommit() { 236 is_commit_pending_ = false; 237 238 // Clearing the specifics might free up some memory. It can't hurt to try. 239 specifics_.Clear(); 240 } 241 242 } // namespace syncer 243