diff options
-rw-r--r-- | sync/BUILD.gn | 6 | ||||
-rw-r--r-- | sync/engine/entity_tracker.cc | 6 | ||||
-rw-r--r-- | sync/engine/entity_tracker.h | 24 | ||||
-rw-r--r-- | sync/engine/model_type_entity.cc | 191 | ||||
-rw-r--r-- | sync/engine/model_type_entity.h | 205 | ||||
-rw-r--r-- | sync/engine/model_type_entity_unittest.cc | 181 | ||||
-rw-r--r-- | sync/internal_api/model_type_entity.cc | 211 | ||||
-rw-r--r-- | sync/internal_api/model_type_entity_unittest.cc | 275 | ||||
-rw-r--r-- | sync/internal_api/public/model_type_entity.h | 147 | ||||
-rw-r--r-- | sync/internal_api/public/non_blocking_sync_common.h | 6 | ||||
-rw-r--r-- | sync/internal_api/public/util/proto_value_ptr.h | 20 | ||||
-rw-r--r-- | sync/internal_api/shared_model_type_processor.cc | 46 | ||||
-rw-r--r-- | sync/internal_api/shared_model_type_processor_unittest.cc | 13 | ||||
-rw-r--r-- | sync/sync.gyp | 4 | ||||
-rw-r--r-- | sync/sync_tests.gypi | 2 |
15 files changed, 696 insertions, 641 deletions
diff --git a/sync/BUILD.gn b/sync/BUILD.gn index d4d89d1..edeaf34dd 100644 --- a/sync/BUILD.gn +++ b/sync/BUILD.gn @@ -94,8 +94,6 @@ source_set("sync_core") { "engine/get_updates_delegate.h", "engine/get_updates_processor.cc", "engine/get_updates_processor.h", - "engine/model_type_entity.cc", - "engine/model_type_entity.h", "engine/model_type_worker.cc", "engine/model_type_worker.h", "engine/net/server_connection_manager.cc", @@ -173,6 +171,7 @@ source_set("sync_core") { "internal_api/js_sync_encryption_handler_observer.h", "internal_api/js_sync_manager_observer.cc", "internal_api/js_sync_manager_observer.h", + "internal_api/model_type_entity.cc", "internal_api/model_type_store_backend.cc", "internal_api/model_type_store_impl.cc", "internal_api/protocol_event_buffer.cc", @@ -243,6 +242,7 @@ source_set("sync_core") { "internal_api/public/http_post_provider_interface.h", "internal_api/public/internal_components_factory.h", "internal_api/public/internal_components_factory_impl.h", + "internal_api/public/model_type_entity.h", "internal_api/public/model_type_processor.cc", "internal_api/public/model_type_processor.h", "internal_api/public/model_type_store_backend.h", @@ -632,7 +632,6 @@ test("sync_unit_tests") { "engine/directory_update_handler_unittest.cc", "engine/entity_tracker_unittest.cc", "engine/get_updates_processor_unittest.cc", - "engine/model_type_entity_unittest.cc", "engine/model_type_worker_unittest.cc", "engine/sync_scheduler_unittest.cc", "engine/syncer_proto_util_unittest.cc", @@ -654,6 +653,7 @@ test("sync_unit_tests") { "internal_api/js_mutation_event_observer_unittest.cc", "internal_api/js_sync_encryption_handler_observer_unittest.cc", "internal_api/js_sync_manager_observer_unittest.cc", + "internal_api/model_type_entity_unittest.cc", "internal_api/model_type_store_backend_unittest.cc", "internal_api/model_type_store_impl_unittest.cc", "internal_api/protocol_event_buffer_unittest.cc", diff --git a/sync/engine/entity_tracker.cc b/sync/engine/entity_tracker.cc index d4d2eab..9728d67 100644 --- a/sync/engine/entity_tracker.cc +++ b/sync/engine/entity_tracker.cc @@ -14,14 +14,12 @@ namespace syncer_v2 { scoped_ptr<EntityTracker> EntityTracker::FromUpdateResponse( const UpdateResponseData& data) { - // TODO(stanisc): Share entire EntityData with EntityTracker. return make_scoped_ptr(new EntityTracker( data.entity->id, data.entity->client_tag_hash, 0, data.response_version)); } scoped_ptr<EntityTracker> EntityTracker::FromCommitRequest( const CommitRequestData& data) { - // TODO(stanisc): Share entire EntityData with EntityTracker. return make_scoped_ptr( new EntityTracker(data.entity->id, data.entity->client_tag_hash, 0, 0)); } @@ -96,6 +94,8 @@ void EntityTracker::RequestCommit(const CommitRequestData& data) { // This entity is identified by its client tag. That value can never change. DCHECK_EQ(client_tag_hash_, data.entity->client_tag_hash); + // TODO(stanisc): consider simply copying CommitRequestData instead of + // allocating one dynamically. pending_commit_.reset(new CommitRequestData(data)); // Do our counter values indicate a conflict? If so, don't commit. @@ -209,4 +209,4 @@ void EntityTracker::ClearPendingCommit() { pending_commit_.reset(); } -} // namespace syncer +} // namespace syncer_v2 diff --git a/sync/engine/entity_tracker.h b/sync/engine/entity_tracker.h index 8dc13cd..39898cf 100644 --- a/sync/engine/entity_tracker.h +++ b/sync/engine/entity_tracker.h @@ -81,27 +81,13 @@ class SYNC_EXPORT EntityTracker { void ClearPendingUpdate(); private: - // Initializes received update state. Does not initialize state related to - // pending commits and sets |is_commit_pending_| to false. + // Initializes the entity tracker's main fields. Does not initialize state + // related to a pending commit. EntityTracker(const std::string& id, const std::string& client_tag_hash, int64 highest_commit_response_version, int64 highest_gu_response_version); - // Initializes all fields. Sets |is_commit_pending_| to true. - EntityTracker(const std::string& id, - const std::string& client_tag_hash, - int64 highest_commit_response_version, - int64 highest_gu_response_version, - bool is_commit_pending, - int64 sequence_number, - int64 base_version, - base::Time ctime, - base::Time mtime, - const std::string& non_unique_name, - bool deleted, - const sync_pb::EntitySpecifics& specifics); - // Checks if the current state indicates a conflict. // // This can be true only while a call to this object is in progress. @@ -126,10 +112,6 @@ class SYNC_EXPORT EntityTracker { // The highest version seen in a GU response for this entry. int64 highest_gu_response_version_; - // Flag that indicates whether or not we're waiting for a chance to commit - // this item. - bool is_commit_pending_; - // Used to track in-flight commit requests on the model thread. All we need // to do here is return it back to the model thread when the pending commit // is completed and confirmed. Not valid if no commit is pending. @@ -149,6 +131,6 @@ class SYNC_EXPORT EntityTracker { DISALLOW_COPY_AND_ASSIGN(EntityTracker); }; -} // namespace syncer +} // namespace syncer_v2 #endif // SYNC_ENGINE_ENTITY_TRACKER_H_ diff --git a/sync/engine/model_type_entity.cc b/sync/engine/model_type_entity.cc deleted file mode 100644 index a4dd505..0000000 --- a/sync/engine/model_type_entity.cc +++ /dev/null @@ -1,191 +0,0 @@ -// Copyright 2014 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/model_type_entity.h" -#include "sync/internal_api/public/non_blocking_sync_common.h" -#include "sync/syncable/syncable_util.h" - -namespace syncer_v2 { - -scoped_ptr<ModelTypeEntity> ModelTypeEntity::NewLocalItem( - const std::string& client_tag, - const sync_pb::EntitySpecifics& specifics, - base::Time now) { - return scoped_ptr<ModelTypeEntity>(new ModelTypeEntity( - 1, 0, 0, kUncommittedVersion, true, - std::string(), // Sync thread will assign the initial ID. - syncer::syncable::GenerateSyncableHash( - syncer::GetModelTypeFromSpecifics(specifics), client_tag), - client_tag, // As non-unique name. - specifics, false, now, now, std::string())); -} - -scoped_ptr<ModelTypeEntity> ModelTypeEntity::FromServerUpdate( - const std::string& id, - const std::string& client_tag_hash, - const std::string& non_unique_name, - int64 version, - const sync_pb::EntitySpecifics& specifics, - bool deleted, - base::Time ctime, - base::Time mtime, - const std::string& encryption_key_name) { - return scoped_ptr<ModelTypeEntity>(new ModelTypeEntity(0, - 0, - 0, - version, - true, - id, - client_tag_hash, - non_unique_name, - specifics, - deleted, - ctime, - mtime, - encryption_key_name)); -} - -ModelTypeEntity::ModelTypeEntity(int64 sequence_number, - int64 commit_requested_sequence_number, - int64 acked_sequence_number, - int64 base_version, - bool is_dirty, - const std::string& id, - const std::string& client_tag_hash, - const std::string& non_unique_name, - const sync_pb::EntitySpecifics& specifics, - bool deleted, - base::Time ctime, - base::Time mtime, - const std::string& encryption_key_name) - : sequence_number_(sequence_number), - commit_requested_sequence_number_(commit_requested_sequence_number), - acked_sequence_number_(acked_sequence_number), - base_version_(base_version), - is_dirty_(is_dirty), - id_(id), - client_tag_hash_(client_tag_hash), - non_unique_name_(non_unique_name), - specifics_(specifics), - deleted_(deleted), - ctime_(ctime), - mtime_(mtime), - encryption_key_name_(encryption_key_name) { -} - -ModelTypeEntity::~ModelTypeEntity() { -} - -bool ModelTypeEntity::IsWriteRequired() const { - return is_dirty_; -} - -bool ModelTypeEntity::IsUnsynced() const { - return sequence_number_ > acked_sequence_number_; -} - -bool ModelTypeEntity::RequiresCommitRequest() const { - return sequence_number_ > commit_requested_sequence_number_; -} - -bool ModelTypeEntity::UpdateIsReflection(int64 update_version) const { - return base_version_ >= update_version; -} - -bool ModelTypeEntity::UpdateIsInConflict(int64 update_version) const { - return IsUnsynced() && !UpdateIsReflection(update_version); -} - -void ModelTypeEntity::ApplyUpdateFromServer( - int64 update_version, - bool deleted, - const sync_pb::EntitySpecifics& specifics, - base::Time mtime, - const std::string& encryption_key_name) { - // There was a conflict and the server just won it. - // This implicitly acks all outstanding commits because a received update - // will clobber any pending commits on the sync thread. - acked_sequence_number_ = sequence_number_; - commit_requested_sequence_number_ = sequence_number_; - - base_version_ = update_version; - specifics_ = specifics; - mtime_ = mtime; -} - -void ModelTypeEntity::MakeLocalChange( - const sync_pb::EntitySpecifics& specifics) { - sequence_number_++; - specifics_ = specifics; -} - -void ModelTypeEntity::UpdateDesiredEncryptionKey(const std::string& name) { - if (encryption_key_name_ == name) - return; - - DVLOG(2) << id_ << ": Encryption triggered commit: " << encryption_key_name_ - << " -> " << name; - - // Schedule commit with the expectation that the worker will re-encrypt with - // the latest encryption key as it does. - sequence_number_++; -} - -void ModelTypeEntity::Delete() { - sequence_number_++; - specifics_.Clear(); - deleted_ = true; -} - -void ModelTypeEntity::InitializeCommitRequestData( - CommitRequestData* request) const { - // TODO(stanisc): Switch ModelTypeEntity to EntityData to - // avoid temporary EntityData - EntityData data; - data.id = id_; - data.client_tag_hash = client_tag_hash_; - data.creation_time = ctime_; - data.modification_time = mtime_; - data.non_unique_name = non_unique_name_; - - if (!deleted_) { - data.specifics.CopyFrom(specifics_); - } - - request->entity = data.Pass(); - request->sequence_number = sequence_number_; - request->base_version = base_version_; -} - -void ModelTypeEntity::SetCommitRequestInProgress() { - commit_requested_sequence_number_ = sequence_number_; -} - -void ModelTypeEntity::ReceiveCommitResponse( - const std::string& id, - int64 sequence_number, - int64 response_version, - const std::string& encryption_key_name) { - id_ = id; // The server can assign us a new ID in a commit response. - acked_sequence_number_ = sequence_number; - base_version_ = response_version; - encryption_key_name_ = encryption_key_name; -} - -void ModelTypeEntity::ClearTransientSyncState() { - // If we have any unacknowledged commit requests outstatnding, they've been - // dropped and we should forget about them. - commit_requested_sequence_number_ = acked_sequence_number_; -} - -void ModelTypeEntity::ClearSyncState() { - base_version_ = kUncommittedVersion; - is_dirty_ = true; - sequence_number_ = 1; - commit_requested_sequence_number_ = 0; - acked_sequence_number_ = 0; - id_.clear(); -} - -} // namespace syncer diff --git a/sync/engine/model_type_entity.h b/sync/engine/model_type_entity.h deleted file mode 100644 index 6f99706..0000000 --- a/sync/engine/model_type_entity.h +++ /dev/null @@ -1,205 +0,0 @@ -// Copyright 2014 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. - -#ifndef SYNC_ENGINE_MODEL_TYPE_ENTITY_H_ -#define SYNC_ENGINE_MODEL_TYPE_ENTITY_H_ - -#include <string> - -#include "base/memory/scoped_ptr.h" -#include "base/time/time.h" -#include "sync/base/sync_export.h" -#include "sync/protocol/sync.pb.h" - -namespace syncer_v2 { -struct CommitRequestData; - -// This is the model thread's representation of a SyncEntity. -// -// The model type entity receives updates from the model itself and -// (asynchronously) from the sync server via the sync thread. From the point -// of view of this class, updates from the server take precedence over local -// changes, though the model may be given an opportunity to overwrite this -// decision later. -// -// Sync will try to commit this entity's data to the sync server and local -// storage. -// -// Most of the logic related to those processes live outside this class. This -// class helps out a bit by offering some functions to serialize its data to -// various formats and query the entity's status. -class SYNC_EXPORT_PRIVATE ModelTypeEntity { - public: - // Construct an instance representing a new locally-created item. - static scoped_ptr<ModelTypeEntity> NewLocalItem( - const std::string& client_tag, - const sync_pb::EntitySpecifics& specifics, - base::Time now); - - // Construct an instance representing an item newly received from the server. - static scoped_ptr<ModelTypeEntity> FromServerUpdate( - const std::string& id, - const std::string& client_tag_hash, - const std::string& non_unique_name, - int64 version, - const sync_pb::EntitySpecifics& specifics, - bool deleted, - base::Time ctime, - base::Time mtime, - const std::string& encryption_key_name); - - // TODO(rlarocque): Implement FromDisk constructor when we implement storage. - - ~ModelTypeEntity(); - - // Returns true if this data is out of sync with local storage. - bool IsWriteRequired() const; - - // Returns true if this data is out of sync with the server. - // A commit may or may not be in progress at this time. - bool IsUnsynced() const; - - // Returns true if this data is out of sync with the sync thread. - // - // There may or may not be a commit in progress for this item, but there's - // definitely no commit in progress for this (most up to date) version of - // this item. - bool RequiresCommitRequest() const; - - // Returns true if the specified update version does not contain new data. - bool UpdateIsReflection(int64 update_version) const; - - // Returns true if the specified update version conflicts with local changes. - bool UpdateIsInConflict(int64 update_version) const; - - // Applies an update from the sync server. - // - // Overrides any local changes. Check UpdateIsInConflict() before calling - // this function if you want to handle conflicts differently. - void ApplyUpdateFromServer(int64 update_version, - bool deleted, - const sync_pb::EntitySpecifics& specifics, - base::Time mtime, - const std::string& encryption_key_name); - - // Applies a local change to this item. - void MakeLocalChange(const sync_pb::EntitySpecifics& specifics); - - // Schedule a commit if the |name| does not match this item's last known - // encryption key. The worker that performs the commit is expected to - // encrypt the item using the latest available key. - void UpdateDesiredEncryptionKey(const std::string& name); - - // Applies a local deletion to this item. - void Delete(); - - // Initializes a message representing this item's uncommitted state - // to be forwarded to the sync server for committing. - void InitializeCommitRequestData(CommitRequestData* request) const; - - // Notes that the current version of this item has been queued for commit. - void SetCommitRequestInProgress(); - - // Receives a successful commit response. - // - // Sucssful commit responses can overwrite an item's ID. - // - // Note that the receipt of a successful commit response does not necessarily - // unset IsUnsynced(). If many local changes occur in quick succession, it's - // possible that the committed item was already out of date by the time it - // reached the server. - void ReceiveCommitResponse(const std::string& id, - int64 sequence_number, - int64 response_version, - const std::string& encryption_key_name); - - // Clears any in-memory sync state associated with outstanding commits. - void ClearTransientSyncState(); - - // Clears all sync state. Invoked when a user signs out. - void ClearSyncState(); - - private: - ModelTypeEntity(int64 sequence_number, - int64 commit_requested_sequence_number, - int64 acked_sequence_number, - int64 base_version, - bool is_dirty, - const std::string& id, - const std::string& client_tag_hash, - const std::string& non_unique_name, - const sync_pb::EntitySpecifics& specifics, - bool deleted, - base::Time ctime, - base::Time mtime, - const std::string& encryption_key_name); - - // A sequence number used to track in-progress commits. Each local change - // increments this number. - int64 sequence_number_; - - // The sequence number of the last item sent to the sync thread. - int64 commit_requested_sequence_number_; - - // The sequence number of the last item known to be successfully committed. - int64 acked_sequence_number_; - - // The server version on which this item is based. - // - // If there are no local changes, this is the version of the entity as we see - // it here. - // - // If there are local changes, this is the version of the entity on which - // those changes are based. - int64 base_version_; - - // True if this entity is out of sync with local storage. - bool is_dirty_; - - // The entity's ID. - // - // Most of the time, this is a server-assigned value. - // - // Prior to the item's first commit, we leave this value as an empty string. - // The initial ID for a newly created item has to meet certain uniqueness - // requirements, and we handle those on the sync thread. - std::string id_; - - // A hash based on the client tag and model type. - // Used for various map lookups. Should always be available. - std::string client_tag_hash_; - - // A non-unique name associated with this entity. - // - // It is sometimes used for debugging. It gets saved to and restored from - // the sync server. - // - // Its value is often related to the item's unhashed client tag, though this - // is not guaranteed and should not be relied on. May be hidden when - // encryption is enabled. - std::string non_unique_name_; - - // A protobuf filled with type-specific information. Contains the most - // up-to-date specifics, whether it be from the server or a locally modified - // version. - sync_pb::EntitySpecifics specifics_; - - // Whether or not the item is deleted. The |specifics_| field may be empty - // if this flag is true. - bool deleted_; - - // Entity creation and modification timestamps. - // Assigned by the client and synced by the server, though the server usually - // doesn't bother to inspect their values. - base::Time ctime_; - base::Time mtime_; - - // The name of the encryption key used to encrypt this item on the server. - // Empty when no encryption is in use. - std::string encryption_key_name_; -}; - -} // namespace syncer - -#endif // SYNC_ENGINE_MODEL_TYPE_ENTITY_H_ diff --git a/sync/engine/model_type_entity_unittest.cc b/sync/engine/model_type_entity_unittest.cc deleted file mode 100644 index 5efe72b..0000000 --- a/sync/engine/model_type_entity_unittest.cc +++ /dev/null @@ -1,181 +0,0 @@ -// Copyright 2014 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/model_type_entity.h" - -#include "base/memory/scoped_ptr.h" -#include "base/time/time.h" -#include "sync/internal_api/public/base/model_type.h" -#include "sync/protocol/sync.pb.h" -#include "sync/syncable/syncable_util.h" - -#include "testing/gtest/include/gtest/gtest.h" - -namespace syncer_v2 { - -// Some simple sanity tests for the ModelTypeEntity. -// -// A lot of the more complicated sync logic is implemented in the -// SharedModelTypeProcessor that owns the ModelTypeEntity. We can't unit test -// it here. -// -// Instead, we focus on simple tests to make sure that variables are getting -// properly intialized and flags properly set. Anything more complicated would -// be a redundant and incomplete version of the SharedModelTypeProcessor tests. -class ModelTypeEntityTest : public ::testing::Test { - public: - ModelTypeEntityTest() - : kServerId("ServerID"), - kClientTag("sample.pref.name"), - kClientTagHash( - syncer::syncable::GenerateSyncableHash(syncer::PREFERENCES, - kClientTag)), - kCtime(base::Time::UnixEpoch() + base::TimeDelta::FromDays(10)), - kMtime(base::Time::UnixEpoch() + base::TimeDelta::FromDays(20)) { - sync_pb::PreferenceSpecifics* pref_specifics = - specifics.mutable_preference(); - pref_specifics->set_name(kClientTag); - pref_specifics->set_value("pref.value"); - } - - const std::string kServerId; - const std::string kClientTag; - const std::string kClientTagHash; - const base::Time kCtime; - const base::Time kMtime; - sync_pb::EntitySpecifics specifics; -}; - -TEST_F(ModelTypeEntityTest, NewLocalItem) { - scoped_ptr<ModelTypeEntity> entity( - ModelTypeEntity::NewLocalItem("asdf", specifics, kCtime)); - - EXPECT_TRUE(entity->IsWriteRequired()); - EXPECT_TRUE(entity->IsUnsynced()); - EXPECT_FALSE(entity->UpdateIsReflection(1)); - EXPECT_TRUE(entity->UpdateIsInConflict(1)); -} - -TEST_F(ModelTypeEntityTest, FromServerUpdate) { - scoped_ptr<ModelTypeEntity> entity( - ModelTypeEntity::FromServerUpdate(kServerId, - kClientTagHash, - kClientTag, // As non-unique name. - 10, - specifics, - false, - kCtime, - kMtime, - std::string())); - - EXPECT_TRUE(entity->IsWriteRequired()); - EXPECT_FALSE(entity->IsUnsynced()); - EXPECT_TRUE(entity->UpdateIsReflection(9)); - EXPECT_TRUE(entity->UpdateIsReflection(10)); - EXPECT_FALSE(entity->UpdateIsReflection(11)); - EXPECT_FALSE(entity->UpdateIsInConflict(11)); -} - -// Tombstones should behave just like regular updates. Mostly. The strangest -// thing about them is that they don't have specifics, so it can be hard to -// detect their type. Fortunately, this class doesn't care about types in -// received updates. -TEST_F(ModelTypeEntityTest, TombstoneUpdate) { - scoped_ptr<ModelTypeEntity> entity( - ModelTypeEntity::FromServerUpdate(kServerId, - kClientTagHash, - kClientTag, // As non-unique name. - 10, - sync_pb::EntitySpecifics(), - true, - kCtime, - kMtime, - std::string())); - - EXPECT_TRUE(entity->IsWriteRequired()); - EXPECT_FALSE(entity->IsUnsynced()); - EXPECT_TRUE(entity->UpdateIsReflection(9)); - EXPECT_TRUE(entity->UpdateIsReflection(10)); - EXPECT_FALSE(entity->UpdateIsReflection(11)); - EXPECT_FALSE(entity->UpdateIsInConflict(11)); -} - -// Apply a deletion update. -TEST_F(ModelTypeEntityTest, ApplyUpdate) { - scoped_ptr<ModelTypeEntity> entity( - ModelTypeEntity::FromServerUpdate(kServerId, - kClientTagHash, - kClientTag, // As non-unique name. - 10, - specifics, - false, - kCtime, - kMtime, - std::string())); - - // A deletion update one version later. - entity->ApplyUpdateFromServer(11, - true, - sync_pb::EntitySpecifics(), - kMtime + base::TimeDelta::FromSeconds(10), - std::string()); - - EXPECT_TRUE(entity->IsWriteRequired()); - EXPECT_FALSE(entity->IsUnsynced()); - EXPECT_TRUE(entity->UpdateIsReflection(11)); - EXPECT_FALSE(entity->UpdateIsReflection(12)); -} - -TEST_F(ModelTypeEntityTest, LocalChange) { - scoped_ptr<ModelTypeEntity> entity( - ModelTypeEntity::FromServerUpdate(kServerId, - kClientTagHash, - kClientTag, // As non-unique name. - 10, - specifics, - false, - kCtime, - kMtime, - std::string())); - - sync_pb::EntitySpecifics specifics2; - specifics2.CopyFrom(specifics); - specifics2.mutable_preference()->set_value("new.pref.value"); - - entity->MakeLocalChange(specifics2); - EXPECT_TRUE(entity->IsWriteRequired()); - EXPECT_TRUE(entity->IsUnsynced()); - - EXPECT_TRUE(entity->UpdateIsReflection(10)); - EXPECT_FALSE(entity->UpdateIsInConflict(10)); - - EXPECT_FALSE(entity->UpdateIsReflection(11)); - EXPECT_TRUE(entity->UpdateIsInConflict(11)); -} - -TEST_F(ModelTypeEntityTest, LocalDeletion) { - scoped_ptr<ModelTypeEntity> entity( - ModelTypeEntity::FromServerUpdate(kServerId, - kClientTagHash, - kClientTag, // As non-unique name. - 10, - specifics, - false, - kCtime, - kMtime, - std::string())); - - entity->Delete(); - - EXPECT_TRUE(entity->IsWriteRequired()); - EXPECT_TRUE(entity->IsUnsynced()); - - EXPECT_TRUE(entity->UpdateIsReflection(10)); - EXPECT_FALSE(entity->UpdateIsInConflict(10)); - - EXPECT_FALSE(entity->UpdateIsReflection(11)); - EXPECT_TRUE(entity->UpdateIsInConflict(11)); -} - -} // namespace syncer diff --git a/sync/internal_api/model_type_entity.cc b/sync/internal_api/model_type_entity.cc new file mode 100644 index 0000000..eabdd06 --- /dev/null +++ b/sync/internal_api/model_type_entity.cc @@ -0,0 +1,211 @@ +// Copyright 2014 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/internal_api/public/model_type_entity.h" + +#include "base/base64.h" +#include "base/sha1.h" +#include "base/time/time.h" +#include "sync/internal_api/public/non_blocking_sync_common.h" +#include "sync/syncable/syncable_util.h" +#include "sync/util/time.h" + +namespace syncer_v2 { + +scoped_ptr<ModelTypeEntity> ModelTypeEntity::CreateNew( + const std::string& client_tag, + const std::string& client_tag_hash, + const std::string& id, + base::Time creation_time) { + // Initialize metadata + sync_pb::EntityMetadata metadata; + metadata.set_client_tag_hash(client_tag_hash); + if (!id.empty()) + metadata.set_server_id(id); + metadata.set_sequence_number(0); + metadata.set_acked_sequence_number(0); + metadata.set_server_version(kUncommittedVersion); + metadata.set_creation_time(syncer::TimeToProtoTime(creation_time)); + + return scoped_ptr<ModelTypeEntity>( + new ModelTypeEntity(client_tag, &metadata)); +} + +ModelTypeEntity::ModelTypeEntity(const std::string& client_key, + sync_pb::EntityMetadata* metadata) + : client_key_(client_key), commit_requested_sequence_number_(0) { + DCHECK(metadata); + DCHECK(metadata->has_client_tag_hash()); + metadata_.Swap(metadata); +} + +ModelTypeEntity::~ModelTypeEntity() {} + +void ModelTypeEntity::CacheCommitData(EntityData* data) { + commit_data_ = data->Pass(); +} + +bool ModelTypeEntity::HasCommitData() const { + return !commit_data_->client_tag_hash.empty(); +} + +bool ModelTypeEntity::IsUnsynced() const { + return metadata_.sequence_number() > metadata_.acked_sequence_number(); +} + +bool ModelTypeEntity::RequiresCommitRequest() const { + return metadata_.sequence_number() > commit_requested_sequence_number_; +} + +bool ModelTypeEntity::UpdateIsReflection(int64 update_version) const { + return metadata_.server_version() >= update_version; +} + +bool ModelTypeEntity::UpdateIsInConflict(int64 update_version) const { + return IsUnsynced() && !UpdateIsReflection(update_version); +} + +void ModelTypeEntity::ApplyUpdateFromServer( + const UpdateResponseData& response_data) { + DCHECK(metadata_.has_client_tag_hash()); + DCHECK(!metadata_.client_tag_hash().empty()); + DCHECK(metadata_.has_creation_time()); + DCHECK(metadata_.has_sequence_number()); + + // TODO(stanisc): crbug/561829: Filter out update if specifics hash hasn't + // changed. + + // TODO(stanisc): crbug/521867: Understand and verify the conflict resolution + // logic here. + // There was a conflict and the server just won it. + // This implicitly acks all outstanding commits because a received update + // will clobber any pending commits on the sync thread. + metadata_.set_acked_sequence_number(metadata_.sequence_number()); + commit_requested_sequence_number_ = metadata_.sequence_number(); + + metadata_.set_server_version(response_data.response_version); + metadata_.set_modification_time( + syncer::TimeToProtoTime(response_data.entity->modification_time)); + UpdateSpecificsHash(response_data.entity->specifics); + + encryption_key_name_ = response_data.encryption_key_name; +} + +void ModelTypeEntity::MakeLocalChange(const std::string& non_unique_name, + const sync_pb::EntitySpecifics& specifics, + base::Time modification_time) { + DCHECK(metadata_.has_client_tag_hash()); + DCHECK(!metadata_.client_tag_hash().empty()); + DCHECK(metadata_.has_creation_time()); + + metadata_.set_modification_time(syncer::TimeToProtoTime(modification_time)); + metadata_.set_is_deleted(false); + IncrementSequenceNumber(); + UpdateSpecificsHash(specifics); + + // Build and cache commit data. + // TODO(stanisc): Consider moving this out to make caching the data + // optional. + EntityData data; + data.client_tag_hash = metadata_.client_tag_hash(); + data.id = metadata_.server_id(); + data.non_unique_name = non_unique_name; + data.creation_time = syncer::ProtoTimeToTime(metadata_.creation_time()); + data.modification_time = modification_time; + // TODO(stanisc): Consider taking over specifics value without copying. + data.specifics.CopyFrom(specifics); + + CacheCommitData(&data); +} + +void ModelTypeEntity::UpdateDesiredEncryptionKey(const std::string& name) { + if (encryption_key_name_ == name) + return; + + DVLOG(2) << metadata_.server_id() + << ": Encryption triggered commit: " << encryption_key_name_ + << " -> " << name; + + // Schedule commit with the expectation that the worker will re-encrypt with + // the latest encryption key as it does. + IncrementSequenceNumber(); +} + +void ModelTypeEntity::Delete() { + IncrementSequenceNumber(); + metadata_.set_is_deleted(true); + metadata_.clear_specifics_hash(); + + EntityData data; + data.client_tag_hash = metadata_.client_tag_hash(); + data.id = metadata_.server_id(); + data.creation_time = syncer::ProtoTimeToTime(metadata_.creation_time()); + + CacheCommitData(&data); +} + +void ModelTypeEntity::InitializeCommitRequestData( + CommitRequestData* request) const { + DCHECK(HasCommitData()); + DCHECK_EQ(commit_data_->client_tag_hash, metadata_.client_tag_hash()); + + request->sequence_number = metadata_.sequence_number(); + request->base_version = metadata_.server_version(); + request->entity = commit_data_; +} + +void ModelTypeEntity::SetCommitRequestInProgress() { + commit_requested_sequence_number_ = metadata_.sequence_number(); +} + +void ModelTypeEntity::ReceiveCommitResponse( + const std::string& id, + int64 sequence_number, + int64 response_version, + const std::string& encryption_key_name) { + // The server can assign us a new ID in a commit response. + metadata_.set_server_id(id); + metadata_.set_acked_sequence_number(sequence_number); + metadata_.set_server_version(response_version); + encryption_key_name_ = encryption_key_name; +} + +void ModelTypeEntity::ClearTransientSyncState() { + // If we have any unacknowledged commit requests outstanding, they've been + // dropped and we should forget about them. + commit_requested_sequence_number_ = metadata_.acked_sequence_number(); +} + +void ModelTypeEntity::ClearSyncState() { + // TODO(stanisc): crbug/561830: Need to review this entire method. It looks + // like the tests expect this to reset some metadata state but not the data. + // We should be able to reimplement this once we have the code fore + // fetching the data from the service. + metadata_.set_server_version(kUncommittedVersion); + // TODO(stanisc): Why is this 1 and not 0? This leaves the item unsynced. + metadata_.set_sequence_number(1); + metadata_.set_acked_sequence_number(0); + metadata_.clear_server_id(); + commit_requested_sequence_number_ = 0; +} + +void ModelTypeEntity::IncrementSequenceNumber() { + DCHECK(metadata_.has_sequence_number()); + metadata_.set_sequence_number(metadata_.sequence_number() + 1); +} + +// Update hash string for EntitySpecifics. +void ModelTypeEntity::UpdateSpecificsHash( + const sync_pb::EntitySpecifics& specifics) { + if (specifics.ByteSize() > 0) { + std::string hash_input; + specifics.AppendToString(&hash_input); + base::Base64Encode(base::SHA1HashString(hash_input), + metadata_.mutable_specifics_hash()); + } else { + metadata_.clear_specifics_hash(); + } +} + +} // namespace syncer_v2 diff --git a/sync/internal_api/model_type_entity_unittest.cc b/sync/internal_api/model_type_entity_unittest.cc new file mode 100644 index 0000000..0af2b58 --- /dev/null +++ b/sync/internal_api/model_type_entity_unittest.cc @@ -0,0 +1,275 @@ +// Copyright 2014 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/internal_api/public/model_type_entity.h" + +#include "base/memory/scoped_ptr.h" +#include "base/time/time.h" +#include "sync/internal_api/public/base/model_type.h" +#include "sync/internal_api/public/non_blocking_sync_common.h" +#include "sync/protocol/sync.pb.h" +#include "sync/syncable/syncable_util.h" + +#include "testing/gtest/include/gtest/gtest.h" + +namespace syncer_v2 { + +// Some simple sanity tests for the ModelTypeEntity. +// +// A lot of the more complicated sync logic is implemented in the +// SharedModelTypeProcessor that owns the ModelTypeEntity. We can't unit test +// it here. +// +// Instead, we focus on simple tests to make sure that variables are getting +// properly intialized and flags properly set. Anything more complicated would +// be a redundant and incomplete version of the SharedModelTypeProcessor tests. +class ModelTypeEntityTest : public ::testing::Test { + public: + ModelTypeEntityTest() + : kServerId("ServerID"), + kClientTag("sample.pref.name"), + kClientTagHash(GetSyncableHash(kClientTag)), + kCtime(base::Time::UnixEpoch() + base::TimeDelta::FromDays(10)), + kMtime(base::Time::UnixEpoch() + base::TimeDelta::FromDays(20)) { + sync_pb::PreferenceSpecifics* pref_specifics = + specifics.mutable_preference(); + pref_specifics->set_name(kClientTag); + pref_specifics->set_value("pref.value"); + } + + static std::string GetSyncableHash(const std::string& tag) { + return syncer::syncable::GenerateSyncableHash(syncer::PREFERENCES, tag); + } + + scoped_ptr<ModelTypeEntity> NewLocalItem(const std::string& tag) { + return scoped_ptr<ModelTypeEntity>( + ModelTypeEntity::CreateNew(tag, GetSyncableHash(tag), "", kCtime)); + } + + scoped_ptr<ModelTypeEntity> NewLocalItem( + const std::string& tag, + const sync_pb::EntitySpecifics& specifics) { + scoped_ptr<ModelTypeEntity> entity(NewLocalItem(tag)); + MakeLocalChange(entity.get(), specifics); + return entity.Pass(); + } + + void MakeLocalChange(ModelTypeEntity* entity, + const sync_pb::EntitySpecifics& specifics) { + entity->MakeLocalChange("foo", specifics, kMtime); + } + + scoped_ptr<ModelTypeEntity> NewServerItem() { + return scoped_ptr<ModelTypeEntity>(ModelTypeEntity::CreateNew( + kClientTag, kClientTagHash, kServerId, kCtime)); + } + + scoped_ptr<ModelTypeEntity> NewServerItem( + int64 version, + const sync_pb::EntitySpecifics& specifics) { + scoped_ptr<ModelTypeEntity> entity(NewServerItem()); + ApplyUpdateFromServer(entity.get(), version, specifics); + return entity.Pass(); + } + + void ApplyUpdateFromServer(ModelTypeEntity* entity, + int64 version, + const sync_pb::EntitySpecifics& specifics) { + ApplyUpdateFromServer(entity, version, specifics, kMtime); + } + + void ApplyUpdateFromServer(ModelTypeEntity* entity, + int64 version, + const sync_pb::EntitySpecifics& specifics, + base::Time mtime) { + EntityData data; + data.id = entity->metadata().server_id(); + data.client_tag_hash = entity->metadata().client_tag_hash(); + data.modification_time = mtime; + data.specifics = specifics; + + UpdateResponseData response_data; + response_data.response_version = version; + response_data.entity = data.Pass(); + + entity->ApplyUpdateFromServer(response_data); + } + + bool HasSpecificsHash(const scoped_ptr<ModelTypeEntity>& entity) const { + return !entity->metadata().specifics_hash().empty(); + } + + const std::string kServerId; + const std::string kClientTag; + const std::string kClientTagHash; + const base::Time kCtime; + const base::Time kMtime; + sync_pb::EntitySpecifics specifics; +}; + +TEST_F(ModelTypeEntityTest, NewItem) { + scoped_ptr<ModelTypeEntity> entity(NewLocalItem("asdf")); + + EXPECT_EQ(entity->client_key(), "asdf"); + EXPECT_EQ(entity->metadata().client_tag_hash(), GetSyncableHash("asdf")); + + EXPECT_FALSE(entity->HasCommitData()); + EXPECT_FALSE(HasSpecificsHash(entity)); + + EXPECT_FALSE(entity->IsUnsynced()); + EXPECT_FALSE(entity->UpdateIsReflection(1)); + EXPECT_FALSE(entity->UpdateIsInConflict(1)); +} + +TEST_F(ModelTypeEntityTest, NewLocalItem) { + scoped_ptr<ModelTypeEntity> entity(NewLocalItem("asdf", specifics)); + + EXPECT_TRUE(entity->HasCommitData()); + EXPECT_TRUE(HasSpecificsHash(entity)); + EXPECT_TRUE(entity->IsUnsynced()); + EXPECT_FALSE(entity->UpdateIsReflection(1)); + EXPECT_TRUE(entity->UpdateIsInConflict(1)); +} + +TEST_F(ModelTypeEntityTest, FromServerUpdate) { + scoped_ptr<ModelTypeEntity> entity(NewServerItem()); + + EXPECT_EQ(entity->client_key(), kClientTag); + EXPECT_EQ(entity->metadata().client_tag_hash(), kClientTagHash); + EXPECT_FALSE(HasSpecificsHash(entity)); + + ApplyUpdateFromServer(entity.get(), 10, specifics); + + // No data cached but the specifics hash should be updated. + EXPECT_FALSE(entity->HasCommitData()); + EXPECT_TRUE(HasSpecificsHash(entity)); + + EXPECT_FALSE(entity->IsUnsynced()); + EXPECT_TRUE(entity->UpdateIsReflection(9)); + EXPECT_TRUE(entity->UpdateIsReflection(10)); + EXPECT_FALSE(entity->UpdateIsReflection(11)); + EXPECT_FALSE(entity->UpdateIsInConflict(11)); +} + +// Tombstones should behave just like regular updates. Mostly. The strangest +// thing about them is that they don't have specifics, so it can be hard to +// detect their type. Fortunately, this class doesn't care about types in +// received updates. +TEST_F(ModelTypeEntityTest, TombstoneUpdate) { + // Empty EntitySpecifics indicates tombstone update. + scoped_ptr<ModelTypeEntity> entity( + NewServerItem(10, sync_pb::EntitySpecifics())); + + EXPECT_EQ(kClientTagHash, entity->metadata().client_tag_hash()); + EXPECT_FALSE(entity->HasCommitData()); + EXPECT_FALSE(HasSpecificsHash(entity)); + + EXPECT_FALSE(entity->IsUnsynced()); + EXPECT_TRUE(entity->UpdateIsReflection(9)); + EXPECT_TRUE(entity->UpdateIsReflection(10)); + EXPECT_FALSE(entity->UpdateIsReflection(11)); + EXPECT_FALSE(entity->UpdateIsInConflict(11)); +} + +// Apply a deletion update. +TEST_F(ModelTypeEntityTest, ApplyUpdate) { + // Start with a non-deleted state with version 10. + scoped_ptr<ModelTypeEntity> entity(NewServerItem(10, specifics)); + + EXPECT_TRUE(HasSpecificsHash(entity)); + + // A deletion update one version later. + ApplyUpdateFromServer(entity.get(), 11, sync_pb::EntitySpecifics(), + kMtime + base::TimeDelta::FromSeconds(10)); + + EXPECT_FALSE(HasSpecificsHash(entity)); + + EXPECT_FALSE(entity->IsUnsynced()); + EXPECT_TRUE(entity->UpdateIsReflection(11)); + EXPECT_FALSE(entity->UpdateIsReflection(12)); +} + +TEST_F(ModelTypeEntityTest, LocalChange) { + // Start with a non-deleted state with version 10. + scoped_ptr<ModelTypeEntity> entity(NewServerItem(10, specifics)); + + std::string specifics_hash = entity->metadata().specifics_hash(); + + // Make a local change with different specifics. + sync_pb::EntitySpecifics specifics2; + specifics2.CopyFrom(specifics); + specifics2.mutable_preference()->set_value("new.pref.value"); + + MakeLocalChange(entity.get(), specifics2); + EXPECT_NE(entity->metadata().specifics_hash(), specifics_hash); + + EXPECT_TRUE(entity->HasCommitData()); + EXPECT_TRUE(entity->IsUnsynced()); + + EXPECT_TRUE(entity->UpdateIsReflection(10)); + EXPECT_FALSE(entity->UpdateIsInConflict(10)); + + EXPECT_FALSE(entity->UpdateIsReflection(11)); + EXPECT_TRUE(entity->UpdateIsInConflict(11)); +} + +TEST_F(ModelTypeEntityTest, LocalDeletion) { + // Start with a non-deleted state with version 10. + scoped_ptr<ModelTypeEntity> entity(NewServerItem(10, specifics)); + EXPECT_TRUE(HasSpecificsHash(entity)); + + // Make a local delete. + entity->Delete(); + EXPECT_FALSE(HasSpecificsHash(entity)); + + EXPECT_TRUE(entity->HasCommitData()); + EXPECT_TRUE(entity->IsUnsynced()); + + EXPECT_TRUE(entity->UpdateIsReflection(10)); + EXPECT_FALSE(entity->UpdateIsInConflict(10)); + + EXPECT_FALSE(entity->UpdateIsReflection(11)); + EXPECT_TRUE(entity->UpdateIsInConflict(11)); +} + +// Verify generation of CommitRequestData from ModelTypeEntity. +// Verify that the sequence number increments on local changes. +TEST_F(ModelTypeEntityTest, InitializeCommitRequestData) { + scoped_ptr<ModelTypeEntity> entity(NewLocalItem(kClientTag)); + MakeLocalChange(entity.get(), specifics); + + CommitRequestData commit_request; + entity->InitializeCommitRequestData(&commit_request); + + EXPECT_EQ(1, commit_request.sequence_number); + EXPECT_EQ(kUncommittedVersion, commit_request.base_version); + + const EntityData& data = commit_request.entity.value(); + EXPECT_EQ(entity->metadata().client_tag_hash(), data.client_tag_hash); + EXPECT_EQ(specifics.SerializeAsString(), data.specifics.SerializeAsString()); + EXPECT_FALSE(data.is_deleted()); + + sync_pb::EntitySpecifics specifics2; + specifics2.CopyFrom(specifics); + specifics2.mutable_preference()->set_value("new.pref.value"); + MakeLocalChange(entity.get(), specifics2); + + entity->InitializeCommitRequestData(&commit_request); + const EntityData& data2 = commit_request.entity.value(); + + EXPECT_EQ(2, commit_request.sequence_number); + EXPECT_EQ(specifics2.SerializeAsString(), + data2.specifics.SerializeAsString()); + EXPECT_FALSE(data2.is_deleted()); + + entity->Delete(); + + entity->InitializeCommitRequestData(&commit_request); + const EntityData& data3 = commit_request.entity.value(); + + EXPECT_EQ(3, commit_request.sequence_number); + EXPECT_TRUE(data3.is_deleted()); +} + +} // namespace syncer_v2 diff --git a/sync/internal_api/public/model_type_entity.h b/sync/internal_api/public/model_type_entity.h new file mode 100644 index 0000000..42401da --- /dev/null +++ b/sync/internal_api/public/model_type_entity.h @@ -0,0 +1,147 @@ +// Copyright 2014 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. + +#ifndef SYNC_INTERNAL_API_PUBLIC_MODEL_TYPE_ENTITY_H_ +#define SYNC_INTERNAL_API_PUBLIC_MODEL_TYPE_ENTITY_H_ + +#include <string> + +#include "base/memory/scoped_ptr.h" +#include "base/time/time.h" +#include "sync/api/entity_data.h" +#include "sync/base/sync_export.h" +#include "sync/protocol/entity_metadata.pb.h" + +namespace syncer_v2 { +struct CommitRequestData; +struct UpdateResponseData; + +// This is the model thread's representation of a sync entity which is used +// to cache entity data and metadata in SharedModelTypeProcessor. +// +// The metadata part of ModelTypeEntity is loaded on Sync startup and is always +// present. The data part of ModelTypeEntity is cached temporarily, only for +// in-flight entities that are being committed to the server. +// +class SYNC_EXPORT_PRIVATE ModelTypeEntity { + public: + // Construct an instance representing a new locally-created item. + static scoped_ptr<ModelTypeEntity> CreateNew( + const std::string& client_tag, + const std::string& client_tag_hash, + const std::string& id, + base::Time creation_time); + + ~ModelTypeEntity(); + + // Returns entity's client key. + const std::string& client_key() const { return client_key_; } + + // Returns entity's metadata. + const sync_pb::EntityMetadata& metadata() const { return metadata_; } + + // Returns true if this data is out of sync with the server. + // A commit may or may not be in progress at this time. + bool IsUnsynced() const; + + // Returns true if this data is out of sync with the sync thread. + // + // There may or may not be a commit in progress for this item, but there's + // definitely no commit in progress for this (most up to date) version of + // this item. + bool RequiresCommitRequest() const; + + // Returns true if the specified update version does not contain new data. + bool UpdateIsReflection(int64 update_version) const; + + // Returns true if the specified update version conflicts with local changes. + bool UpdateIsInConflict(int64 update_version) const; + + // Applies an update from the sync server. + // + // Overrides any local changes. Check UpdateIsInConflict() before calling + // this function if you want to handle conflicts differently. + void ApplyUpdateFromServer(const UpdateResponseData& response_data); + + // Applies a local change to this item. + void MakeLocalChange(const std::string& non_unique_name, + const sync_pb::EntitySpecifics& specifics, + base::Time modification_time); + + // Schedule a commit if the |name| does not match this item's last known + // encryption key. The worker that performs the commit is expected to + // encrypt the item using the latest available key. + void UpdateDesiredEncryptionKey(const std::string& name); + + // Applies a local deletion to this item. + void Delete(); + + // Initializes a message representing this item's uncommitted state + // to be forwarded to the sync server for committing. + void InitializeCommitRequestData(CommitRequestData* request) const; + + // Notes that the current version of this item has been queued for commit. + void SetCommitRequestInProgress(); + + // Receives a successful commit response. + // + // Successful commit responses can overwrite an item's ID. + // + // Note that the receipt of a successful commit response does not necessarily + // unset IsUnsynced(). If many local changes occur in quick succession, it's + // possible that the committed item was already out of date by the time it + // reached the server. + void ReceiveCommitResponse(const std::string& id, + int64 sequence_number, + int64 response_version, + const std::string& encryption_key_name); + + // Clears any in-memory sync state associated with outstanding commits. + void ClearTransientSyncState(); + + // Clears all sync state. Invoked when a user signs out. + void ClearSyncState(); + + // Takes the passed commit data and caches it in the instance. + // The data is swapped from the input struct without copying. + void CacheCommitData(EntityData* data); + + // Check if the instance has cached commit data. + bool HasCommitData() const; + + private: + friend class ModelTypeEntityTest; + + // The constructor swaps the data from the passed metadata. + ModelTypeEntity(const std::string& client_key, + sync_pb::EntityMetadata* metadata); + + // Increment sequence number in the metadata. + void IncrementSequenceNumber(); + + // Update hash string for EntitySpecifics in the metadata. + void UpdateSpecificsHash(const sync_pb::EntitySpecifics& specifics); + + // Client key. Should always be available. + std::string client_key_; + + // Serializable Sync metadata. + sync_pb::EntityMetadata metadata_; + + // Sync data that exists for items being committed only. + // The data is reset once commit confirmation is received. + EntityDataPtr commit_data_; + + // The sequence number of the last item sent to the sync thread. + int64 commit_requested_sequence_number_; + + // TODO(stanisc): this should be removed. + // The name of the encryption key used to encrypt this item on the server. + // Empty when no encryption is in use. + std::string encryption_key_name_; +}; + +} // namespace syncer_v2 + +#endif // SYNC_INTERNAL_API_PUBLIC_MODEL_TYPE_ENTITY_H_ diff --git a/sync/internal_api/public/non_blocking_sync_common.h b/sync/internal_api/public/non_blocking_sync_common.h index f3ca352..894e6f8 100644 --- a/sync/internal_api/public/non_blocking_sync_common.h +++ b/sync/internal_api/public/non_blocking_sync_common.h @@ -67,11 +67,11 @@ struct SYNC_EXPORT_PRIVATE CommitResponseData { }; struct SYNC_EXPORT_PRIVATE UpdateResponseData { - EntityDataPtr entity; - UpdateResponseData(); ~UpdateResponseData(); + EntityDataPtr entity; + int64 response_version = 0; std::string encryption_key_name; }; @@ -80,6 +80,6 @@ typedef std::vector<CommitRequestData> CommitRequestDataList; typedef std::vector<CommitResponseData> CommitResponseDataList; typedef std::vector<UpdateResponseData> UpdateResponseDataList; -} // namespace syncer +} // namespace syncer_v2 #endif // SYNC_INTERNAL_API_PUBLIC_NON_BLOCKING_SYNC_COMMON_H_ diff --git a/sync/internal_api/public/util/proto_value_ptr.h b/sync/internal_api/public/util/proto_value_ptr.h index dc0c98f..a4f4fc0 100644 --- a/sync/internal_api/public/util/proto_value_ptr.h +++ b/sync/internal_api/public/util/proto_value_ptr.h @@ -2,15 +2,16 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef SYNC_SYNCABLE_ENTRY_PROTO_FIELD_PTR_H_ -#define SYNC_SYNCABLE_ENTRY_PROTO_FIELD_PTR_H_ +#ifndef SYNC_INTERNAL_API_PUBLIC_UTIL_PROTO_VALUE_PTR_H_ +#define SYNC_INTERNAL_API_PUBLIC_UTIL_PROTO_VALUE_PTR_H_ #include "base/gtest_prod_util.h" #include "base/memory/ref_counted.h" namespace syncer_v2 { struct EntityData; -} // namespace syncable +class ModelTypeEntity; +} // namespace syncer_v2 namespace syncer { @@ -58,8 +59,8 @@ class ProtoValuePtr { // Immutable shareable ref-counted wrapper that embeds the value. class Wrapper : public base::RefCountedThreadSafe<Wrapper> { public: - Wrapper(const T& value) { Traits::CopyValue(&value_, value); } - Wrapper(T* value) { Traits::SwapValue(&value_, value); } + explicit Wrapper(const T& value) { Traits::CopyValue(&value_, value); } + explicit Wrapper(T* value) { Traits::SwapValue(&value_, value); } const T& value() const { return value_; } // Create wrapper by deserializing a BLOB. @@ -93,6 +94,7 @@ class ProtoValuePtr { private: friend struct syncable::EntryKernel; friend struct syncer_v2::EntityData; + friend class syncer_v2::ModelTypeEntity; FRIEND_TEST_ALL_PREFIXES(ProtoValuePtrTest, ValueAssignment); FRIEND_TEST_ALL_PREFIXES(ProtoValuePtrTest, ValueSwap); FRIEND_TEST_ALL_PREFIXES(ProtoValuePtrTest, SharingTest); @@ -104,17 +106,19 @@ class ProtoValuePtr { wrapper_ = new Wrapper(new_value); } else { // Don't store default value. - wrapper_ = nullptr; + reset(); } } + void reset() { wrapper_ = nullptr; } + // Take over |src| value (swap). void swap_value(T* src) { if (Traits::HasValue(*src)) { wrapper_ = new Wrapper(src); } else { // Don't store default value. - wrapper_ = nullptr; + reset(); } } @@ -127,4 +131,4 @@ class ProtoValuePtr { } // namespace syncer -#endif // SYNC_SYNCABLE_ENTRY_PROTO_FIELD_PTR_H_ +#endif // SYNC_INTERNAL_API_PUBLIC_UTIL_PROTO_VALUE_PTR_H_ diff --git a/sync/internal_api/shared_model_type_processor.cc b/sync/internal_api/shared_model_type_processor.cc index e653d62..db511df 100644 --- a/sync/internal_api/shared_model_type_processor.cc +++ b/sync/internal_api/shared_model_type_processor.cc @@ -10,8 +10,8 @@ #include "base/location.h" #include "base/thread_task_runner_handle.h" #include "sync/engine/commit_queue.h" -#include "sync/engine/model_type_entity.h" #include "sync/internal_api/public/activation_context.h" +#include "sync/internal_api/public/model_type_entity.h" #include "sync/syncable/syncable_util.h" namespace syncer_v2 { @@ -158,17 +158,23 @@ void SharedModelTypeProcessor::Put(const std::string& client_tag, const std::string client_tag_hash( syncer::syncable::GenerateSyncableHash(type_, client_tag)); + base::Time now = base::Time::Now(); + ModelTypeEntity* entity = nullptr; + // TODO(stanisc): crbug/561818: Search by client_tag rather than + // client_tag_hash. EntityMap::const_iterator it = entities_.find(client_tag_hash); if (it == entities_.end()) { - scoped_ptr<ModelTypeEntity> entity(ModelTypeEntity::NewLocalItem( - client_tag, specifics, base::Time::Now())); - entities_.insert(std::make_pair(client_tag_hash, std::move(entity))); + scoped_ptr<ModelTypeEntity> scoped_entity = + ModelTypeEntity::CreateNew(client_tag, client_tag_hash, "", now); + entity = scoped_entity.get(); + entities_.insert( + std::make_pair(client_tag_hash, std::move(scoped_entity))); } else { - ModelTypeEntity* entity = it->second.get(); - entity->MakeLocalChange(specifics); + entity = it->second.get(); } + entity->MakeLocalChange(non_unique_name, specifics, now); FlushPendingCommitRequests(); } @@ -263,24 +269,26 @@ void SharedModelTypeProcessor::OnUpdateReceived( // the previous pending updates. pending_updates_map_.erase(client_tag_hash); + ModelTypeEntity* entity = nullptr; EntityMap::const_iterator it = entities_.find(client_tag_hash); if (it == entities_.end()) { - // TODO(stanisc): Pass / share entire EntityData with ModelTypeEntity. - scoped_ptr<ModelTypeEntity> entity = ModelTypeEntity::FromServerUpdate( - data.id, data.client_tag_hash, data.non_unique_name, - response_data.response_version, data.specifics, data.is_deleted(), - data.creation_time, data.modification_time, - response_data.encryption_key_name); - entities_.insert(std::make_pair(client_tag_hash, std::move(entity))); - } else { - ModelTypeEntity* entity = it->second.get(); - entity->ApplyUpdateFromServer( - response_data.response_version, data.is_deleted(), data.specifics, - data.modification_time, response_data.encryption_key_name); + // TODO(stanisc): crbug/561821: Get client_tag from the service. + std::string client_tag = client_tag_hash; + + scoped_ptr<ModelTypeEntity> scoped_entity = ModelTypeEntity::CreateNew( + client_tag, client_tag_hash, data.id, data.creation_time); + entity = scoped_entity.get(); + entities_.insert( + std::make_pair(client_tag_hash, std::move(scoped_entity))); - // TODO(rlarocque): Do something special when conflicts are detected. + } else { + entity = it->second.get(); } + entity->ApplyUpdateFromServer(response_data); + + // TODO(stanisc): Do something special when conflicts are detected. + // If the received entity has out of date encryption, we schedule another // commit to fix it. if (data_type_state_.encryption_key_name != diff --git a/sync/internal_api/shared_model_type_processor_unittest.cc b/sync/internal_api/shared_model_type_processor_unittest.cc index 2b5bc64..2cc9998 100644 --- a/sync/internal_api/shared_model_type_processor_unittest.cc +++ b/sync/internal_api/shared_model_type_processor_unittest.cc @@ -202,7 +202,8 @@ void SharedModelTypeProcessorTest::StartDone( void SharedModelTypeProcessorTest::WriteItem(const std::string& tag, const std::string& value) { sync_pb::EntitySpecifics specifics = GenerateSpecifics(tag, value); - type_processor_->Put(tag, std::string(), specifics, nullptr); + // Use the tag as non-unique name. + type_processor_->Put(tag, tag, specifics, nullptr); } void SharedModelTypeProcessorTest::DeleteItem(const std::string& tag) { @@ -652,7 +653,9 @@ TEST_F(SharedModelTypeProcessorTest, StopWithPendingUpdates) { } // Test re-encrypt everything when desired encryption key changes. -TEST_F(SharedModelTypeProcessorTest, ReEncryptCommitsWithNewKey) { +// TODO(stanisc): crbug/561814: Disabled due to data caching changes in +// ModelTypeEntity. Revisit the test once fetching of data is implemented. +TEST_F(SharedModelTypeProcessorTest, DISABLED_ReEncryptCommitsWithNewKey) { InitializeToReadyState(); // Commit an item. @@ -684,7 +687,9 @@ TEST_F(SharedModelTypeProcessorTest, ReEncryptCommitsWithNewKey) { } // Test receipt of updates with new and old keys. -TEST_F(SharedModelTypeProcessorTest, ReEncryptUpdatesWithNewKey) { +// TODO(stanisc): crbug/561814: Disabled due to data caching changes in +// ModelTypeEntity. Revisit the test once fetching of data is implemented. +TEST_F(SharedModelTypeProcessorTest, DISABLED_ReEncryptUpdatesWithNewKey) { InitializeToReadyState(); // Receive an unencrpted update. @@ -717,4 +722,4 @@ TEST_F(SharedModelTypeProcessorTest, ReEncryptUpdatesWithNewKey) { EXPECT_FALSE(HasCommitRequestForTag("enc_k2")); } -} // namespace syncer +} // namespace syncer_v2 diff --git a/sync/sync.gyp b/sync/sync.gyp index 45943fc..2de3160 100644 --- a/sync/sync.gyp +++ b/sync/sync.gyp @@ -144,8 +144,6 @@ 'engine/get_updates_delegate.h', 'engine/get_updates_processor.cc', 'engine/get_updates_processor.h', - 'engine/model_type_entity.cc', - 'engine/model_type_entity.h', 'engine/model_type_worker.cc', 'engine/model_type_worker.h', 'engine/net/server_connection_manager.cc', @@ -223,6 +221,7 @@ 'internal_api/js_sync_encryption_handler_observer.h', 'internal_api/js_sync_manager_observer.cc', 'internal_api/js_sync_manager_observer.h', + 'internal_api/model_type_entity.cc', 'internal_api/model_type_store_backend.cc', 'internal_api/model_type_store_impl.cc', 'internal_api/protocol_event_buffer.cc', @@ -296,6 +295,7 @@ 'internal_api/public/http_post_provider_interface.h', 'internal_api/public/internal_components_factory.h', 'internal_api/public/internal_components_factory_impl.h', + 'internal_api/public/model_type_entity.h', 'internal_api/public/model_type_processor.cc', 'internal_api/public/model_type_processor.h', 'internal_api/public/model_type_store_backend.h', diff --git a/sync/sync_tests.gypi b/sync/sync_tests.gypi index e550c30..bc0c307 100644 --- a/sync/sync_tests.gypi +++ b/sync/sync_tests.gypi @@ -278,7 +278,6 @@ 'engine/directory_update_handler_unittest.cc', 'engine/entity_tracker_unittest.cc', 'engine/get_updates_processor_unittest.cc', - 'engine/model_type_entity_unittest.cc', 'engine/model_type_worker_unittest.cc', 'engine/sync_scheduler_unittest.cc', 'engine/syncer_proto_util_unittest.cc', @@ -300,6 +299,7 @@ 'internal_api/js_mutation_event_observer_unittest.cc', 'internal_api/js_sync_encryption_handler_observer_unittest.cc', 'internal_api/js_sync_manager_observer_unittest.cc', + 'internal_api/model_type_entity_unittest.cc', 'internal_api/model_type_store_backend_unittest.cc', 'internal_api/model_type_store_impl_unittest.cc', 'internal_api/protocol_event_buffer_unittest.cc', |