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