| OLD | NEW |
| 1 // Copyright 2012 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "sync/engine/commit.h" | 5 #include "sync/engine/commit.h" |
| 6 | 6 |
| 7 #include "base/debug/trace_event.h" | 7 #include "base/debug/trace_event.h" |
| 8 #include "sync/engine/build_commit_command.h" | 8 #include "sync/engine/commit_util.h" |
| 9 #include "sync/engine/get_commit_ids.h" | 9 #include "sync/engine/sync_directory_commit_contribution.h" |
| 10 #include "sync/engine/process_commit_response_command.h" | |
| 11 #include "sync/engine/syncer.h" | 10 #include "sync/engine/syncer.h" |
| 12 #include "sync/engine/syncer_proto_util.h" | 11 #include "sync/engine/syncer_proto_util.h" |
| 13 #include "sync/sessions/sync_session.h" | 12 #include "sync/sessions/sync_session.h" |
| 14 #include "sync/syncable/mutable_entry.h" | |
| 15 #include "sync/syncable/syncable_model_neutral_write_transaction.h" | |
| 16 | 13 |
| 17 namespace syncer { | 14 namespace syncer { |
| 18 | 15 |
| 19 using sessions::SyncSession; | 16 Commit::Commit( |
| 20 using sessions::StatusController; | 17 const std::map<ModelType, SyncDirectoryCommitContribution*>& contributions, |
| 21 using syncable::SYNCER; | 18 const sync_pb::ClientToServerMessage& message, |
| 22 using syncable::ModelNeutralWriteTransaction; | 19 ExtensionsActivity::Records extensions_activity_buffer) |
| 20 : contributions_(contributions), |
| 21 deleter_(&contributions_), |
| 22 message_(message), |
| 23 extensions_activity_buffer_(extensions_activity_buffer), |
| 24 cleaned_up_(false) { |
| 25 } |
| 23 | 26 |
| 24 namespace { | 27 Commit::~Commit() { |
| 28 DCHECK(cleaned_up_); |
| 29 } |
| 25 | 30 |
| 26 // Sets the SYNCING bits of all items in the commit set to value_to_set. | 31 Commit* Commit::Init( |
| 27 void SetAllSyncingBitsToValue(ModelNeutralWriteTransaction* trans, | 32 ModelTypeSet requested_types, |
| 28 const sessions::OrderedCommitSet& commit_set, | 33 size_t max_entries, |
| 29 bool value_to_set) { | 34 const std::string& account_name, |
| 30 const std::vector<int64>& commit_handles = commit_set.GetAllCommitHandles(); | 35 const std::string& cache_guid, |
| 31 for (std::vector<int64>::const_iterator it = commit_handles.begin(); | 36 CommitContributorMap* contributor_map, |
| 32 it != commit_handles.end(); ++it) { | 37 ExtensionsActivity* extensions_activity) { |
| 33 syncable::ModelNeutralMutableEntry entry( | 38 // Gather per-type contributions. |
| 34 trans, syncable::GET_BY_HANDLE, *it); | 39 ContributionMap contributions; |
| 35 if (entry.good()) { | 40 size_t num_entries = 0; |
| 36 entry.PutSyncing(value_to_set); | 41 for (ModelTypeSet::Iterator it = requested_types.First(); |
| 42 it.Good(); it.Inc()) { |
| 43 CommitContributorMap::iterator cm_it = contributor_map->find(it.Get()); |
| 44 if (cm_it == contributor_map->end()) { |
| 45 NOTREACHED() |
| 46 << "Could not find requested type " << ModelTypeToString(it.Get()) |
| 47 << " in contributor map."; |
| 48 continue; |
| 49 } |
| 50 size_t spaces_remaining = max_entries - num_entries; |
| 51 SyncDirectoryCommitContribution* contribution = |
| 52 cm_it->second->GetContribution(spaces_remaining); |
| 53 if (contribution) { |
| 54 num_entries += contribution->GetNumEntries(); |
| 55 contributions.insert(std::make_pair(it.Get(), contribution)); |
| 56 } |
| 57 if (num_entries == max_entries) { |
| 58 break; // No point in continuting to iterate in this case. |
| 37 } | 59 } |
| 38 } | 60 } |
| 61 |
| 62 // Give up if no one had anything to commit. |
| 63 if (contributions.empty()) |
| 64 return NULL; |
| 65 |
| 66 sync_pb::ClientToServerMessage message; |
| 67 message.set_message_contents(sync_pb::ClientToServerMessage::COMMIT); |
| 68 message.set_share(account_name); |
| 69 |
| 70 sync_pb::CommitMessage* commit_message = message.mutable_commit(); |
| 71 commit_message->set_cache_guid(cache_guid); |
| 72 |
| 73 // Set extensions activity if bookmark commits are present. |
| 74 ExtensionsActivity::Records extensions_activity_buffer; |
| 75 ContributionMap::iterator it = contributions.find(syncer::BOOKMARKS); |
| 76 if (it != contributions.end() && it->second->GetNumEntries() != 0) { |
| 77 commit_util::AddExtensionsActivityToMessage( |
| 78 extensions_activity, |
| 79 &extensions_activity_buffer, |
| 80 commit_message); |
| 81 } |
| 82 |
| 83 // Set the client config params. |
| 84 ModelTypeSet enabled_types; |
| 85 for (CommitContributorMap::iterator it = contributor_map->begin(); |
| 86 it != contributor_map->end(); ++it) { |
| 87 enabled_types.Put(it->first); |
| 88 } |
| 89 commit_util::AddClientConfigParamsToMessage(enabled_types, |
| 90 commit_message); |
| 91 |
| 92 // Finally, serialize all our contributions. |
| 93 for (std::map<ModelType, SyncDirectoryCommitContribution*>::iterator it = |
| 94 contributions.begin(); it != contributions.end(); ++it) { |
| 95 it->second->AddToCommitMessage(&message); |
| 96 } |
| 97 |
| 98 // If we made it this far, then we've successfully prepared a commit message. |
| 99 return new Commit(contributions, message, extensions_activity_buffer); |
| 39 } | 100 } |
| 40 | 101 |
| 41 // Sets the SYNCING bits for all items in the OrderedCommitSet. | 102 SyncerError Commit::PostAndProcessResponse( |
| 42 void SetSyncingBits(ModelNeutralWriteTransaction* trans, | 103 sessions::SyncSession* session, |
| 43 const sessions::OrderedCommitSet& commit_set) { | 104 sessions::StatusController* status, |
| 44 SetAllSyncingBitsToValue(trans, commit_set, true); | 105 ExtensionsActivity* extensions_activity) { |
| 106 ModelTypeSet request_types; |
| 107 for (ContributionMap::const_iterator it = contributions_.begin(); |
| 108 it != contributions_.end(); ++it) { |
| 109 request_types.Put(it->first); |
| 110 } |
| 111 session->mutable_status_controller()->set_commit_request_types(request_types); |
| 112 |
| 113 DVLOG(1) << "Sending commit message."; |
| 114 TRACE_EVENT_BEGIN0("sync", "PostCommit"); |
| 115 const SyncerError post_result = SyncerProtoUtil::PostClientToServerMessage( |
| 116 &message_, &response_, session); |
| 117 TRACE_EVENT_END0("sync", "PostCommit"); |
| 118 |
| 119 if (post_result != SYNCER_OK) { |
| 120 LOG(WARNING) << "Post commit failed"; |
| 121 return post_result; |
| 122 } |
| 123 |
| 124 if (!response_.has_commit()) { |
| 125 LOG(WARNING) << "Commit response has no commit body!"; |
| 126 return SERVER_RESPONSE_VALIDATION_FAILED; |
| 127 } |
| 128 |
| 129 size_t message_entries = message_.commit().entries_size(); |
| 130 size_t response_entries = response_.commit().entryresponse_size(); |
| 131 if (message_entries != response_entries) { |
| 132 LOG(ERROR) |
| 133 << "Commit response has wrong number of entries! " |
| 134 << "Expected: " << message_entries << ", " |
| 135 << "Got: " << response_entries; |
| 136 return SERVER_RESPONSE_VALIDATION_FAILED; |
| 137 } |
| 138 |
| 139 // Let the contributors process the responses to each of their requests. |
| 140 SyncerError processing_result = SYNCER_OK; |
| 141 for (std::map<ModelType, SyncDirectoryCommitContribution*>::iterator it = |
| 142 contributions_.begin(); it != contributions_.end(); ++it) { |
| 143 TRACE_EVENT1("sync", "ProcessCommitResponse", |
| 144 "type", ModelTypeToString(it->first)); |
| 145 SyncerError type_result = |
| 146 it->second->ProcessCommitResponse(response_, status); |
| 147 if (processing_result == SYNCER_OK && type_result != SYNCER_OK) { |
| 148 processing_result = type_result; |
| 149 } |
| 150 } |
| 151 |
| 152 // Handle bookmarks' special extensions activity stats. |
| 153 if (session->status_controller(). |
| 154 model_neutral_state().num_successful_bookmark_commits == 0) { |
| 155 extensions_activity->PutRecords(extensions_activity_buffer_); |
| 156 } |
| 157 |
| 158 return processing_result; |
| 45 } | 159 } |
| 46 | 160 |
| 47 // Clears the SYNCING bits for all items in the OrderedCommitSet. | 161 void Commit::CleanUp() { |
| 48 void ClearSyncingBits(syncable::Directory* dir, | 162 for (ContributionMap::iterator it = contributions_.begin(); |
| 49 const sessions::OrderedCommitSet& commit_set) { | 163 it != contributions_.end(); ++it) { |
| 50 ModelNeutralWriteTransaction trans(FROM_HERE, SYNCER, dir); | 164 it->second->CleanUp(); |
| 51 SetAllSyncingBitsToValue(&trans, commit_set, false); | |
| 52 } | |
| 53 | |
| 54 // Helper function that finds sync items that are ready to be committed to the | |
| 55 // server and serializes them into a commit message protobuf. It will return | |
| 56 // false iff there are no entries ready to be committed at this time. | |
| 57 // | |
| 58 // The OrderedCommitSet parameter is an output parameter which will contain | |
| 59 // the set of all items which are to be committed. The number of items in | |
| 60 // the set shall not exceed the maximum batch size. (The default batch size | |
| 61 // is currently 25, though it can be overwritten by the server.) | |
| 62 // | |
| 63 // The ClientToServerMessage parameter is an output parameter which will contain | |
| 64 // the commit message which should be sent to the server. It is valid iff the | |
| 65 // return value of this function is true. | |
| 66 bool PrepareCommitMessage( | |
| 67 sessions::SyncSession* session, | |
| 68 ModelTypeSet requested_types, | |
| 69 sessions::OrderedCommitSet* commit_set, | |
| 70 sync_pb::ClientToServerMessage* commit_message, | |
| 71 ExtensionsActivity::Records* extensions_activity_buffer) { | |
| 72 TRACE_EVENT0("sync", "PrepareCommitMessage"); | |
| 73 | |
| 74 commit_set->Clear(); | |
| 75 commit_message->Clear(); | |
| 76 | |
| 77 ModelNeutralWriteTransaction trans( | |
| 78 FROM_HERE, SYNCER, session->context()->directory()); | |
| 79 | |
| 80 // Fetch the items to commit. | |
| 81 const size_t batch_size = session->context()->max_commit_batch_size(); | |
| 82 GetCommitIds(&trans, requested_types, batch_size, commit_set); | |
| 83 | |
| 84 DVLOG(1) << "Commit message will contain " << commit_set->Size() << " items."; | |
| 85 if (commit_set->Empty()) { | |
| 86 return false; | |
| 87 } | 165 } |
| 88 | 166 cleaned_up_ = true; |
| 89 // Serialize the message. | |
| 90 BuildCommitCommand build_commit_command(&trans, | |
| 91 *commit_set, | |
| 92 commit_message, | |
| 93 extensions_activity_buffer); | |
| 94 build_commit_command.Execute(session); | |
| 95 | |
| 96 SetSyncingBits(&trans, *commit_set); | |
| 97 return true; | |
| 98 } | |
| 99 | |
| 100 SyncerError BuildAndPostCommitsImpl(ModelTypeSet requested_types, | |
| 101 Syncer* syncer, | |
| 102 sessions::SyncSession* session, | |
| 103 sessions::OrderedCommitSet* commit_set) { | |
| 104 ModelTypeSet commit_request_types; | |
| 105 while (!syncer->ExitRequested()) { | |
| 106 sync_pb::ClientToServerMessage commit_message; | |
| 107 ExtensionsActivity::Records extensions_activity_buffer; | |
| 108 | |
| 109 if (!PrepareCommitMessage(session, | |
| 110 requested_types, | |
| 111 commit_set, | |
| 112 &commit_message, | |
| 113 &extensions_activity_buffer)) { | |
| 114 break; | |
| 115 } | |
| 116 | |
| 117 commit_request_types.PutAll(commit_set->Types()); | |
| 118 session->mutable_status_controller()->set_commit_request_types( | |
| 119 commit_request_types); | |
| 120 | |
| 121 sync_pb::ClientToServerResponse commit_response; | |
| 122 | |
| 123 DVLOG(1) << "Sending commit message."; | |
| 124 TRACE_EVENT_BEGIN0("sync", "PostCommit"); | |
| 125 const SyncerError post_result = SyncerProtoUtil::PostClientToServerMessage( | |
| 126 &commit_message, &commit_response, session); | |
| 127 TRACE_EVENT_END0("sync", "PostCommit"); | |
| 128 | |
| 129 // TODO(rlarocque): Put all the post-commit logic in one place. | |
| 130 // See crbug.com/196338. | |
| 131 | |
| 132 if (post_result != SYNCER_OK) { | |
| 133 LOG(WARNING) << "Post commit failed"; | |
| 134 return post_result; | |
| 135 } | |
| 136 | |
| 137 if (!commit_response.has_commit()) { | |
| 138 LOG(WARNING) << "Commit response has no commit body!"; | |
| 139 return SERVER_RESPONSE_VALIDATION_FAILED; | |
| 140 } | |
| 141 | |
| 142 const size_t num_responses = commit_response.commit().entryresponse_size(); | |
| 143 if (num_responses != commit_set->Size()) { | |
| 144 LOG(ERROR) | |
| 145 << "Commit response has wrong number of entries! " | |
| 146 << "Expected: " << commit_set->Size() << ", " | |
| 147 << "Got: " << num_responses; | |
| 148 return SERVER_RESPONSE_VALIDATION_FAILED; | |
| 149 } | |
| 150 | |
| 151 TRACE_EVENT_BEGIN0("sync", "ProcessCommitResponse"); | |
| 152 ProcessCommitResponseCommand process_response_command( | |
| 153 *commit_set, commit_message, commit_response); | |
| 154 const SyncerError processing_result = | |
| 155 process_response_command.Execute(session); | |
| 156 TRACE_EVENT_END0("sync", "ProcessCommitResponse"); | |
| 157 | |
| 158 // If the commit failed, return the data to the ExtensionsActivityMonitor. | |
| 159 if (session->status_controller(). | |
| 160 model_neutral_state().num_successful_bookmark_commits == 0) { | |
| 161 ExtensionsActivity* extensions_activity = | |
| 162 session->context()->extensions_activity(); | |
| 163 extensions_activity->PutRecords(extensions_activity_buffer); | |
| 164 } | |
| 165 | |
| 166 if (processing_result != SYNCER_OK) { | |
| 167 return processing_result; | |
| 168 } | |
| 169 session->SendEventNotification(SyncEngineEvent::STATUS_CHANGED); | |
| 170 } | |
| 171 | |
| 172 return SYNCER_OK; | |
| 173 } | |
| 174 | |
| 175 } // namespace | |
| 176 | |
| 177 | |
| 178 SyncerError BuildAndPostCommits(ModelTypeSet requested_types, | |
| 179 Syncer* syncer, | |
| 180 sessions::SyncSession* session) { | |
| 181 sessions::OrderedCommitSet commit_set; | |
| 182 SyncerError result = | |
| 183 BuildAndPostCommitsImpl(requested_types, syncer, session, &commit_set); | |
| 184 if (result != SYNCER_OK) { | |
| 185 ClearSyncingBits(session->context()->directory(), commit_set); | |
| 186 } | |
| 187 return result; | |
| 188 } | 167 } |
| 189 | 168 |
| 190 } // namespace syncer | 169 } // namespace syncer |
| OLD | NEW |