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 scoped_ptr
<EntityTracker
> EntityTracker::FromServerUpdate(
16 const std::string
& id_string
,
17 const std::string
& client_tag_hash
,
18 int64 received_version
) {
19 return make_scoped_ptr(
20 new EntityTracker(id_string
, client_tag_hash
, 0, received_version
));
23 scoped_ptr
<EntityTracker
> EntityTracker::FromCommitRequest(
24 const std::string
& id_string
,
25 const std::string
& client_tag_hash
,
26 int64 sequence_number
,
30 const std::string
& non_unique_name
,
32 const sync_pb::EntitySpecifics
& specifics
) {
33 return make_scoped_ptr(new EntityTracker(
34 id_string
, client_tag_hash
, 0, 0, true, sequence_number
, base_version
,
35 ctime
, mtime
, non_unique_name
, deleted
, specifics
));
38 // Constructor that does not set any pending commit fields.
39 EntityTracker::EntityTracker(const std::string
& id
,
40 const std::string
& client_tag_hash
,
41 int64 highest_commit_response_version
,
42 int64 highest_gu_response_version
)
44 client_tag_hash_(client_tag_hash
),
45 highest_commit_response_version_(highest_commit_response_version
),
46 highest_gu_response_version_(highest_gu_response_version
),
47 is_commit_pending_(false),
53 EntityTracker::EntityTracker(const std::string
& id
,
54 const std::string
& client_tag_hash
,
55 int64 highest_commit_response_version
,
56 int64 highest_gu_response_version
,
57 bool is_commit_pending
,
58 int64 sequence_number
,
62 const std::string
& non_unique_name
,
64 const sync_pb::EntitySpecifics
& specifics
)
66 client_tag_hash_(client_tag_hash
),
67 highest_commit_response_version_(highest_commit_response_version
),
68 highest_gu_response_version_(highest_gu_response_version
),
69 is_commit_pending_(is_commit_pending
),
70 sequence_number_(sequence_number
),
71 base_version_(base_version
),
74 non_unique_name_(non_unique_name
),
76 specifics_(specifics
) {
79 EntityTracker::~EntityTracker() {
82 bool EntityTracker::IsCommitPending() const {
83 return is_commit_pending_
;
86 void EntityTracker::PrepareCommitProto(sync_pb::SyncEntity
* commit_entity
,
87 int64
* sequence_number
) const {
88 // Set ID if we have a server-assigned ID. Otherwise, it will be up to
89 // our caller to assign a client-unique initial ID.
90 if (base_version_
!= syncer_v2::kUncommittedVersion
) {
91 commit_entity
->set_id_string(id_
);
94 commit_entity
->set_client_defined_unique_tag(client_tag_hash_
);
95 commit_entity
->set_version(base_version_
);
96 commit_entity
->set_deleted(deleted_
);
97 commit_entity
->set_folder(false);
98 commit_entity
->set_name(non_unique_name_
);
100 commit_entity
->set_ctime(TimeToProtoTime(ctime_
));
101 commit_entity
->set_mtime(TimeToProtoTime(mtime_
));
102 commit_entity
->mutable_specifics()->CopyFrom(specifics_
);
105 *sequence_number
= sequence_number_
;
108 void EntityTracker::RequestCommit(const std::string
& id
,
109 const std::string
& client_tag_hash
,
110 int64 sequence_number
,
114 const std::string
& non_unique_name
,
116 const sync_pb::EntitySpecifics
& specifics
) {
117 DCHECK_GE(base_version
, base_version_
)
118 << "Base version should never decrease";
120 DCHECK_GE(sequence_number
, sequence_number_
)
121 << "Sequence number should never decrease";
123 // Update our book-keeping counters.
124 base_version_
= base_version
;
125 sequence_number_
= sequence_number
;
127 // Do our counter values indicate a conflict? If so, don't commit.
129 // There's no need to inform the model thread of the conflict. The
130 // conflicting update has already been posted to its task runner; it will
131 // figure it out as soon as it runs that task.
132 is_commit_pending_
= true;
133 if (IsInConflict()) {
134 ClearPendingCommit();
138 // We don't commit deletions of server-unknown items.
139 if (deleted
&& !IsServerKnown()) {
140 ClearPendingCommit();
144 // Otherwise, we should store the data associated with this pending commit
145 // so we're ready to commit at the next possible opportunity.
147 // We intentionally don't update the id_ here. Good ID values come from the
148 // server and always pass through the sync thread first. There's no way the
149 // model thread could have a better ID value than we do.
151 // This entity is identified by its client tag. That value can never change.
152 DCHECK_EQ(client_tag_hash_
, client_tag_hash
);
154 // Set the fields for the pending commit.
157 non_unique_name_
= non_unique_name
;
159 specifics_
= specifics
;
162 void EntityTracker::ReceiveCommitResponse(const std::string
& response_id
,
163 int64 response_version
,
164 int64 sequence_number
) {
165 // Commit responses, especially after the first commit, can update our ID.
168 DCHECK_GT(response_version
, highest_commit_response_version_
)
169 << "Had expected higher response version."
172 // Commits are synchronous, so there's no reason why the sequence numbers
174 DCHECK_EQ(sequence_number_
, sequence_number
)
175 << "Unexpected sequence number mismatch."
178 highest_commit_response_version_
= response_version
;
180 // Because an in-progress commit blocks the sync thread, we can assume that
181 // the item we just committed successfully is exactly the one we have now.
182 // Nothing changed it while the commit was happening. Since we're now in
183 // sync with the server, we can clear the pending commit.
184 ClearPendingCommit();
187 void EntityTracker::ReceiveUpdate(int64 version
) {
188 if (version
<= highest_gu_response_version_
)
191 highest_gu_response_version_
= version
;
193 // Got an applicable update newer than any pending updates. It must be safe
194 // to discard the old pending update, if there was one.
195 ClearPendingUpdate();
197 if (IsInConflict()) {
198 // Incoming update clobbers the pending commit on the sync thread.
199 // The model thread can re-request this commit later if it wants to.
200 ClearPendingCommit();
204 bool EntityTracker::ReceivePendingUpdate(
205 const syncer_v2::UpdateResponseData
& data
) {
206 if (data
.response_version
< highest_gu_response_version_
)
209 highest_gu_response_version_
= data
.response_version
;
210 pending_update_
.reset(new syncer_v2::UpdateResponseData(data
));
211 ClearPendingCommit();
215 bool EntityTracker::HasPendingUpdate() const {
216 return !!pending_update_
;
219 syncer_v2::UpdateResponseData
EntityTracker::GetPendingUpdate() const {
220 return *pending_update_
;
223 void EntityTracker::ClearPendingUpdate() {
224 pending_update_
.reset();
227 bool EntityTracker::IsInConflict() const {
228 if (!is_commit_pending_
)
231 if (HasPendingUpdate())
234 if (highest_gu_response_version_
<= highest_commit_response_version_
) {
235 // The most recent server state was created in a commit made by this
236 // client. We're fully up to date, and therefore not in conflict.
239 // The most recent server state was written by someone else.
240 // Did the model thread have the most up to date version when it issued the
242 if (base_version_
>= highest_gu_response_version_
) {
243 return false; // Yes.
250 bool EntityTracker::IsServerKnown() const {
251 return base_version_
!= syncer_v2::kUncommittedVersion
;
254 void EntityTracker::ClearPendingCommit() {
255 is_commit_pending_
= false;
257 // Clearing the specifics might free up some memory. It can't hurt to try.
261 } // namespace syncer