Home | History | Annotate | Download | only in engine
      1 // Copyright 2012 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/commit.h"
      6 
      7 #include "base/debug/trace_event.h"
      8 #include "sync/engine/build_commit_command.h"
      9 #include "sync/engine/get_commit_ids_command.h"
     10 #include "sync/engine/process_commit_response_command.h"
     11 #include "sync/engine/syncer_proto_util.h"
     12 #include "sync/sessions/sync_session.h"
     13 #include "sync/syncable/mutable_entry.h"
     14 #include "sync/syncable/syncable_write_transaction.h"
     15 
     16 namespace syncer {
     17 
     18 using sessions::SyncSession;
     19 using sessions::StatusController;
     20 using syncable::SYNCER;
     21 using syncable::WriteTransaction;
     22 
     23 namespace {
     24 
     25 // Sets the SYNCING bits of all items in the commit set to value_to_set.
     26 void SetAllSyncingBitsToValue(WriteTransaction* trans,
     27                               const sessions::OrderedCommitSet& commit_set,
     28                               bool value_to_set) {
     29   const std::vector<syncable::Id>& commit_ids = commit_set.GetAllCommitIds();
     30   for (std::vector<syncable::Id>::const_iterator it = commit_ids.begin();
     31        it != commit_ids.end(); ++it) {
     32     syncable::MutableEntry entry(trans, syncable::GET_BY_ID, *it);
     33     if (entry.good()) {
     34       entry.Put(syncable::SYNCING, value_to_set);
     35     }
     36   }
     37 }
     38 
     39 // Sets the SYNCING bits for all items in the OrderedCommitSet.
     40 void SetSyncingBits(WriteTransaction* trans,
     41                     const sessions::OrderedCommitSet& commit_set) {
     42   SetAllSyncingBitsToValue(trans, commit_set, true);
     43 }
     44 
     45 // Clears the SYNCING bits for all items in the OrderedCommitSet.
     46 void ClearSyncingBits(syncable::Directory* dir,
     47                       const sessions::OrderedCommitSet& commit_set) {
     48   WriteTransaction trans(FROM_HERE, SYNCER, dir);
     49   SetAllSyncingBitsToValue(&trans, commit_set, false);
     50 }
     51 
     52 // Helper function that finds sync items that are ready to be committed to the
     53 // server and serializes them into a commit message protobuf.  It will return
     54 // false iff there are no entries ready to be committed at this time.
     55 //
     56 // The OrderedCommitSet parameter is an output parameter which will contain
     57 // the set of all items which are to be committed.  The number of items in
     58 // the set shall not exceed the maximum batch size.  (The default batch size
     59 // is currently 25, though it can be overwritten by the server.)
     60 //
     61 // The ClientToServerMessage parameter is an output parameter which will contain
     62 // the commit message which should be sent to the server.  It is valid iff the
     63 // return value of this function is true.
     64 bool PrepareCommitMessage(
     65     sessions::SyncSession* session,
     66     ModelTypeSet requested_types,
     67     sessions::OrderedCommitSet* commit_set,
     68     sync_pb::ClientToServerMessage* commit_message,
     69     ExtensionsActivity::Records* extensions_activity_buffer) {
     70   TRACE_EVENT0("sync", "PrepareCommitMessage");
     71 
     72   commit_set->Clear();
     73   commit_message->Clear();
     74 
     75   WriteTransaction trans(FROM_HERE, SYNCER, session->context()->directory());
     76 
     77   // Fetch the items to commit.
     78   const size_t batch_size = session->context()->max_commit_batch_size();
     79   GetCommitIdsCommand get_commit_ids_command(&trans,
     80                                              requested_types,
     81                                              batch_size,
     82                                              commit_set);
     83   get_commit_ids_command.Execute(session);
     84 
     85   DVLOG(1) << "Commit message will contain " << commit_set->Size() << " items.";
     86   if (commit_set->Empty()) {
     87     return false;
     88   }
     89 
     90   // Serialize the message.
     91   BuildCommitCommand build_commit_command(&trans,
     92                                           *commit_set,
     93                                           commit_message,
     94                                           extensions_activity_buffer);
     95   build_commit_command.Execute(session);
     96 
     97   SetSyncingBits(&trans, *commit_set);
     98   return true;
     99 }
    100 
    101 SyncerError BuildAndPostCommitsImpl(ModelTypeSet requested_types,
    102                                     Syncer* syncer,
    103                                     sessions::SyncSession* session,
    104                                     sessions::OrderedCommitSet* commit_set) {
    105   ModelTypeSet commit_request_types;
    106   while (!syncer->ExitRequested()) {
    107     sync_pb::ClientToServerMessage commit_message;
    108     ExtensionsActivity::Records extensions_activity_buffer;
    109 
    110     if (!PrepareCommitMessage(session,
    111                               requested_types,
    112                               commit_set,
    113                               &commit_message,
    114                               &extensions_activity_buffer)) {
    115       break;
    116     }
    117 
    118     commit_request_types.PutAll(commit_set->Types());
    119     session->mutable_status_controller()->set_commit_request_types(
    120         commit_request_types);
    121 
    122     sync_pb::ClientToServerResponse commit_response;
    123 
    124     DVLOG(1) << "Sending commit message.";
    125     TRACE_EVENT_BEGIN0("sync", "PostCommit");
    126     const SyncerError post_result = SyncerProtoUtil::PostClientToServerMessage(
    127         &commit_message, &commit_response, session);
    128     TRACE_EVENT_END0("sync", "PostCommit");
    129 
    130     // TODO(rlarocque): Put all the post-commit logic in one place.
    131     // See crbug.com/196338.
    132 
    133     if (post_result != SYNCER_OK) {
    134       LOG(WARNING) << "Post commit failed";
    135       return post_result;
    136     }
    137 
    138     if (!commit_response.has_commit()) {
    139       LOG(WARNING) << "Commit response has no commit body!";
    140       return SERVER_RESPONSE_VALIDATION_FAILED;
    141     }
    142 
    143     const size_t num_responses = commit_response.commit().entryresponse_size();
    144     if (num_responses != commit_set->Size()) {
    145       LOG(ERROR)
    146          << "Commit response has wrong number of entries! "
    147          << "Expected: " << commit_set->Size() << ", "
    148          << "Got: " << num_responses;
    149       return SERVER_RESPONSE_VALIDATION_FAILED;
    150     }
    151 
    152     TRACE_EVENT_BEGIN0("sync", "ProcessCommitResponse");
    153     ProcessCommitResponseCommand process_response_command(
    154         *commit_set, commit_message, commit_response);
    155     const SyncerError processing_result =
    156         process_response_command.Execute(session);
    157     TRACE_EVENT_END0("sync", "ProcessCommitResponse");
    158 
    159     // If the commit failed, return the data to the ExtensionsActivityMonitor.
    160     if (session->status_controller().
    161         model_neutral_state().num_successful_bookmark_commits == 0) {
    162       ExtensionsActivity* extensions_activity =
    163           session->context()->extensions_activity();
    164       extensions_activity->PutRecords(extensions_activity_buffer);
    165     }
    166 
    167     if (processing_result != SYNCER_OK) {
    168       return processing_result;
    169     }
    170     session->SendEventNotification(SyncEngineEvent::STATUS_CHANGED);
    171   }
    172 
    173   return SYNCER_OK;
    174 }
    175 
    176 }  // namespace
    177 
    178 
    179 SyncerError BuildAndPostCommits(ModelTypeSet requested_types,
    180                                 Syncer* syncer,
    181                                 sessions::SyncSession* session) {
    182   sessions::OrderedCommitSet commit_set(session->context()->routing_info());
    183   SyncerError result =
    184       BuildAndPostCommitsImpl(requested_types, syncer, session, &commit_set);
    185   if (result != SYNCER_OK) {
    186     ClearSyncingBits(session->context()->directory(), commit_set);
    187   }
    188   return result;
    189 }
    190 
    191 }  // namespace syncer
    192