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