commit.cc revision a36e5920737c6adbddd3e43b760e5de8431db6e0
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
16namespace syncer {
17
18using sessions::SyncSession;
19using sessions::StatusController;
20using syncable::SYNCER;
21using syncable::WriteTransaction;
22
23namespace {
24
25// Sets the SYNCING bits of all items in the commit set to value_to_set.
26void 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.
40void 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.
46void 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.
64bool 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
101SyncerError 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
179SyncerError 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