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.
5 #include "sync/engine/entity_tracker.h"
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"
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
);
22 EntityTracker
* EntityTracker::FromCommitRequest(
23 const std::string
& id_string
,
24 const std::string
& client_tag_hash
,
25 int64 sequence_number
,
29 const std::string
& non_unique_name
,
31 const sync_pb::EntitySpecifics
& specifics
) {
32 return new EntityTracker(id_string
,
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
)
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),
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
,
70 const std::string
& non_unique_name
,
72 const sync_pb::EntitySpecifics
& specifics
)
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
),
82 non_unique_name_(non_unique_name
),
84 specifics_(specifics
) {
87 EntityTracker::~EntityTracker() {
90 bool EntityTracker::IsCommitPending() const {
91 return is_commit_pending_
;
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_
);
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_
);
108 commit_entity
->set_ctime(TimeToProtoTime(ctime_
));
109 commit_entity
->set_mtime(TimeToProtoTime(mtime_
));
110 commit_entity
->mutable_specifics()->CopyFrom(specifics_
);
113 *sequence_number
= sequence_number_
;
116 void EntityTracker::RequestCommit(const std::string
& id
,
117 const std::string
& client_tag_hash
,
118 int64 sequence_number
,
122 const std::string
& non_unique_name
,
124 const sync_pb::EntitySpecifics
& specifics
) {
125 DCHECK_GE(base_version
, base_version_
)
126 << "Base version should never decrease";
128 DCHECK_GE(sequence_number
, sequence_number_
)
129 << "Sequence number should never decrease";
131 // Update our book-keeping counters.
132 base_version_
= base_version
;
133 sequence_number_
= sequence_number
;
135 // Do our counter values indicate a conflict? If so, don't commit.
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();
146 // We don't commit deletions of server-unknown items.
147 if (deleted
&& !IsServerKnown()) {
148 ClearPendingCommit();
152 // Otherwise, we should store the data associated with this pending commit
153 // so we're ready to commit at the next possible opportunity.
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.
159 // This entity is identified by its client tag. That value can never change.
160 DCHECK_EQ(client_tag_hash_
, client_tag_hash
);
162 // Set the fields for the pending commit.
165 non_unique_name_
= non_unique_name
;
167 specifics_
= specifics
;
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.
176 DCHECK_GT(response_version
, highest_commit_response_version_
)
177 << "Had expected higher response version."
180 // Commits are synchronous, so there's no reason why the sequence numbers
182 DCHECK_EQ(sequence_number_
, sequence_number
)
183 << "Unexpected sequence number mismatch."
186 highest_commit_response_version_
= response_version
;
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();
195 void EntityTracker::ReceiveUpdate(int64 version
) {
196 if (version
<= highest_gu_response_version_
)
199 highest_gu_response_version_
= version
;
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();
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();
212 bool EntityTracker::ReceivePendingUpdate(const UpdateResponseData
& data
) {
213 if (data
.response_version
< highest_gu_response_version_
)
216 highest_gu_response_version_
= data
.response_version
;
217 pending_update_
.reset(new UpdateResponseData(data
));
218 ClearPendingCommit();
222 bool EntityTracker::HasPendingUpdate() const {
223 return !!pending_update_
;
226 UpdateResponseData
EntityTracker::GetPendingUpdate() const {
227 return *pending_update_
;
230 void EntityTracker::ClearPendingUpdate() {
231 pending_update_
.reset();
234 bool EntityTracker::IsInConflict() const {
235 if (!is_commit_pending_
)
238 if (HasPendingUpdate())
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.
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
249 if (base_version_
>= highest_gu_response_version_
) {
250 return false; // Yes.
257 bool EntityTracker::IsServerKnown() const {
258 return base_version_
!= kUncommittedVersion
;
261 void EntityTracker::ClearPendingCommit() {
262 is_commit_pending_
= false;
264 // Clearing the specifics might free up some memory. It can't hurt to try.
268 } // namespace syncer