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 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 29 DirectoryUpdateHandler::~DirectoryUpdateHandler() {} 30 31 void DirectoryUpdateHandler::GetDownloadProgress( 32 sync_pb::DataTypeProgressMarker* progress_marker) const { 33 dir_->GetDownloadProgress(type_, progress_marker); 34 } 35 36 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 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 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 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 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 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 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 206 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 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 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(¤t_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