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_commit_contribution.h"
      6 
      7 #include "sync/engine/commit_util.h"
      8 #include "sync/engine/get_commit_ids.h"
      9 #include "sync/engine/syncer_util.h"
     10 #include "sync/internal_api/public/sessions/commit_counters.h"
     11 #include "sync/syncable/model_neutral_mutable_entry.h"
     12 #include "sync/syncable/syncable_model_neutral_write_transaction.h"
     13 
     14 namespace syncer {
     15 
     16 using syncable::GET_BY_HANDLE;
     17 using syncable::SYNCER;
     18 
     19 DirectoryCommitContribution::~DirectoryCommitContribution() {
     20   DCHECK(!syncing_bits_set_);
     21 }
     22 
     23 // static.
     24 scoped_ptr<DirectoryCommitContribution> DirectoryCommitContribution::Build(
     25     syncable::Directory* dir,
     26     ModelType type,
     27     size_t max_entries,
     28     DirectoryTypeDebugInfoEmitter* debug_info_emitter) {
     29   DCHECK(debug_info_emitter);
     30 
     31   std::vector<int64> metahandles;
     32 
     33   syncable::ModelNeutralWriteTransaction trans(FROM_HERE, SYNCER, dir);
     34   GetCommitIdsForType(&trans, type, max_entries, &metahandles);
     35 
     36   if (metahandles.empty())
     37     return scoped_ptr<DirectoryCommitContribution>();
     38 
     39   google::protobuf::RepeatedPtrField<sync_pb::SyncEntity> entities;
     40   for (std::vector<int64>::iterator it = metahandles.begin();
     41        it != metahandles.end(); ++it) {
     42     sync_pb::SyncEntity* entity = entities.Add();
     43     syncable::ModelNeutralMutableEntry entry(&trans, GET_BY_HANDLE, *it);
     44     commit_util::BuildCommitItem(entry, entity);
     45     entry.PutSyncing(true);
     46   }
     47 
     48   sync_pb::DataTypeContext context;
     49   dir->GetDataTypeContext(&trans, type, &context);
     50 
     51   return scoped_ptr<DirectoryCommitContribution>(
     52       new DirectoryCommitContribution(
     53           metahandles,
     54           entities,
     55           context,
     56           dir,
     57           debug_info_emitter));
     58 }
     59 
     60 void DirectoryCommitContribution::AddToCommitMessage(
     61     sync_pb::ClientToServerMessage* msg) {
     62   DCHECK(syncing_bits_set_);
     63   sync_pb::CommitMessage* commit_message = msg->mutable_commit();
     64   entries_start_index_ = commit_message->entries_size();
     65   std::copy(entities_.begin(),
     66             entities_.end(),
     67             RepeatedPtrFieldBackInserter(commit_message->mutable_entries()));
     68   if (!context_.context().empty())
     69     commit_message->add_client_contexts()->Swap(&context_);
     70 
     71   CommitCounters* counters = debug_info_emitter_->GetMutableCommitCounters();
     72   counters->num_commits_attempted += entities_.size();
     73 }
     74 
     75 SyncerError DirectoryCommitContribution::ProcessCommitResponse(
     76     const sync_pb::ClientToServerResponse& response,
     77     sessions::StatusController* status) {
     78   DCHECK(syncing_bits_set_);
     79   const sync_pb::CommitResponse& commit_response = response.commit();
     80 
     81   int transient_error_commits = 0;
     82   int conflicting_commits = 0;
     83   int error_commits = 0;
     84   int successes = 0;
     85 
     86   std::set<syncable::Id> deleted_folders;
     87   {
     88     syncable::ModelNeutralWriteTransaction trans(FROM_HERE, SYNCER, dir_);
     89     for (size_t i = 0; i < metahandles_.size(); ++i) {
     90       sync_pb::CommitResponse::ResponseType response_type =
     91           commit_util::ProcessSingleCommitResponse(
     92               &trans,
     93               commit_response.entryresponse(entries_start_index_ + i),
     94               entities_.Get(i),
     95               metahandles_[i],
     96               &deleted_folders);
     97       switch (response_type) {
     98         case sync_pb::CommitResponse::INVALID_MESSAGE:
     99           ++error_commits;
    100           break;
    101         case sync_pb::CommitResponse::CONFLICT:
    102           ++conflicting_commits;
    103           status->increment_num_server_conflicts();
    104           break;
    105         case sync_pb::CommitResponse::SUCCESS:
    106           ++successes;
    107           {
    108             syncable::Entry e(&trans, GET_BY_HANDLE, metahandles_[i]);
    109             if (e.GetModelType() == BOOKMARKS)
    110               status->increment_num_successful_bookmark_commits();
    111           }
    112           status->increment_num_successful_commits();
    113           break;
    114         case sync_pb::CommitResponse::OVER_QUOTA:
    115           // We handle over quota like a retry, which is same as transient.
    116         case sync_pb::CommitResponse::RETRY:
    117         case sync_pb::CommitResponse::TRANSIENT_ERROR:
    118           ++transient_error_commits;
    119           break;
    120         default:
    121           LOG(FATAL) << "Bad return from ProcessSingleCommitResponse";
    122       }
    123     }
    124     MarkDeletedChildrenSynced(dir_, &trans, &deleted_folders);
    125   }
    126 
    127   CommitCounters* counters = debug_info_emitter_->GetMutableCommitCounters();
    128   counters->num_commits_success += successes;
    129   counters->num_commits_conflict += transient_error_commits;
    130   counters->num_commits_error += transient_error_commits;
    131 
    132   int commit_count = static_cast<int>(metahandles_.size());
    133   if (commit_count == successes) {
    134     return SYNCER_OK;
    135   } else if (error_commits > 0) {
    136     return SERVER_RETURN_UNKNOWN_ERROR;
    137   } else if (transient_error_commits > 0) {
    138     return SERVER_RETURN_TRANSIENT_ERROR;
    139   } else if (conflicting_commits > 0) {
    140     // This means that the server already has an item with this version, but
    141     // we haven't seen that update yet.
    142     //
    143     // A well-behaved client should respond to this by proceeding to the
    144     // download updates phase, fetching the conflicting items, then attempting
    145     // to resolve the conflict.  That's not what this client does.
    146     //
    147     // We don't currently have any code to support that exceptional control
    148     // flow.  Instead, we abort the current sync cycle and start a new one.  The
    149     // end result is the same.
    150     return SERVER_RETURN_CONFLICT;
    151   } else {
    152     LOG(FATAL) << "Inconsistent counts when processing commit response";
    153     return SYNCER_OK;
    154   }
    155 }
    156 
    157 void DirectoryCommitContribution::CleanUp() {
    158   DCHECK(syncing_bits_set_);
    159   UnsetSyncingBits();
    160   debug_info_emitter_->EmitCommitCountersUpdate();
    161   debug_info_emitter_->EmitStatusCountersUpdate();
    162 }
    163 
    164 size_t DirectoryCommitContribution::GetNumEntries() const {
    165   return metahandles_.size();
    166 }
    167 
    168 DirectoryCommitContribution::DirectoryCommitContribution(
    169     const std::vector<int64>& metahandles,
    170     const google::protobuf::RepeatedPtrField<sync_pb::SyncEntity>& entities,
    171     const sync_pb::DataTypeContext& context,
    172     syncable::Directory* dir,
    173     DirectoryTypeDebugInfoEmitter* debug_info_emitter)
    174     : dir_(dir),
    175       metahandles_(metahandles),
    176       entities_(entities),
    177       context_(context),
    178       entries_start_index_(0xDEADBEEF),
    179       syncing_bits_set_(true),
    180       debug_info_emitter_(debug_info_emitter) {}
    181 
    182 void DirectoryCommitContribution::UnsetSyncingBits() {
    183   syncable::ModelNeutralWriteTransaction trans(FROM_HERE, SYNCER, dir_);
    184   for (std::vector<int64>::const_iterator it = metahandles_.begin();
    185        it != metahandles_.end(); ++it) {
    186     syncable::ModelNeutralMutableEntry entry(&trans, GET_BY_HANDLE, *it);
    187     entry.PutSyncing(false);
    188   }
    189   syncing_bits_set_ = false;
    190 }
    191 
    192 }  // namespace syncer
    193