OLD | NEW |
(Empty) | |
| 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 |
OLD | NEW |