• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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