OLD | NEW |
| (Empty) |
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/internal_api/public/processor_entity_tracker.h" | |
6 | |
7 #include <stdint.h> | |
8 | |
9 #include "base/base64.h" | |
10 #include "base/sha1.h" | |
11 #include "base/time/time.h" | |
12 #include "sync/internal_api/public/non_blocking_sync_common.h" | |
13 #include "sync/syncable/syncable_util.h" | |
14 #include "sync/util/time.h" | |
15 | |
16 namespace syncer_v2 { | |
17 | |
18 namespace { | |
19 | |
20 void HashSpecifics(const sync_pb::EntitySpecifics& specifics, | |
21 std::string* hash) { | |
22 DCHECK_GT(specifics.ByteSize(), 0); | |
23 base::Base64Encode(base::SHA1HashString(specifics.SerializeAsString()), hash); | |
24 } | |
25 | |
26 } // namespace | |
27 | |
28 std::unique_ptr<ProcessorEntityTracker> ProcessorEntityTracker::CreateNew( | |
29 const std::string& client_tag, | |
30 const std::string& client_tag_hash, | |
31 const std::string& id, | |
32 base::Time creation_time) { | |
33 // Initialize metadata | |
34 sync_pb::EntityMetadata metadata; | |
35 metadata.set_client_tag_hash(client_tag_hash); | |
36 if (!id.empty()) | |
37 metadata.set_server_id(id); | |
38 metadata.set_sequence_number(0); | |
39 metadata.set_acked_sequence_number(0); | |
40 metadata.set_server_version(kUncommittedVersion); | |
41 metadata.set_creation_time(syncer::TimeToProtoTime(creation_time)); | |
42 | |
43 return std::unique_ptr<ProcessorEntityTracker>( | |
44 new ProcessorEntityTracker(client_tag, &metadata)); | |
45 } | |
46 | |
47 std::unique_ptr<ProcessorEntityTracker> | |
48 ProcessorEntityTracker::CreateFromMetadata(const std::string& client_tag, | |
49 sync_pb::EntityMetadata* metadata) { | |
50 return std::unique_ptr<ProcessorEntityTracker>( | |
51 new ProcessorEntityTracker(client_tag, metadata)); | |
52 } | |
53 | |
54 ProcessorEntityTracker::ProcessorEntityTracker( | |
55 const std::string& client_tag, | |
56 sync_pb::EntityMetadata* metadata) | |
57 : client_tag_(client_tag), | |
58 commit_requested_sequence_number_(metadata->acked_sequence_number()) { | |
59 DCHECK(metadata->has_client_tag_hash()); | |
60 DCHECK(metadata->has_creation_time()); | |
61 metadata_.Swap(metadata); | |
62 } | |
63 | |
64 ProcessorEntityTracker::~ProcessorEntityTracker() {} | |
65 | |
66 void ProcessorEntityTracker::CacheCommitData(EntityData* data) { | |
67 DCHECK(data); | |
68 if (data->client_tag_hash.empty()) { | |
69 data->client_tag_hash = metadata_.client_tag_hash(); | |
70 } | |
71 CacheCommitData(data->PassToPtr()); | |
72 } | |
73 | |
74 void ProcessorEntityTracker::CacheCommitData(const EntityDataPtr& data_ptr) { | |
75 DCHECK(RequiresCommitData()); | |
76 commit_data_ = data_ptr; | |
77 DCHECK(HasCommitData()); | |
78 } | |
79 | |
80 bool ProcessorEntityTracker::HasCommitData() const { | |
81 return !commit_data_->client_tag_hash.empty(); | |
82 } | |
83 | |
84 bool ProcessorEntityTracker::MatchesData(const EntityData& data) const { | |
85 return metadata_.is_deleted() ? data.is_deleted() : | |
86 MatchesSpecificsHash(data.specifics); | |
87 } | |
88 | |
89 bool ProcessorEntityTracker::MatchesBaseData(const EntityData& data) const { | |
90 DCHECK(IsUnsynced()); | |
91 if (data.is_deleted() || metadata_.base_specifics_hash().empty()) { | |
92 return false; | |
93 } | |
94 std::string hash; | |
95 HashSpecifics(data.specifics, &hash); | |
96 return hash == metadata_.base_specifics_hash(); | |
97 } | |
98 | |
99 bool ProcessorEntityTracker::IsUnsynced() const { | |
100 return metadata_.sequence_number() > metadata_.acked_sequence_number(); | |
101 } | |
102 | |
103 bool ProcessorEntityTracker::RequiresCommitRequest() const { | |
104 return metadata_.sequence_number() > commit_requested_sequence_number_; | |
105 } | |
106 | |
107 bool ProcessorEntityTracker::RequiresCommitData() const { | |
108 return RequiresCommitRequest() && !HasCommitData() && !metadata_.is_deleted(); | |
109 } | |
110 | |
111 bool ProcessorEntityTracker::CanClearMetadata() const { | |
112 return metadata_.is_deleted() && !IsUnsynced(); | |
113 } | |
114 | |
115 bool ProcessorEntityTracker::UpdateIsReflection(int64_t update_version) const { | |
116 return metadata_.server_version() >= update_version; | |
117 } | |
118 | |
119 void ProcessorEntityTracker::RecordIgnoredUpdate( | |
120 const UpdateResponseData& update) { | |
121 DCHECK(metadata_.server_id().empty() || | |
122 metadata_.server_id() == update.entity->id); | |
123 metadata_.set_server_id(update.entity->id); | |
124 metadata_.set_server_version(update.response_version); | |
125 // Either these already matched, acked was just bumped to squash a pending | |
126 // commit and this should follow, or the pending commit needs to be requeued. | |
127 commit_requested_sequence_number_ = metadata_.acked_sequence_number(); | |
128 } | |
129 | |
130 void ProcessorEntityTracker::RecordAcceptedUpdate( | |
131 const UpdateResponseData& update) { | |
132 DCHECK(!IsUnsynced()); | |
133 RecordIgnoredUpdate(update); | |
134 metadata_.set_is_deleted(update.entity->is_deleted()); | |
135 metadata_.set_modification_time( | |
136 syncer::TimeToProtoTime(update.entity->modification_time)); | |
137 UpdateSpecificsHash(update.entity->specifics); | |
138 } | |
139 | |
140 void ProcessorEntityTracker::RecordForcedUpdate( | |
141 const UpdateResponseData& update) { | |
142 DCHECK(IsUnsynced()); | |
143 // There was a conflict and the server just won it. Explicitly ack all | |
144 // pending commits so they are never enqueued again. | |
145 metadata_.set_acked_sequence_number(metadata_.sequence_number()); | |
146 commit_data_.reset(); | |
147 RecordAcceptedUpdate(update); | |
148 } | |
149 | |
150 void ProcessorEntityTracker::MakeLocalChange(std::unique_ptr<EntityData> data) { | |
151 DCHECK(!metadata_.client_tag_hash().empty()); | |
152 DCHECK_EQ(metadata_.client_tag_hash(), data->client_tag_hash); | |
153 | |
154 if (data->modification_time.is_null()) { | |
155 data->modification_time = base::Time::Now(); | |
156 } | |
157 | |
158 IncrementSequenceNumber(); | |
159 UpdateSpecificsHash(data->specifics); | |
160 metadata_.set_modification_time( | |
161 syncer::TimeToProtoTime(data->modification_time)); | |
162 metadata_.set_is_deleted(false); | |
163 | |
164 data->id = metadata_.server_id(); | |
165 data->creation_time = syncer::ProtoTimeToTime(metadata_.creation_time()); | |
166 commit_data_.reset(); | |
167 CacheCommitData(data.get()); | |
168 } | |
169 | |
170 void ProcessorEntityTracker::Delete() { | |
171 IncrementSequenceNumber(); | |
172 metadata_.set_modification_time(syncer::TimeToProtoTime(base::Time::Now())); | |
173 metadata_.set_is_deleted(true); | |
174 metadata_.clear_specifics_hash(); | |
175 // Clear any cached pending commit data. | |
176 commit_data_.reset(); | |
177 } | |
178 | |
179 void ProcessorEntityTracker::InitializeCommitRequestData( | |
180 CommitRequestData* request) { | |
181 if (!metadata_.is_deleted()) { | |
182 DCHECK(HasCommitData()); | |
183 DCHECK_EQ(commit_data_->client_tag_hash, metadata_.client_tag_hash()); | |
184 request->entity = commit_data_; | |
185 } else { | |
186 // Make an EntityData with empty specifics to indicate deletion. This is | |
187 // done lazily here to simplify loading a pending deletion on startup. | |
188 EntityData data; | |
189 data.client_tag_hash = metadata_.client_tag_hash(); | |
190 data.id = metadata_.server_id(); | |
191 data.creation_time = syncer::ProtoTimeToTime(metadata_.creation_time()); | |
192 data.modification_time = | |
193 syncer::ProtoTimeToTime(metadata_.modification_time()); | |
194 request->entity = data.PassToPtr(); | |
195 } | |
196 | |
197 request->sequence_number = metadata_.sequence_number(); | |
198 request->base_version = metadata_.server_version(); | |
199 request->specifics_hash = metadata_.specifics_hash(); | |
200 commit_requested_sequence_number_ = metadata_.sequence_number(); | |
201 } | |
202 | |
203 void ProcessorEntityTracker::ReceiveCommitResponse( | |
204 const CommitResponseData& data) { | |
205 DCHECK_EQ(metadata_.client_tag_hash(), data.client_tag_hash); | |
206 DCHECK_GT(data.sequence_number, metadata_.acked_sequence_number()); | |
207 DCHECK_GT(data.response_version, metadata_.server_version()); | |
208 | |
209 // The server can assign us a new ID in a commit response. | |
210 metadata_.set_server_id(data.id); | |
211 metadata_.set_acked_sequence_number(data.sequence_number); | |
212 metadata_.set_server_version(data.response_version); | |
213 if (!IsUnsynced()) { | |
214 // Clear pending commit data if there hasn't been another commit request | |
215 // since the one that is currently getting acked. | |
216 commit_data_.reset(); | |
217 metadata_.clear_base_specifics_hash(); | |
218 } else { | |
219 metadata_.set_base_specifics_hash(data.specifics_hash); | |
220 } | |
221 } | |
222 | |
223 void ProcessorEntityTracker::ClearTransientSyncState() { | |
224 // If we have any unacknowledged commit requests outstanding, they've been | |
225 // dropped and we should forget about them. | |
226 commit_requested_sequence_number_ = metadata_.acked_sequence_number(); | |
227 } | |
228 | |
229 void ProcessorEntityTracker::IncrementSequenceNumber() { | |
230 DCHECK(metadata_.has_sequence_number()); | |
231 if (!IsUnsynced()) { | |
232 // Update the base specifics hash if this entity wasn't already out of sync. | |
233 metadata_.set_base_specifics_hash(metadata_.specifics_hash()); | |
234 } | |
235 metadata_.set_sequence_number(metadata_.sequence_number() + 1); | |
236 } | |
237 | |
238 bool ProcessorEntityTracker::MatchesSpecificsHash( | |
239 const sync_pb::EntitySpecifics& specifics) const { | |
240 DCHECK(!metadata_.is_deleted()); | |
241 std::string hash; | |
242 HashSpecifics(specifics, &hash); | |
243 return hash == metadata_.specifics_hash(); | |
244 } | |
245 | |
246 void ProcessorEntityTracker::UpdateSpecificsHash( | |
247 const sync_pb::EntitySpecifics& specifics) { | |
248 if (specifics.ByteSize() > 0) { | |
249 HashSpecifics(specifics, metadata_.mutable_specifics_hash()); | |
250 } else { | |
251 metadata_.clear_specifics_hash(); | |
252 } | |
253 } | |
254 | |
255 } // namespace syncer_v2 | |
OLD | NEW |