• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 "sync/engine/conflict_resolver.h"
8 #include "sync/engine/process_updates_util.h"
9 #include "sync/engine/update_applicator.h"
10 #include "sync/sessions/directory_type_debug_info_emitter.h"
11 #include "sync/syncable/directory.h"
12 #include "sync/syncable/syncable_model_neutral_write_transaction.h"
13 #include "sync/syncable/syncable_write_transaction.h"
14 
15 namespace syncer {
16 
17 using syncable::SYNCER;
18 
DirectoryUpdateHandler(syncable::Directory * dir,ModelType type,scoped_refptr<ModelSafeWorker> worker,DirectoryTypeDebugInfoEmitter * debug_info_emitter)19 DirectoryUpdateHandler::DirectoryUpdateHandler(
20     syncable::Directory* dir,
21     ModelType type,
22     scoped_refptr<ModelSafeWorker> worker,
23     DirectoryTypeDebugInfoEmitter* debug_info_emitter)
24   : dir_(dir),
25     type_(type),
26     worker_(worker),
27     debug_info_emitter_(debug_info_emitter) {}
28 
~DirectoryUpdateHandler()29 DirectoryUpdateHandler::~DirectoryUpdateHandler() {}
30 
GetDownloadProgress(sync_pb::DataTypeProgressMarker * progress_marker) const31 void DirectoryUpdateHandler::GetDownloadProgress(
32     sync_pb::DataTypeProgressMarker* progress_marker) const {
33   dir_->GetDownloadProgress(type_, progress_marker);
34 }
35 
GetDataTypeContext(sync_pb::DataTypeContext * context) const36 void DirectoryUpdateHandler::GetDataTypeContext(
37     sync_pb::DataTypeContext* context) const {
38   syncable::ModelNeutralWriteTransaction trans(FROM_HERE, SYNCER, dir_);
39   dir_->GetDataTypeContext(&trans, type_, context);
40 }
41 
ProcessGetUpdatesResponse(const sync_pb::DataTypeProgressMarker & progress_marker,const sync_pb::DataTypeContext & mutated_context,const SyncEntityList & applicable_updates,sessions::StatusController * status)42 SyncerError DirectoryUpdateHandler::ProcessGetUpdatesResponse(
43     const sync_pb::DataTypeProgressMarker& progress_marker,
44     const sync_pb::DataTypeContext& mutated_context,
45     const SyncEntityList& applicable_updates,
46     sessions::StatusController* status) {
47   syncable::ModelNeutralWriteTransaction trans(FROM_HERE, SYNCER, dir_);
48   if (mutated_context.has_context()) {
49     sync_pb::DataTypeContext local_context;
50     dir_->GetDataTypeContext(&trans, type_, &local_context);
51 
52     // Only update the local context if it is still relevant. If the local
53     // version is higher, it means a local change happened while the mutation
54     // was in flight, and the local context takes priority.
55     if (mutated_context.version() >= local_context.version() &&
56         local_context.context() != mutated_context.context()) {
57       dir_->SetDataTypeContext(&trans, type_, mutated_context);
58       // TODO(zea): trigger the datatype's UpdateDataTypeContext method.
59     } else if (mutated_context.version() < local_context.version()) {
60       // A GetUpdates using the old context was in progress when the context was
61       // set. Fail this get updates cycle, to force a retry.
62       DVLOG(1) << "GU Context conflict detected, forcing GU retry.";
63       debug_info_emitter_->EmitUpdateCountersUpdate();
64       return DATATYPE_TRIGGERED_RETRY;
65     }
66   }
67 
68   UpdateSyncEntities(&trans, applicable_updates, status);
69 
70   if (IsValidProgressMarker(progress_marker)) {
71     ExpireEntriesIfNeeded(&trans, progress_marker);
72     UpdateProgressMarker(progress_marker);
73   }
74   debug_info_emitter_->EmitUpdateCountersUpdate();
75   return SYNCER_OK;
76 }
77 
ApplyUpdates(sessions::StatusController * status)78 void DirectoryUpdateHandler::ApplyUpdates(sessions::StatusController* status) {
79   if (!IsApplyUpdatesRequired()) {
80     return;
81   }
82 
83   // This will invoke handlers that belong to the model and its thread, so we
84   // switch to the appropriate thread before we start this work.
85   WorkCallback c = base::Bind(
86       &DirectoryUpdateHandler::ApplyUpdatesImpl,
87       // We wait until the callback is executed.  We can safely use Unretained.
88       base::Unretained(this),
89       base::Unretained(status));
90   worker_->DoWorkAndWaitUntilDone(c);
91 
92   debug_info_emitter_->EmitUpdateCountersUpdate();
93   debug_info_emitter_->EmitStatusCountersUpdate();
94 }
95 
PassiveApplyUpdates(sessions::StatusController * status)96 void DirectoryUpdateHandler::PassiveApplyUpdates(
97     sessions::StatusController* status) {
98   if (!IsApplyUpdatesRequired()) {
99     return;
100   }
101 
102   // Just do the work here instead of deferring to another thread.
103   ApplyUpdatesImpl(status);
104 
105   debug_info_emitter_->EmitUpdateCountersUpdate();
106   debug_info_emitter_->EmitStatusCountersUpdate();
107 }
108 
ApplyUpdatesImpl(sessions::StatusController * status)109 SyncerError DirectoryUpdateHandler::ApplyUpdatesImpl(
110     sessions::StatusController* status) {
111   syncable::WriteTransaction trans(FROM_HERE, syncable::SYNCER, dir_);
112 
113   std::vector<int64> handles;
114   dir_->GetUnappliedUpdateMetaHandles(
115       &trans,
116       FullModelTypeSet(type_),
117       &handles);
118 
119   // First set of update application passes.
120   UpdateApplicator applicator(dir_->GetCryptographer(&trans));
121   applicator.AttemptApplications(&trans, handles);
122 
123   // The old StatusController counters.
124   status->increment_num_updates_applied_by(applicator.updates_applied());
125   status->increment_num_hierarchy_conflicts_by(
126       applicator.hierarchy_conflicts());
127   status->increment_num_encryption_conflicts_by(
128       applicator.encryption_conflicts());
129 
130   // The new UpdateCounter counters.
131   UpdateCounters* counters = debug_info_emitter_->GetMutableUpdateCounters();
132   counters->num_updates_applied += applicator.updates_applied();
133   counters->num_hierarchy_conflict_application_failures =
134       applicator.hierarchy_conflicts();
135   counters->num_encryption_conflict_application_failures +=
136       applicator.encryption_conflicts();
137 
138   if (applicator.simple_conflict_ids().size() != 0) {
139     // Resolve the simple conflicts we just detected.
140     ConflictResolver resolver;
141     resolver.ResolveConflicts(&trans,
142                               dir_->GetCryptographer(&trans),
143                               applicator.simple_conflict_ids(),
144                               status,
145                               counters);
146 
147     // Conflict resolution sometimes results in more updates to apply.
148     handles.clear();
149     dir_->GetUnappliedUpdateMetaHandles(
150         &trans,
151         FullModelTypeSet(type_),
152         &handles);
153 
154     UpdateApplicator conflict_applicator(dir_->GetCryptographer(&trans));
155     conflict_applicator.AttemptApplications(&trans, handles);
156 
157     // We count the number of updates from both applicator passes.
158     status->increment_num_updates_applied_by(
159         conflict_applicator.updates_applied());
160     counters->num_updates_applied += conflict_applicator.updates_applied();
161 
162     // Encryption conflicts should remain unchanged by the resolution of simple
163     // conflicts.  Those can only be solved by updating our nigori key bag.
164     DCHECK_EQ(conflict_applicator.encryption_conflicts(),
165               applicator.encryption_conflicts());
166 
167     // Hierarchy conflicts should also remain unchanged, for reasons that are
168     // more subtle.  Hierarchy conflicts exist when the application of a pending
169     // update from the server would make the local folder hierarchy
170     // inconsistent.  The resolution of simple conflicts could never affect the
171     // hierarchy conflicting item directly, because hierarchy conflicts are not
172     // processed by the conflict resolver.  It could, in theory, modify the
173     // local hierarchy on which hierarchy conflict detection depends.  However,
174     // the conflict resolution algorithm currently in use does not allow this.
175     DCHECK_EQ(conflict_applicator.hierarchy_conflicts(),
176               applicator.hierarchy_conflicts());
177 
178     // There should be no simple conflicts remaining.  We know this because the
179     // resolver should have resolved all the conflicts we detected last time
180     // and, by the two previous assertions, that no conflicts have been
181     // downgraded from encryption or hierarchy down to simple.
182     DCHECK(conflict_applicator.simple_conflict_ids().empty());
183   }
184 
185   return SYNCER_OK;
186 }
187 
IsApplyUpdatesRequired()188 bool DirectoryUpdateHandler::IsApplyUpdatesRequired() {
189   if (IsControlType(type_)) {
190     return false;  // We don't process control types here.
191   }
192 
193   return dir_->TypeHasUnappliedUpdates(type_);
194 }
195 
UpdateSyncEntities(syncable::ModelNeutralWriteTransaction * trans,const SyncEntityList & applicable_updates,sessions::StatusController * status)196 void DirectoryUpdateHandler::UpdateSyncEntities(
197     syncable::ModelNeutralWriteTransaction* trans,
198     const SyncEntityList& applicable_updates,
199     sessions::StatusController* status) {
200   UpdateCounters* counters = debug_info_emitter_->GetMutableUpdateCounters();
201   counters->num_updates_received += applicable_updates.size();
202   ProcessDownloadedUpdates(dir_, trans, type_,
203                            applicable_updates, status, counters);
204 }
205 
IsValidProgressMarker(const sync_pb::DataTypeProgressMarker & progress_marker) const206 bool DirectoryUpdateHandler::IsValidProgressMarker(
207     const sync_pb::DataTypeProgressMarker& progress_marker) const {
208   int field_number = progress_marker.data_type_id();
209   ModelType model_type = GetModelTypeFromSpecificsFieldNumber(field_number);
210   if (!IsRealDataType(model_type) || type_ != model_type) {
211     NOTREACHED()
212         << "Update handler of type " << ModelTypeToString(type_)
213         << " asked to process progress marker with invalid type "
214         << field_number;
215     return false;
216   }
217   return true;
218 }
219 
UpdateProgressMarker(const sync_pb::DataTypeProgressMarker & progress_marker)220 void DirectoryUpdateHandler::UpdateProgressMarker(
221     const sync_pb::DataTypeProgressMarker& progress_marker) {
222   if (progress_marker.has_gc_directive() || !cached_gc_directive_) {
223     dir_->SetDownloadProgress(type_, progress_marker);
224   } else {
225     sync_pb::DataTypeProgressMarker merged_marker = progress_marker;
226     merged_marker.mutable_gc_directive()->CopyFrom(*cached_gc_directive_);
227     dir_->SetDownloadProgress(type_, merged_marker);
228   }
229 }
230 
ExpireEntriesIfNeeded(syncable::ModelNeutralWriteTransaction * trans,const sync_pb::DataTypeProgressMarker & progress_marker)231 void DirectoryUpdateHandler::ExpireEntriesIfNeeded(
232     syncable::ModelNeutralWriteTransaction* trans,
233     const sync_pb::DataTypeProgressMarker& progress_marker) {
234   if (!cached_gc_directive_) {
235     sync_pb::DataTypeProgressMarker current_marker;
236     GetDownloadProgress(&current_marker);
237     if (current_marker.has_gc_directive()) {
238       cached_gc_directive_.reset(new sync_pb::GarbageCollectionDirective(
239           current_marker.gc_directive()));
240     }
241   }
242 
243   if (!progress_marker.has_gc_directive())
244     return;
245 
246   const sync_pb::GarbageCollectionDirective& new_gc_directive =
247       progress_marker.gc_directive();
248 
249   if (new_gc_directive.has_version_watermark() &&
250       (!cached_gc_directive_ ||
251           cached_gc_directive_->version_watermark() <
252               new_gc_directive.version_watermark())) {
253     ExpireEntriesByVersion(dir_, trans, type_,
254                            new_gc_directive.version_watermark());
255   }
256 
257   cached_gc_directive_.reset(
258       new sync_pb::GarbageCollectionDirective(new_gc_directive));
259 }
260 
261 }  // namespace syncer
262