Home | History | Annotate | Download | only in engine
      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/sync_thread_sync_entity.h"
      6 
      7 #include "base/logging.h"
      8 #include "sync/engine/non_blocking_sync_common.h"
      9 #include "sync/internal_api/public/base/model_type.h"
     10 #include "sync/syncable/syncable_util.h"
     11 #include "sync/util/time.h"
     12 
     13 namespace syncer {
     14 
     15 SyncThreadSyncEntity* SyncThreadSyncEntity::FromServerUpdate(
     16     const std::string& id_string,
     17     const std::string& client_tag_hash,
     18     int64 received_version) {
     19   return new SyncThreadSyncEntity(
     20       id_string, client_tag_hash, 0, received_version);
     21 }
     22 
     23 SyncThreadSyncEntity* SyncThreadSyncEntity::FromCommitRequest(
     24     const std::string& id_string,
     25     const std::string& client_tag_hash,
     26     int64 sequence_number,
     27     int64 base_version,
     28     base::Time ctime,
     29     base::Time mtime,
     30     const std::string& non_unique_name,
     31     bool deleted,
     32     const sync_pb::EntitySpecifics& specifics) {
     33   return new SyncThreadSyncEntity(id_string,
     34                                   client_tag_hash,
     35                                   0,
     36                                   0,
     37                                   true,
     38                                   sequence_number,
     39                                   base_version,
     40                                   ctime,
     41                                   mtime,
     42                                   non_unique_name,
     43                                   deleted,
     44                                   specifics);
     45 }
     46 
     47 // Constructor that does not set any pending commit fields.
     48 SyncThreadSyncEntity::SyncThreadSyncEntity(
     49     const std::string& id,
     50     const std::string& client_tag_hash,
     51     int64 highest_commit_response_version,
     52     int64 highest_gu_response_version)
     53     : id_(id),
     54       client_tag_hash_(client_tag_hash),
     55       highest_commit_response_version_(highest_commit_response_version),
     56       highest_gu_response_version_(highest_gu_response_version),
     57       is_commit_pending_(false),
     58       sequence_number_(0),
     59       base_version_(0),
     60       deleted_(false) {
     61 }
     62 
     63 SyncThreadSyncEntity::SyncThreadSyncEntity(
     64     const std::string& id,
     65     const std::string& client_tag_hash,
     66     int64 highest_commit_response_version,
     67     int64 highest_gu_response_version,
     68     bool is_commit_pending,
     69     int64 sequence_number,
     70     int64 base_version,
     71     base::Time ctime,
     72     base::Time mtime,
     73     const std::string& non_unique_name,
     74     bool deleted,
     75     const sync_pb::EntitySpecifics& specifics)
     76     : id_(id),
     77       client_tag_hash_(client_tag_hash),
     78       highest_commit_response_version_(highest_commit_response_version),
     79       highest_gu_response_version_(highest_gu_response_version),
     80       is_commit_pending_(is_commit_pending),
     81       sequence_number_(sequence_number),
     82       base_version_(base_version),
     83       ctime_(ctime),
     84       mtime_(mtime),
     85       non_unique_name_(non_unique_name),
     86       deleted_(deleted),
     87       specifics_(specifics) {
     88 }
     89 
     90 SyncThreadSyncEntity::~SyncThreadSyncEntity() {
     91 }
     92 
     93 bool SyncThreadSyncEntity::IsCommitPending() const {
     94   return is_commit_pending_;
     95 }
     96 
     97 void SyncThreadSyncEntity::PrepareCommitProto(
     98     sync_pb::SyncEntity* commit_entity,
     99     int64* sequence_number) const {
    100   // Set ID if we have a server-assigned ID.  Otherwise, it will be up to
    101   // our caller to assign a client-unique initial ID.
    102   if (base_version_ != kUncommittedVersion) {
    103     commit_entity->set_id_string(id_);
    104   }
    105 
    106   commit_entity->set_client_defined_unique_tag(client_tag_hash_);
    107   commit_entity->set_version(base_version_);
    108   commit_entity->set_deleted(deleted_);
    109   commit_entity->set_folder(false);
    110   commit_entity->set_name(non_unique_name_);
    111   if (!deleted_) {
    112     commit_entity->set_ctime(TimeToProtoTime(ctime_));
    113     commit_entity->set_mtime(TimeToProtoTime(mtime_));
    114     commit_entity->mutable_specifics()->CopyFrom(specifics_);
    115   }
    116 
    117   *sequence_number = sequence_number_;
    118 }
    119 
    120 void SyncThreadSyncEntity::RequestCommit(
    121     const std::string& id,
    122     const std::string& client_tag_hash,
    123     int64 sequence_number,
    124     int64 base_version,
    125     base::Time ctime,
    126     base::Time mtime,
    127     const std::string& non_unique_name,
    128     bool deleted,
    129     const sync_pb::EntitySpecifics& specifics) {
    130   DCHECK_GE(base_version, base_version_)
    131       << "Base version should never decrease";
    132 
    133   DCHECK_GE(sequence_number, sequence_number_)
    134       << "Sequence number should never decrease";
    135 
    136   // Update our book-keeping counters.
    137   base_version_ = base_version;
    138   sequence_number_ = sequence_number;
    139 
    140   // Do our counter values indicate a conflict?  If so, don't commit.
    141   //
    142   // There's no need to inform the model thread of the conflict.  The
    143   // conflicting update has already been posted to its task runner; it will
    144   // figure it out as soon as it runs that task.
    145   is_commit_pending_ = true;
    146   if (IsInConflict()) {
    147     ClearPendingCommit();
    148     return;
    149   }
    150 
    151   // We don't commit deletions of server-unknown items.
    152   if (deleted && !IsServerKnown()) {
    153     ClearPendingCommit();
    154     return;
    155   }
    156 
    157   // Otherwise, we should store the data associated with this pending commit
    158   // so we're ready to commit at the next possible opportunity.
    159 
    160   // We intentionally don't update the id_ here.  Good ID values come from the
    161   // server and always pass through the sync thread first.  There's no way the
    162   // model thread could have a better ID value than we do.
    163 
    164   // This entity is identified by its client tag.  That value can never change.
    165   DCHECK_EQ(client_tag_hash_, client_tag_hash);
    166 
    167   // Set the fields for the pending commit.
    168   ctime_ = ctime;
    169   mtime_ = mtime;
    170   non_unique_name_ = non_unique_name;
    171   deleted_ = deleted;
    172   specifics_ = specifics;
    173 }
    174 
    175 void SyncThreadSyncEntity::ReceiveCommitResponse(const std::string& response_id,
    176                                                  int64 response_version,
    177                                                  int64 sequence_number) {
    178   // Commit responses, especially after the first commit, can update our ID.
    179   id_ = response_id;
    180 
    181   DCHECK_GT(response_version, highest_commit_response_version_)
    182       << "Had expected higher response version."
    183       << " id: " << id_;
    184 
    185   // Commits are synchronous, so there's no reason why the sequence numbers
    186   // wouldn't match.
    187   DCHECK_EQ(sequence_number_, sequence_number)
    188       << "Unexpected sequence number mismatch."
    189       << " id: " << id_;
    190 
    191   highest_commit_response_version_ = response_version;
    192 
    193   // Because an in-progress commit blocks the sync thread, we can assume that
    194   // the item we just committed successfully is exactly the one we have now.
    195   // Nothing changed it while the commit was happening.  Since we're now in
    196   // sync with the server, we can clear the pending commit.
    197   ClearPendingCommit();
    198 }
    199 
    200 void SyncThreadSyncEntity::ReceiveUpdate(int64 version) {
    201   highest_gu_response_version_ =
    202       std::max(highest_gu_response_version_, version);
    203 
    204   if (IsInConflict()) {
    205     // Incoming update clobbers the pending commit on the sync thread.
    206     // The model thread can re-request this commit later if it wants to.
    207     ClearPendingCommit();
    208   }
    209 }
    210 
    211 bool SyncThreadSyncEntity::IsInConflict() const {
    212   if (!is_commit_pending_)
    213     return false;
    214 
    215   if (highest_gu_response_version_ <= highest_commit_response_version_) {
    216     // The most recent server state was created in a commit made by this
    217     // client.  We're fully up to date, and therefore not in conflict.
    218     return false;
    219   } else {
    220     // The most recent server state was written by someone else.
    221     // Did the model thread have the most up to date version when it issued the
    222     // commit request?
    223     if (base_version_ >= highest_gu_response_version_) {
    224       return false;  // Yes.
    225     } else {
    226       return true;  // No.
    227     }
    228   }
    229 }
    230 
    231 bool SyncThreadSyncEntity::IsServerKnown() const {
    232   return base_version_ != kUncommittedVersion;
    233 }
    234 
    235 void SyncThreadSyncEntity::ClearPendingCommit() {
    236   is_commit_pending_ = false;
    237 
    238   // Clearing the specifics might free up some memory.  It can't hurt to try.
    239   specifics_.Clear();
    240 }
    241 
    242 }  // namespace syncer
    243