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
FromServerUpdate(const std::string & id_string,const std::string & client_tag_hash,int64 received_version)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
FromCommitRequest(const std::string & id_string,const std::string & client_tag_hash,int64 sequence_number,int64 base_version,base::Time ctime,base::Time mtime,const std::string & non_unique_name,bool deleted,const sync_pb::EntitySpecifics & specifics)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.
EntityTracker(const std::string & id,const std::string & client_tag_hash,int64 highest_commit_response_version,int64 highest_gu_response_version)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
EntityTracker(const std::string & id,const std::string & client_tag_hash,int64 highest_commit_response_version,int64 highest_gu_response_version,bool is_commit_pending,int64 sequence_number,int64 base_version,base::Time ctime,base::Time mtime,const std::string & non_unique_name,bool deleted,const sync_pb::EntitySpecifics & specifics)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
~EntityTracker()87 EntityTracker::~EntityTracker() {
88 }
89
IsCommitPending() const90 bool EntityTracker::IsCommitPending() const {
91 return is_commit_pending_;
92 }
93
PrepareCommitProto(sync_pb::SyncEntity * commit_entity,int64 * sequence_number) const94 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
RequestCommit(const std::string & id,const std::string & client_tag_hash,int64 sequence_number,int64 base_version,base::Time ctime,base::Time mtime,const std::string & non_unique_name,bool deleted,const sync_pb::EntitySpecifics & specifics)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
ReceiveCommitResponse(const std::string & response_id,int64 response_version,int64 sequence_number)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
ReceiveUpdate(int64 version)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
ReceivePendingUpdate(const UpdateResponseData & data)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
HasPendingUpdate() const222 bool EntityTracker::HasPendingUpdate() const {
223 return !!pending_update_;
224 }
225
GetPendingUpdate() const226 UpdateResponseData EntityTracker::GetPendingUpdate() const {
227 return *pending_update_;
228 }
229
ClearPendingUpdate()230 void EntityTracker::ClearPendingUpdate() {
231 pending_update_.reset();
232 }
233
IsInConflict() const234 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
IsServerKnown() const257 bool EntityTracker::IsServerKnown() const {
258 return base_version_ != kUncommittedVersion;
259 }
260
ClearPendingCommit()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