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