1// Copyright 2014 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/non_blocking_type_commit_contribution.h"
6
7#include "sync/engine/model_type_sync_worker_impl.h"
8#include "sync/internal_api/public/non_blocking_sync_common.h"
9#include "sync/protocol/proto_value_conversions.h"
10
11namespace syncer {
12
13NonBlockingTypeCommitContribution::NonBlockingTypeCommitContribution(
14    const sync_pb::DataTypeContext& context,
15    const google::protobuf::RepeatedPtrField<sync_pb::SyncEntity>& entities,
16    const std::vector<int64>& sequence_numbers,
17    ModelTypeSyncWorkerImpl* worker)
18    : worker_(worker),
19      context_(context),
20      entities_(entities),
21      sequence_numbers_(sequence_numbers),
22      cleaned_up_(false) {
23}
24
25NonBlockingTypeCommitContribution::~NonBlockingTypeCommitContribution() {
26  DCHECK(cleaned_up_);
27}
28
29void NonBlockingTypeCommitContribution::AddToCommitMessage(
30    sync_pb::ClientToServerMessage* msg) {
31  sync_pb::CommitMessage* commit_message = msg->mutable_commit();
32  entries_start_index_ = commit_message->entries_size();
33
34  std::copy(entities_.begin(),
35            entities_.end(),
36            RepeatedPtrFieldBackInserter(commit_message->mutable_entries()));
37  if (!context_.context().empty())
38    commit_message->add_client_contexts()->CopyFrom(context_);
39}
40
41SyncerError NonBlockingTypeCommitContribution::ProcessCommitResponse(
42    const sync_pb::ClientToServerResponse& response,
43    sessions::StatusController* status) {
44  const sync_pb::CommitResponse& commit_response = response.commit();
45
46  bool transient_error = false;
47  bool commit_conflict = false;
48  bool unknown_error = false;
49
50  CommitResponseDataList response_list;
51
52  for (size_t i = 0; i < sequence_numbers_.size(); ++i) {
53    const sync_pb::CommitResponse_EntryResponse& entry_response =
54        commit_response.entryresponse(entries_start_index_ + i);
55
56    switch (entry_response.response_type()) {
57      case sync_pb::CommitResponse::INVALID_MESSAGE:
58        LOG(ERROR) << "Server reports commit message is invalid.";
59        DLOG(ERROR) << "Message was: " << SyncEntityToValue(entities_.Get(i),
60                                                            false);
61        unknown_error = true;
62        break;
63      case sync_pb::CommitResponse::CONFLICT:
64        DVLOG(1) << "Server reports conflict for commit message.";
65        DVLOG(1) << "Message was: " << SyncEntityToValue(entities_.Get(i),
66                                                         false);
67        commit_conflict = true;
68        break;
69      case sync_pb::CommitResponse::SUCCESS: {
70        CommitResponseData response_data;
71        response_data.id = entry_response.id_string();
72        response_data.client_tag_hash =
73            entities_.Get(i).client_defined_unique_tag();
74        response_data.sequence_number = sequence_numbers_[i];
75        response_data.response_version = entry_response.version();
76        response_list.push_back(response_data);
77        break;
78      }
79      case sync_pb::CommitResponse::OVER_QUOTA:
80      case sync_pb::CommitResponse::RETRY:
81      case sync_pb::CommitResponse::TRANSIENT_ERROR:
82        DLOG(WARNING) << "Entity commit blocked by transient error.";
83        transient_error = true;
84        break;
85      default:
86        LOG(ERROR) << "Bad return from ProcessSingleCommitResponse.";
87        unknown_error = true;
88    }
89  }
90
91  // Send whatever successful responses we did get back to our parent.
92  // It's the schedulers job to handle the failures.
93  worker_->OnCommitResponse(response_list);
94
95  // Let the scheduler know about the failures.
96  if (unknown_error) {
97    return SERVER_RETURN_UNKNOWN_ERROR;
98  } else if (transient_error) {
99    return SERVER_RETURN_TRANSIENT_ERROR;
100  } else if (commit_conflict) {
101    return SERVER_RETURN_CONFLICT;
102  } else {
103    return SYNCER_OK;
104  }
105}
106
107void NonBlockingTypeCommitContribution::CleanUp() {
108  cleaned_up_ = true;
109
110  // We could inform our parent NonBlockingCommitContributor that a commit is
111  // no longer in progress.  The current implementation doesn't really care
112  // either way, so we don't bother sending the signal.
113}
114
115size_t NonBlockingTypeCommitContribution::GetNumEntries() const {
116  return sequence_numbers_.size();
117}
118
119}  // namespace syncer
120