Commit 956715c7 authored by Mikel Astiz's avatar Mikel Astiz Committed by Commit Bot

Fix sync protocol violation about birthday requirements

The sync (store) birthday can only be empty during the very initial
interactions with the server, when control datatypes are downloaded,
after enabling sync (or the user signing in, for sync-the-transport
mode).

If, during startup, the birthday is empty, we should throw away all
sync metadata and redownload everything. One way to do so is to
regenerate the cache GUID, which datatypes detect as mismatch and
leads to all sync metadata being cleared.

As can seen in newly added tests, the scenario seems hard to
reproduce, but we have server-side evidence that some users run into
this issue (and would even DCHECK-fail for DCHECK-enabled builds).

Bug: 923285
Change-Id: If9e4807a086001931fd65f3e12a1ab53c4a33c9d
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1599456Reviewed-by: default avatarMarc Treib <treib@chromium.org>
Commit-Queue: Mikel Astiz <mastiz@chromium.org>
Cr-Commit-Position: refs/heads/master@{#658540}
parent 850ad071
...@@ -22,6 +22,7 @@ ...@@ -22,6 +22,7 @@
#include "components/sync/driver/sync_user_settings_impl.h" #include "components/sync/driver/sync_user_settings_impl.h"
#include "components/sync/test/fake_server/bookmark_entity_builder.h" #include "components/sync/test/fake_server/bookmark_entity_builder.h"
#include "components/sync/test/fake_server/entity_builder_factory.h" #include "components/sync/test/fake_server/entity_builder_factory.h"
#include "components/sync/test/fake_server/fake_server_http_post_provider.h"
namespace { namespace {
...@@ -442,6 +443,39 @@ IN_PROC_BROWSER_TEST_F(EnableDisableSingleClientTest, ...@@ -442,6 +443,39 @@ IN_PROC_BROWSER_TEST_F(EnableDisableSingleClientTest,
EXPECT_EQ(cache_guid, prefs.GetCacheGuid()); EXPECT_EQ(cache_guid, prefs.GetCacheGuid());
} }
IN_PROC_BROWSER_TEST_F(EnableDisableSingleClientTest,
PRE_RestartDuringInitialSync) {
SetupSyncNoWaitingForCompletion();
// The intended scenario being tested involves an ongoing initial sync. This
// means a cache GUID has been generated, but the initial birthday hasn't been
// received yet (which is received from the server as part of the initial
// GetUpdates request).
SyncPrefs prefs(GetProfile(0)->GetPrefs());
ASSERT_NE("", prefs.GetCacheGuid());
ASSERT_EQ("", prefs.GetBirthday());
// Mimic a non-responding server (slow connection) during shutdown, to make
// sure test teardown logic doesn't fetch the birthday. This is asserted in
// the next test and would otherwise fail.
fake_server::FakeServerHttpPostProvider::SetNetworkDelay(
base::TimeDelta::Max());
}
IN_PROC_BROWSER_TEST_F(EnableDisableSingleClientTest,
RestartDuringInitialSync) {
ASSERT_TRUE(SetupClients());
SyncPrefs prefs(GetProfile(0)->GetPrefs());
ASSERT_NE("", prefs.GetCacheGuid());
ASSERT_EQ("", prefs.GetBirthday());
EXPECT_TRUE(GetClient(0)->AwaitSyncSetupCompletion());
ASSERT_NE("", prefs.GetCacheGuid());
EXPECT_NE("", prefs.GetBirthday());
}
class EnableDisableSingleClientSelfNotifyTest class EnableDisableSingleClientSelfNotifyTest
: public EnableDisableSingleClientTest { : public EnableDisableSingleClientTest {
public: public:
......
...@@ -484,17 +484,25 @@ void ProfileSyncService::StartUpSlowEngineComponents() { ...@@ -484,17 +484,25 @@ void ProfileSyncService::StartUpSlowEngineComponents() {
} }
params.sync_manager_factory = params.sync_manager_factory =
std::make_unique<SyncManagerFactory>(network_connection_tracker_); std::make_unique<SyncManagerFactory>(network_connection_tracker_);
// The first time we start up the engine we want to ensure we have a clean params.birthday = sync_prefs_.GetBirthday();
// directory, so delete any old one that might be there. params.cache_guid = sync_prefs_.GetCacheGuid();
params.delete_sync_data_folder = !user_settings_->IsFirstSetupComplete(); // If either the cache GUID or the birthday are unitialized, it means we
if (params.delete_sync_data_folder) { // haven't completed a first sync cycle. We regenerate the cache GUID either
// This looks questionable here but it mimics the old behavior of deleting // way, even if just the birthday is missing, because fetching updates
// the directory via Directory::DeleteDirectoryFiles(). One consecuence is // requires that the request either has a birthday, or there should be no
// that, for sync the transport users (without sync-the-feature enabled), // progress marker.
// the cache GUID and other fields are reset on every restart. // TODO(crbug.com/923285): This looks questionable here for
// TODO(crbug.com/923285): Reconsider the lifetime of the cache GUID and // |!IsFirstSetupComplete()| but it mimics the old behavior of deleting the
// its persistence depending on StorageOption. // directory via Directory::DeleteDirectoryFiles(). One consecuence is that,
// for sync the transport users (without sync-the-feature enabled), the cache
// GUID and other fields are reset on every restart.
if (params.cache_guid.empty() || params.birthday.empty() ||
!user_settings_->IsFirstSetupComplete()) {
sync_prefs_.ClearDirectoryConsistencyPreferences(); sync_prefs_.ClearDirectoryConsistencyPreferences();
params.cache_guid = GenerateCacheGUID();
params.birthday.clear();
params.delete_sync_data_folder = true;
sync_prefs_.SetCacheGuid(params.cache_guid);
} }
params.enable_local_sync_backend = sync_prefs_.IsLocalSyncEnabled(); params.enable_local_sync_backend = sync_prefs_.IsLocalSyncEnabled();
params.local_sync_backend_folder = sync_client_->GetLocalSyncBackendFolder(); params.local_sync_backend_folder = sync_client_->GetLocalSyncBackendFolder();
...@@ -502,12 +510,7 @@ void ProfileSyncService::StartUpSlowEngineComponents() { ...@@ -502,12 +510,7 @@ void ProfileSyncService::StartUpSlowEngineComponents() {
sync_prefs_.GetEncryptionBootstrapToken(); sync_prefs_.GetEncryptionBootstrapToken();
params.restored_keystore_key_for_bootstrapping = params.restored_keystore_key_for_bootstrapping =
sync_prefs_.GetKeystoreEncryptionBootstrapToken(); sync_prefs_.GetKeystoreEncryptionBootstrapToken();
params.cache_guid = sync_prefs_.GetCacheGuid();
if (params.cache_guid.empty()) {
params.cache_guid = GenerateCacheGUID();
sync_prefs_.SetCacheGuid(params.cache_guid);
}
params.birthday = sync_prefs_.GetBirthday();
params.bag_of_chips = sync_prefs_.GetBagOfChips(); params.bag_of_chips = sync_prefs_.GetBagOfChips();
params.engine_components_factory = params.engine_components_factory =
std::make_unique<EngineComponentsFactoryImpl>( std::make_unique<EngineComponentsFactoryImpl>(
...@@ -859,6 +862,10 @@ void ProfileSyncService::OnEngineInitialized( ...@@ -859,6 +862,10 @@ void ProfileSyncService::OnEngineInitialized(
return; return;
} }
// TODO(crbug.com/923285): The store birthday should be available at this
// point, so we should ideally save to prefs, since OnSyncCycleCompleted()
// does not get called for the first sync cycle for control types.
sync_js_controller_.AttachJsBackend(js_backend); sync_js_controller_.AttachJsBackend(js_backend);
if (protocol_event_observers_.might_have_observers()) { if (protocol_event_observers_.might_have_observers()) {
......
...@@ -344,9 +344,9 @@ net::HttpStatusCode LoopbackServer::HandleCommand(const string& request, ...@@ -344,9 +344,9 @@ net::HttpStatusCode LoopbackServer::HandleCommand(const string& request,
std::vector<ModelType> datatypes_to_migrate; std::vector<ModelType> datatypes_to_migrate;
switch (message.message_contents()) { switch (message.message_contents()) {
case sync_pb::ClientToServerMessage::GET_UPDATES: case sync_pb::ClientToServerMessage::GET_UPDATES:
success = HandleGetUpdatesRequest(message.get_updates(), success = HandleGetUpdatesRequest(
response_proto.mutable_get_updates(), message.get_updates(), message.store_birthday(),
&datatypes_to_migrate); response_proto.mutable_get_updates(), &datatypes_to_migrate);
break; break;
case sync_pb::ClientToServerMessage::COMMIT: case sync_pb::ClientToServerMessage::COMMIT:
success = HandleCommitRequest(message.commit(), success = HandleCommitRequest(message.commit(),
...@@ -394,10 +394,25 @@ void LoopbackServer::EnableStrongConsistencyWithConflictDetectionModel() { ...@@ -394,10 +394,25 @@ void LoopbackServer::EnableStrongConsistencyWithConflictDetectionModel() {
bool LoopbackServer::HandleGetUpdatesRequest( bool LoopbackServer::HandleGetUpdatesRequest(
const sync_pb::GetUpdatesMessage& get_updates, const sync_pb::GetUpdatesMessage& get_updates,
const std::string& store_birthday,
sync_pb::GetUpdatesResponse* response, sync_pb::GetUpdatesResponse* response,
std::vector<ModelType>* datatypes_to_migrate) { std::vector<ModelType>* datatypes_to_migrate) {
response->set_changes_remaining(0); response->set_changes_remaining(0);
// It's a protocol-level contract that the birthday should only be empty
// during the initial sync cycle, which requires all progress markers to be
// empty. This is also DCHECK-ed on the client, inside syncer_proto_util.cc,
// but we guard against client-side code changes here.
if (store_birthday.empty()) {
for (const sync_pb::DataTypeProgressMarker& marker :
get_updates.from_progress_marker()) {
if (!marker.token().empty()) {
DLOG(WARNING) << "Non-empty progress marker without birthday";
return false;
}
}
}
auto sieve = std::make_unique<UpdateSieve>(get_updates, migration_versions_); auto sieve = std::make_unique<UpdateSieve>(get_updates, migration_versions_);
if (sieve->ShouldTriggerMigration(migration_versions_, if (sieve->ShouldTriggerMigration(migration_versions_,
......
...@@ -89,6 +89,7 @@ class LoopbackServer { ...@@ -89,6 +89,7 @@ class LoopbackServer {
// Processes a GetUpdates call. // Processes a GetUpdates call.
bool HandleGetUpdatesRequest(const sync_pb::GetUpdatesMessage& get_updates, bool HandleGetUpdatesRequest(const sync_pb::GetUpdatesMessage& get_updates,
const std::string& store_birthday,
sync_pb::GetUpdatesResponse* response, sync_pb::GetUpdatesResponse* response,
std::vector<ModelType>* datatypes_to_migrate); std::vector<ModelType>* datatypes_to_migrate);
......
...@@ -8,7 +8,6 @@ ...@@ -8,7 +8,6 @@
#include "base/bind.h" #include "base/bind.h"
#include "base/location.h" #include "base/location.h"
#include "base/time/time.h"
#include "components/sync/test/fake_server/fake_server.h" #include "components/sync/test/fake_server/fake_server.h"
#include "net/base/net_errors.h" #include "net/base/net_errors.h"
...@@ -17,6 +16,10 @@ namespace fake_server { ...@@ -17,6 +16,10 @@ namespace fake_server {
// static // static
std::atomic_bool FakeServerHttpPostProvider::network_enabled_(true); std::atomic_bool FakeServerHttpPostProvider::network_enabled_(true);
// static
std::atomic<base::TimeDelta> FakeServerHttpPostProvider::network_delay_ = {
base::TimeDelta()};
FakeServerHttpPostProviderFactory::FakeServerHttpPostProviderFactory( FakeServerHttpPostProviderFactory::FakeServerHttpPostProviderFactory(
const base::WeakPtr<FakeServer>& fake_server, const base::WeakPtr<FakeServer>& fake_server,
scoped_refptr<base::SequencedTaskRunner> fake_server_task_runner) scoped_refptr<base::SequencedTaskRunner> fake_server_task_runner)
...@@ -89,6 +92,17 @@ bool FakeServerHttpPostProvider::MakeSynchronousPost(int* net_error_code, ...@@ -89,6 +92,17 @@ bool FakeServerHttpPostProvider::MakeSynchronousPost(int* net_error_code,
synchronous_post_completion_.Reset(); synchronous_post_completion_.Reset();
aborted_ = false; aborted_ = false;
const base::TimeDelta network_delay = network_delay_.load();
if (!network_delay.is_zero()) {
// Block the specified time unless Abort() is called meanwhile.
synchronous_post_completion_.TimedWait(network_delay);
if (aborted_) {
*net_error_code = net::ERR_ABORTED;
return false;
}
DCHECK(!synchronous_post_completion_.IsSignaled());
}
// It is assumed that a POST is being made to /command. // It is assumed that a POST is being made to /command.
int post_status_code = -1; int post_status_code = -1;
std::string post_response; std::string post_response;
...@@ -160,6 +174,12 @@ void FakeServerHttpPostProvider::EnableNetwork() { ...@@ -160,6 +174,12 @@ void FakeServerHttpPostProvider::EnableNetwork() {
network_enabled_ = true; network_enabled_ = true;
} }
// static
void FakeServerHttpPostProvider::SetNetworkDelay(base::TimeDelta delay) {
// Note: This may be called on any thread.
network_delay_ = delay;
}
void FakeServerHttpPostProvider::HandleCommandOnFakeServerThread( void FakeServerHttpPostProvider::HandleCommandOnFakeServerThread(
int* http_status_code, int* http_status_code,
std::string* response) { std::string* response) {
......
...@@ -15,6 +15,7 @@ ...@@ -15,6 +15,7 @@
#include "base/sequence_checker.h" #include "base/sequence_checker.h"
#include "base/sequenced_task_runner.h" #include "base/sequenced_task_runner.h"
#include "base/synchronization/waitable_event.h" #include "base/synchronization/waitable_event.h"
#include "base/time/time.h"
#include "components/sync/engine/net/http_post_provider_factory.h" #include "components/sync/engine/net/http_post_provider_factory.h"
#include "components/sync/engine/net/http_post_provider_interface.h" #include "components/sync/engine/net/http_post_provider_interface.h"
...@@ -50,6 +51,9 @@ class FakeServerHttpPostProvider ...@@ -50,6 +51,9 @@ class FakeServerHttpPostProvider
// Undoes the effects of DisableNetwork. // Undoes the effects of DisableNetwork.
static void EnableNetwork(); static void EnableNetwork();
// Mimics a slow network: each request will be blocked for the specified time.
static void SetNetworkDelay(base::TimeDelta delay);
protected: protected:
~FakeServerHttpPostProvider() override; ~FakeServerHttpPostProvider() override;
...@@ -60,6 +64,7 @@ class FakeServerHttpPostProvider ...@@ -60,6 +64,7 @@ class FakeServerHttpPostProvider
std::string* response); std::string* response);
static std::atomic_bool network_enabled_; static std::atomic_bool network_enabled_;
static std::atomic<base::TimeDelta> network_delay_;
// |fake_server_| should only be dereferenced on the same thread as // |fake_server_| should only be dereferenced on the same thread as
// |fake_server_task_runner_| runs on. // |fake_server_task_runner_| runs on.
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment