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