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::FromUpdateResponse(
16 const UpdateResponseData
& data
) {
17 return make_scoped_ptr(new EntityTracker(data
.id
, data
.client_tag_hash
, 0,
18 data
.response_version
));
21 scoped_ptr
<EntityTracker
> EntityTracker::FromCommitRequest(
22 const CommitRequestData
& data
) {
23 return make_scoped_ptr(
24 new EntityTracker(data
.id
, data
.client_tag_hash
, 0, 0));
27 EntityTracker::EntityTracker(const std::string
& id
,
28 const std::string
& client_tag_hash
,
29 int64 highest_commit_response_version
,
30 int64 highest_gu_response_version
)
32 client_tag_hash_(client_tag_hash
),
33 highest_commit_response_version_(highest_commit_response_version
),
34 highest_gu_response_version_(highest_gu_response_version
),
36 base_version_(kUncommittedVersion
) {}
38 EntityTracker::~EntityTracker() {}
40 bool EntityTracker::HasPendingCommit() const {
41 return !!pending_commit_
;
44 void EntityTracker::PrepareCommitProto(sync_pb::SyncEntity
* commit_entity
,
45 int64
* sequence_number
) const {
46 DCHECK(HasPendingCommit());
48 // Set ID if we have a server-assigned ID. Otherwise, it will be up to
49 // our caller to assign a client-unique initial ID.
50 if (base_version_
!= kUncommittedVersion
) {
51 commit_entity
->set_id_string(id_
);
54 commit_entity
->set_client_defined_unique_tag(client_tag_hash_
);
55 commit_entity
->set_version(base_version_
);
56 commit_entity
->set_deleted(pending_commit_
->deleted
);
57 commit_entity
->set_folder(false);
58 commit_entity
->set_name(pending_commit_
->non_unique_name
);
59 if (!pending_commit_
->deleted
) {
60 commit_entity
->set_ctime(syncer::TimeToProtoTime(pending_commit_
->ctime
));
61 commit_entity
->set_mtime(syncer::TimeToProtoTime(pending_commit_
->mtime
));
62 commit_entity
->mutable_specifics()->CopyFrom(pending_commit_
->specifics
);
65 *sequence_number
= sequence_number_
;
68 void EntityTracker::RequestCommit(const CommitRequestData
& data
) {
69 DCHECK_GE(data
.base_version
, base_version_
)
70 << "Base version should never decrease";
72 DCHECK_GE(data
.sequence_number
, sequence_number_
)
73 << "Sequence number should never decrease";
75 // Update our book-keeping counters.
76 base_version_
= data
.base_version
;
77 sequence_number_
= data
.sequence_number
;
79 // Don't commit deletions of server-unknown items.
80 if (data
.deleted
&& !IsServerKnown()) {
85 // We intentionally don't update the id_ here. Good ID values come from the
86 // server and always pass through the sync thread first. There's no way the
87 // model thread could have a better ID value than we do.
89 // This entity is identified by its client tag. That value can never change.
90 DCHECK_EQ(client_tag_hash_
, data
.client_tag_hash
);
91 pending_commit_
.reset(new CommitRequestData(data
));
93 // Do our counter values indicate a conflict? If so, don't commit.
95 // There's no need to inform the model thread of the conflict. The
96 // conflicting update has already been posted to its task runner; it will
97 // figure it out as soon as it runs that task.
99 // Note that this check must be after pending_commit_ is set.
100 if (IsInConflict()) {
101 ClearPendingCommit();
105 // Otherwise, keep the data associated with this pending commit
106 // so it can be committed at the next possible opportunity.
109 void EntityTracker::ReceiveCommitResponse(const std::string
& response_id
,
110 int64 response_version
,
111 int64 sequence_number
) {
112 // Commit responses, especially after the first commit, can update our ID.
115 DCHECK_GT(response_version
, highest_commit_response_version_
)
116 << "Had expected higher response version."
119 // Commits are synchronous, so there's no reason why the sequence numbers
121 DCHECK_EQ(sequence_number_
, sequence_number
)
122 << "Unexpected sequence number mismatch."
125 highest_commit_response_version_
= response_version
;
127 // Because an in-progress commit blocks the sync thread, we can assume that
128 // the item we just committed successfully is exactly the one we have now.
129 // Nothing changed it while the commit was happening. Since we're now in
130 // sync with the server, we can clear the pending commit.
131 ClearPendingCommit();
134 void EntityTracker::ReceiveUpdate(int64 version
) {
135 if (version
<= highest_gu_response_version_
)
138 highest_gu_response_version_
= version
;
140 // Got an applicable update newer than any pending updates. It must be safe
141 // to discard the old pending update, if there was one.
142 ClearPendingUpdate();
144 if (IsInConflict()) {
145 // Incoming update clobbers the pending commit on the sync thread.
146 // The model thread can re-request this commit later if it wants to.
147 ClearPendingCommit();
151 bool EntityTracker::ReceivePendingUpdate(const UpdateResponseData
& data
) {
152 if (data
.response_version
< highest_gu_response_version_
)
155 highest_gu_response_version_
= data
.response_version
;
156 pending_update_
.reset(new UpdateResponseData(data
));
157 ClearPendingCommit();
161 bool EntityTracker::HasPendingUpdate() const {
162 return !!pending_update_
;
165 UpdateResponseData
EntityTracker::GetPendingUpdate() const {
166 return *pending_update_
;
169 void EntityTracker::ClearPendingUpdate() {
170 pending_update_
.reset();
173 bool EntityTracker::IsInConflict() const {
174 if (!HasPendingCommit())
177 if (HasPendingUpdate())
180 if (highest_gu_response_version_
<= highest_commit_response_version_
) {
181 // The most recent server state was created in a commit made by this
182 // client. We're fully up to date, and therefore not in conflict.
185 // The most recent server state was written by someone else.
186 // Did the model thread have the most up to date version when it issued the
188 if (base_version_
>= highest_gu_response_version_
) {
189 return false; // Yes.
196 bool EntityTracker::IsServerKnown() const {
197 return base_version_
!= kUncommittedVersion
;
200 void EntityTracker::ClearPendingCommit() {
201 pending_commit_
.reset();
204 } // namespace syncer