blob: 415c608def6262192f34f0db18f178bc7fd82b08 [file] [log] [blame]
// Copyright 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "sync/engine/commit.h"
#include "base/debug/trace_event.h"
#include "sync/engine/build_commit_command.h"
#include "sync/engine/get_commit_ids.h"
#include "sync/engine/process_commit_response_command.h"
#include "sync/engine/syncer.h"
#include "sync/engine/syncer_proto_util.h"
#include "sync/sessions/sync_session.h"
#include "sync/syncable/mutable_entry.h"
#include "sync/syncable/syncable_write_transaction.h"
namespace syncer {
using sessions::SyncSession;
using sessions::StatusController;
using syncable::SYNCER;
using syncable::WriteTransaction;
namespace {
// Sets the SYNCING bits of all items in the commit set to value_to_set.
void SetAllSyncingBitsToValue(WriteTransaction* trans,
const sessions::OrderedCommitSet& commit_set,
bool value_to_set) {
const std::vector<int64>& commit_handles = commit_set.GetAllCommitHandles();
for (std::vector<int64>::const_iterator it = commit_handles.begin();
it != commit_handles.end(); ++it) {
syncable::MutableEntry entry(trans, syncable::GET_BY_HANDLE, *it);
if (entry.good()) {
entry.PutSyncing(value_to_set);
}
}
}
// Sets the SYNCING bits for all items in the OrderedCommitSet.
void SetSyncingBits(WriteTransaction* trans,
const sessions::OrderedCommitSet& commit_set) {
SetAllSyncingBitsToValue(trans, commit_set, true);
}
// Clears the SYNCING bits for all items in the OrderedCommitSet.
void ClearSyncingBits(syncable::Directory* dir,
const sessions::OrderedCommitSet& commit_set) {
WriteTransaction trans(FROM_HERE, SYNCER, dir);
SetAllSyncingBitsToValue(&trans, commit_set, false);
}
// Helper function that finds sync items that are ready to be committed to the
// server and serializes them into a commit message protobuf. It will return
// false iff there are no entries ready to be committed at this time.
//
// The OrderedCommitSet parameter is an output parameter which will contain
// the set of all items which are to be committed. The number of items in
// the set shall not exceed the maximum batch size. (The default batch size
// is currently 25, though it can be overwritten by the server.)
//
// The ClientToServerMessage parameter is an output parameter which will contain
// the commit message which should be sent to the server. It is valid iff the
// return value of this function is true.
bool PrepareCommitMessage(
sessions::SyncSession* session,
ModelTypeSet requested_types,
sessions::OrderedCommitSet* commit_set,
sync_pb::ClientToServerMessage* commit_message,
ExtensionsActivity::Records* extensions_activity_buffer) {
TRACE_EVENT0("sync", "PrepareCommitMessage");
commit_set->Clear();
commit_message->Clear();
WriteTransaction trans(FROM_HERE, SYNCER, session->context()->directory());
// Fetch the items to commit.
const size_t batch_size = session->context()->max_commit_batch_size();
GetCommitIds(&trans, requested_types, batch_size, commit_set);
DVLOG(1) << "Commit message will contain " << commit_set->Size() << " items.";
if (commit_set->Empty()) {
return false;
}
// Serialize the message.
BuildCommitCommand build_commit_command(&trans,
*commit_set,
commit_message,
extensions_activity_buffer);
build_commit_command.Execute(session);
SetSyncingBits(&trans, *commit_set);
return true;
}
SyncerError BuildAndPostCommitsImpl(ModelTypeSet requested_types,
Syncer* syncer,
sessions::SyncSession* session,
sessions::OrderedCommitSet* commit_set) {
ModelTypeSet commit_request_types;
while (!syncer->ExitRequested()) {
sync_pb::ClientToServerMessage commit_message;
ExtensionsActivity::Records extensions_activity_buffer;
if (!PrepareCommitMessage(session,
requested_types,
commit_set,
&commit_message,
&extensions_activity_buffer)) {
break;
}
commit_request_types.PutAll(commit_set->Types());
session->mutable_status_controller()->set_commit_request_types(
commit_request_types);
sync_pb::ClientToServerResponse commit_response;
DVLOG(1) << "Sending commit message.";
TRACE_EVENT_BEGIN0("sync", "PostCommit");
const SyncerError post_result = SyncerProtoUtil::PostClientToServerMessage(
&commit_message, &commit_response, session);
TRACE_EVENT_END0("sync", "PostCommit");
// TODO(rlarocque): Put all the post-commit logic in one place.
// See crbug.com/196338.
if (post_result != SYNCER_OK) {
LOG(WARNING) << "Post commit failed";
return post_result;
}
if (!commit_response.has_commit()) {
LOG(WARNING) << "Commit response has no commit body!";
return SERVER_RESPONSE_VALIDATION_FAILED;
}
const size_t num_responses = commit_response.commit().entryresponse_size();
if (num_responses != commit_set->Size()) {
LOG(ERROR)
<< "Commit response has wrong number of entries! "
<< "Expected: " << commit_set->Size() << ", "
<< "Got: " << num_responses;
return SERVER_RESPONSE_VALIDATION_FAILED;
}
TRACE_EVENT_BEGIN0("sync", "ProcessCommitResponse");
ProcessCommitResponseCommand process_response_command(
*commit_set, commit_message, commit_response);
const SyncerError processing_result =
process_response_command.Execute(session);
TRACE_EVENT_END0("sync", "ProcessCommitResponse");
// If the commit failed, return the data to the ExtensionsActivityMonitor.
if (session->status_controller().
model_neutral_state().num_successful_bookmark_commits == 0) {
ExtensionsActivity* extensions_activity =
session->context()->extensions_activity();
extensions_activity->PutRecords(extensions_activity_buffer);
}
if (processing_result != SYNCER_OK) {
return processing_result;
}
session->SendEventNotification(SyncEngineEvent::STATUS_CHANGED);
}
return SYNCER_OK;
}
} // namespace
SyncerError BuildAndPostCommits(ModelTypeSet requested_types,
Syncer* syncer,
sessions::SyncSession* session) {
sessions::OrderedCommitSet commit_set(session->context()->routing_info());
SyncerError result =
BuildAndPostCommitsImpl(requested_types, syncer, session, &commit_set);
if (result != SYNCER_OK) {
ClearSyncingBits(session->context()->directory(), commit_set);
}
return result;
}
} // namespace syncer