Home | History | Annotate | Download | only in engine
      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(&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