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/engine/directory_update_handler.h" | |
6 | |
7 #include <stdint.h> | |
8 | |
9 #include <vector> | |
10 | |
11 #include "sync/engine/conflict_resolver.h" | |
12 #include "sync/engine/process_updates_util.h" | |
13 #include "sync/engine/update_applicator.h" | |
14 #include "sync/sessions/directory_type_debug_info_emitter.h" | |
15 #include "sync/syncable/directory.h" | |
16 #include "sync/syncable/model_neutral_mutable_entry.h" | |
17 #include "sync/syncable/syncable_changes_version.h" | |
18 #include "sync/syncable/syncable_model_neutral_write_transaction.h" | |
19 #include "sync/syncable/syncable_write_transaction.h" | |
20 #include "sync/util/data_type_histogram.h" | |
21 | |
22 namespace syncer { | |
23 | |
24 using syncable::SYNCER; | |
25 | |
26 DirectoryUpdateHandler::DirectoryUpdateHandler( | |
27 syncable::Directory* dir, | |
28 ModelType type, | |
29 scoped_refptr<ModelSafeWorker> worker, | |
30 DirectoryTypeDebugInfoEmitter* debug_info_emitter) | |
31 : dir_(dir), | |
32 type_(type), | |
33 worker_(worker), | |
34 debug_info_emitter_(debug_info_emitter) {} | |
35 | |
36 DirectoryUpdateHandler::~DirectoryUpdateHandler() {} | |
37 | |
38 bool DirectoryUpdateHandler::IsInitialSyncEnded() const { | |
39 return dir_->InitialSyncEndedForType(type_); | |
40 } | |
41 | |
42 void DirectoryUpdateHandler::GetDownloadProgress( | |
43 sync_pb::DataTypeProgressMarker* progress_marker) const { | |
44 dir_->GetDownloadProgress(type_, progress_marker); | |
45 } | |
46 | |
47 void DirectoryUpdateHandler::GetDataTypeContext( | |
48 sync_pb::DataTypeContext* context) const { | |
49 syncable::ModelNeutralWriteTransaction trans(FROM_HERE, SYNCER, dir_); | |
50 dir_->GetDataTypeContext(&trans, type_, context); | |
51 } | |
52 | |
53 SyncerError DirectoryUpdateHandler::ProcessGetUpdatesResponse( | |
54 const sync_pb::DataTypeProgressMarker& progress_marker, | |
55 const sync_pb::DataTypeContext& mutated_context, | |
56 const SyncEntityList& applicable_updates, | |
57 sessions::StatusController* status) { | |
58 syncable::ModelNeutralWriteTransaction trans(FROM_HERE, SYNCER, dir_); | |
59 if (progress_marker.ByteSize() > 0) { | |
60 SyncRecordDatatypeBin("DataUse.Sync.ProgressMarker.Bytes", | |
61 ModelTypeToHistogramInt(type_), | |
62 progress_marker.ByteSize()); | |
63 } | |
64 if (mutated_context.has_context()) { | |
65 sync_pb::DataTypeContext local_context; | |
66 dir_->GetDataTypeContext(&trans, type_, &local_context); | |
67 | |
68 // Only update the local context if it is still relevant. If the local | |
69 // version is higher, it means a local change happened while the mutation | |
70 // was in flight, and the local context takes priority. | |
71 if (mutated_context.version() >= local_context.version() && | |
72 local_context.context() != mutated_context.context()) { | |
73 dir_->SetDataTypeContext(&trans, type_, mutated_context); | |
74 // TODO(zea): trigger the datatype's UpdateDataTypeContext method. | |
75 } else if (mutated_context.version() < local_context.version()) { | |
76 // A GetUpdates using the old context was in progress when the context was | |
77 // set. Fail this get updates cycle, to force a retry. | |
78 DVLOG(1) << "GU Context conflict detected, forcing GU retry."; | |
79 debug_info_emitter_->EmitUpdateCountersUpdate(); | |
80 return DATATYPE_TRIGGERED_RETRY; | |
81 } | |
82 } | |
83 | |
84 // Auto-create permanent folder for the type if the progress marker | |
85 // changes from empty to non-empty. | |
86 if (IsTypeWithClientGeneratedRoot(type_) && | |
87 dir_->HasEmptyDownloadProgress(type_) && | |
88 IsValidProgressMarker(progress_marker)) { | |
89 CreateTypeRoot(&trans); | |
90 } | |
91 | |
92 UpdateSyncEntities(&trans, applicable_updates, status); | |
93 | |
94 if (IsValidProgressMarker(progress_marker)) { | |
95 ExpireEntriesIfNeeded(&trans, progress_marker); | |
96 UpdateProgressMarker(progress_marker); | |
97 } | |
98 | |
99 debug_info_emitter_->EmitUpdateCountersUpdate(); | |
100 return SYNCER_OK; | |
101 } | |
102 | |
103 void DirectoryUpdateHandler::CreateTypeRoot( | |
104 syncable::ModelNeutralWriteTransaction* trans) { | |
105 syncable::ModelNeutralMutableEntry entry( | |
106 trans, syncable::CREATE_NEW_TYPE_ROOT, type_); | |
107 if (!entry.good()) { | |
108 // This will fail only if matching entry already exists, for example | |
109 // if the type gets disabled and its progress marker gets cleared, | |
110 // then the type gets re-enabled again. | |
111 DVLOG(1) << "Type root folder " << ModelTypeToRootTag(type_) | |
112 << " already exists."; | |
113 return; | |
114 } | |
115 | |
116 entry.PutServerIsDir(true); | |
117 entry.PutUniqueServerTag(ModelTypeToRootTag(type_)); | |
118 } | |
119 | |
120 void DirectoryUpdateHandler::ApplyUpdates(sessions::StatusController* status) { | |
121 if (IsApplyUpdatesRequired()) { | |
122 // This will invoke handlers that belong to the model and its thread, so we | |
123 // switch to the appropriate thread before we start this work. | |
124 WorkCallback c = | |
125 base::Bind(&DirectoryUpdateHandler::ApplyUpdatesImpl, | |
126 // We wait until the callback is executed. We can safely use | |
127 // Unretained. | |
128 base::Unretained(this), base::Unretained(status)); | |
129 worker_->DoWorkAndWaitUntilDone(c); | |
130 | |
131 debug_info_emitter_->EmitUpdateCountersUpdate(); | |
132 debug_info_emitter_->EmitStatusCountersUpdate(); | |
133 } | |
134 | |
135 PostApplyUpdates(); | |
136 } | |
137 | |
138 void DirectoryUpdateHandler::PassiveApplyUpdates( | |
139 sessions::StatusController* status) { | |
140 if (IsApplyUpdatesRequired()) { | |
141 // Just do the work here instead of deferring to another thread. | |
142 ApplyUpdatesImpl(status); | |
143 | |
144 debug_info_emitter_->EmitUpdateCountersUpdate(); | |
145 debug_info_emitter_->EmitStatusCountersUpdate(); | |
146 } | |
147 | |
148 PostApplyUpdates(); | |
149 } | |
150 | |
151 SyncerError DirectoryUpdateHandler::ApplyUpdatesImpl( | |
152 sessions::StatusController* status) { | |
153 syncable::WriteTransaction trans(FROM_HERE, syncable::SYNCER, dir_); | |
154 | |
155 std::vector<int64_t> handles; | |
156 dir_->GetUnappliedUpdateMetaHandles( | |
157 &trans, | |
158 FullModelTypeSet(type_), | |
159 &handles); | |
160 | |
161 // First set of update application passes. | |
162 UpdateApplicator applicator(dir_->GetCryptographer(&trans)); | |
163 applicator.AttemptApplications(&trans, handles); | |
164 | |
165 // The old StatusController counters. | |
166 status->increment_num_updates_applied_by(applicator.updates_applied()); | |
167 status->increment_num_hierarchy_conflicts_by( | |
168 applicator.hierarchy_conflicts()); | |
169 status->increment_num_encryption_conflicts_by( | |
170 applicator.encryption_conflicts()); | |
171 | |
172 // The new UpdateCounter counters. | |
173 UpdateCounters* counters = debug_info_emitter_->GetMutableUpdateCounters(); | |
174 counters->num_updates_applied += applicator.updates_applied(); | |
175 counters->num_hierarchy_conflict_application_failures = | |
176 applicator.hierarchy_conflicts(); | |
177 counters->num_encryption_conflict_application_failures += | |
178 applicator.encryption_conflicts(); | |
179 | |
180 if (applicator.simple_conflict_ids().size() != 0) { | |
181 // Resolve the simple conflicts we just detected. | |
182 ConflictResolver resolver; | |
183 resolver.ResolveConflicts(&trans, | |
184 dir_->GetCryptographer(&trans), | |
185 applicator.simple_conflict_ids(), | |
186 status, | |
187 counters); | |
188 | |
189 // Conflict resolution sometimes results in more updates to apply. | |
190 handles.clear(); | |
191 dir_->GetUnappliedUpdateMetaHandles( | |
192 &trans, | |
193 FullModelTypeSet(type_), | |
194 &handles); | |
195 | |
196 UpdateApplicator conflict_applicator(dir_->GetCryptographer(&trans)); | |
197 conflict_applicator.AttemptApplications(&trans, handles); | |
198 | |
199 // We count the number of updates from both applicator passes. | |
200 status->increment_num_updates_applied_by( | |
201 conflict_applicator.updates_applied()); | |
202 counters->num_updates_applied += conflict_applicator.updates_applied(); | |
203 | |
204 // Encryption conflicts should remain unchanged by the resolution of simple | |
205 // conflicts. Those can only be solved by updating our nigori key bag. | |
206 DCHECK_EQ(conflict_applicator.encryption_conflicts(), | |
207 applicator.encryption_conflicts()); | |
208 | |
209 // Hierarchy conflicts should also remain unchanged, for reasons that are | |
210 // more subtle. Hierarchy conflicts exist when the application of a pending | |
211 // update from the server would make the local folder hierarchy | |
212 // inconsistent. The resolution of simple conflicts could never affect the | |
213 // hierarchy conflicting item directly, because hierarchy conflicts are not | |
214 // processed by the conflict resolver. It could, in theory, modify the | |
215 // local hierarchy on which hierarchy conflict detection depends. However, | |
216 // the conflict resolution algorithm currently in use does not allow this. | |
217 DCHECK_EQ(conflict_applicator.hierarchy_conflicts(), | |
218 applicator.hierarchy_conflicts()); | |
219 | |
220 // There should be no simple conflicts remaining. We know this because the | |
221 // resolver should have resolved all the conflicts we detected last time | |
222 // and, by the two previous assertions, that no conflicts have been | |
223 // downgraded from encryption or hierarchy down to simple. | |
224 DCHECK(conflict_applicator.simple_conflict_ids().empty()); | |
225 } | |
226 | |
227 return SYNCER_OK; | |
228 } | |
229 | |
230 void DirectoryUpdateHandler::PostApplyUpdates() { | |
231 // If this is a type with client generated root, the root node has been | |
232 // created locally and didn't go through ApplyUpdatesImpl. | |
233 // Mark it as having the initial download completed so that the type | |
234 // reports as properly initialized (which is done by changing the root's | |
235 // base version to a value other than CHANGES_VERSION). | |
236 // This does nothing if the root's base version is already other than | |
237 // CHANGES_VERSION. | |
238 if (IsTypeWithClientGeneratedRoot(type_)) { | |
239 syncable::ModelNeutralWriteTransaction trans(FROM_HERE, SYNCER, dir_); | |
240 dir_->MarkInitialSyncEndedForType(&trans, type_); | |
241 } | |
242 } | |
243 | |
244 bool DirectoryUpdateHandler::IsApplyUpdatesRequired() { | |
245 if (IsControlType(type_)) { | |
246 return false; // We don't process control types here. | |
247 } | |
248 | |
249 return dir_->TypeHasUnappliedUpdates(type_); | |
250 } | |
251 | |
252 void DirectoryUpdateHandler::UpdateSyncEntities( | |
253 syncable::ModelNeutralWriteTransaction* trans, | |
254 const SyncEntityList& applicable_updates, | |
255 sessions::StatusController* status) { | |
256 UpdateCounters* counters = debug_info_emitter_->GetMutableUpdateCounters(); | |
257 counters->num_updates_received += applicable_updates.size(); | |
258 ProcessDownloadedUpdates(dir_, trans, type_, | |
259 applicable_updates, status, counters); | |
260 } | |
261 | |
262 bool DirectoryUpdateHandler::IsValidProgressMarker( | |
263 const sync_pb::DataTypeProgressMarker& progress_marker) const { | |
264 if (progress_marker.token().empty()) { | |
265 return false; | |
266 } | |
267 int field_number = progress_marker.data_type_id(); | |
268 ModelType model_type = GetModelTypeFromSpecificsFieldNumber(field_number); | |
269 if (!IsRealDataType(model_type) || type_ != model_type) { | |
270 NOTREACHED() | |
271 << "Update handler of type " << ModelTypeToString(type_) | |
272 << " asked to process progress marker with invalid type " | |
273 << field_number; | |
274 return false; | |
275 } | |
276 return true; | |
277 } | |
278 | |
279 void DirectoryUpdateHandler::UpdateProgressMarker( | |
280 const sync_pb::DataTypeProgressMarker& progress_marker) { | |
281 if (progress_marker.has_gc_directive() || !cached_gc_directive_) { | |
282 dir_->SetDownloadProgress(type_, progress_marker); | |
283 } else { | |
284 sync_pb::DataTypeProgressMarker merged_marker = progress_marker; | |
285 merged_marker.mutable_gc_directive()->CopyFrom(*cached_gc_directive_); | |
286 dir_->SetDownloadProgress(type_, merged_marker); | |
287 } | |
288 } | |
289 | |
290 void DirectoryUpdateHandler::ExpireEntriesIfNeeded( | |
291 syncable::ModelNeutralWriteTransaction* trans, | |
292 const sync_pb::DataTypeProgressMarker& progress_marker) { | |
293 if (!cached_gc_directive_) { | |
294 sync_pb::DataTypeProgressMarker current_marker; | |
295 GetDownloadProgress(¤t_marker); | |
296 if (current_marker.has_gc_directive()) { | |
297 cached_gc_directive_.reset(new sync_pb::GarbageCollectionDirective( | |
298 current_marker.gc_directive())); | |
299 } | |
300 } | |
301 | |
302 if (!progress_marker.has_gc_directive()) | |
303 return; | |
304 | |
305 const sync_pb::GarbageCollectionDirective& new_gc_directive = | |
306 progress_marker.gc_directive(); | |
307 | |
308 if (new_gc_directive.has_version_watermark() && | |
309 (!cached_gc_directive_ || | |
310 cached_gc_directive_->version_watermark() < | |
311 new_gc_directive.version_watermark())) { | |
312 ExpireEntriesByVersion(dir_, trans, type_, | |
313 new_gc_directive.version_watermark()); | |
314 } | |
315 | |
316 cached_gc_directive_.reset( | |
317 new sync_pb::GarbageCollectionDirective(new_gc_directive)); | |
318 } | |
319 | |
320 } // namespace syncer | |
OLD | NEW |