summaryrefslogtreecommitdiffstats
path: root/sync/engine
diff options
context:
space:
mode:
authorhaitaol@chromium.org <haitaol@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2014-01-14 16:17:43 +0000
committerhaitaol@chromium.org <haitaol@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2014-01-14 16:17:43 +0000
commit3dfc7b7970b85378523b53bea33b1139dceaca6e (patch)
tree74450de20b350f2995db44f319b8da0f4911e97d /sync/engine
parentea7f19230d44bd71eef8082ee79740d11ac047d1 (diff)
downloadchromium_src-3dfc7b7970b85378523b53bea33b1139dceaca6e.zip
chromium_src-3dfc7b7970b85378523b53bea33b1139dceaca6e.tar.gz
chromium_src-3dfc7b7970b85378523b53bea33b1139dceaca6e.tar.bz2
[Recommit]
Support GU retry command in sync engine. The command specifies a delay after which syncer should issue a GU to pick up updates missed by last GU. TBR=rlarocque@chromium.org Review URL: https://codereview.chromium.org/133763007 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@244736 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'sync/engine')
-rw-r--r--sync/engine/download.cc40
-rw-r--r--sync/engine/download.h15
-rw-r--r--sync/engine/download_unittest.cc37
-rw-r--r--sync/engine/sync_scheduler_impl.cc53
-rw-r--r--sync/engine/sync_scheduler_impl.h16
-rw-r--r--sync/engine/sync_scheduler_unittest.cc122
-rw-r--r--sync/engine/syncer.cc16
-rw-r--r--sync/engine/syncer.h2
-rw-r--r--sync/engine/syncer_proto_util.cc5
-rw-r--r--sync/engine/syncer_unittest.cc5
10 files changed, 276 insertions, 35 deletions
diff --git a/sync/engine/download.cc b/sync/engine/download.cc
index 2bc7f7a..448481f 100644
--- a/sync/engine/download.cc
+++ b/sync/engine/download.cc
@@ -234,6 +234,8 @@ void BuildNormalDownloadUpdatesImpl(
// Set the new and improved version of source, too.
get_updates->set_get_updates_origin(sync_pb::SyncEnums::GU_TRIGGER);
+ get_updates->set_is_retry(
+ nudge_tracker.IsRetryRequired(base::TimeTicks::Now()));
// Fill in the notification hints.
for (int i = 0; i < get_updates->from_progress_marker_size(); ++i) {
@@ -331,6 +333,44 @@ void BuildDownloadUpdatesForPollImpl(
get_updates->set_get_updates_origin(sync_pb::SyncEnums::PERIODIC);
}
+void BuildDownloadUpdatesForRetry(
+ SyncSession* session,
+ bool create_mobile_bookmarks_folder,
+ ModelTypeSet request_types,
+ sync_pb::ClientToServerMessage* client_to_server_message) {
+ DVLOG(1) << "Retrying for types "
+ << ModelTypeSetToString(request_types);
+
+ InitDownloadUpdatesContext(
+ session,
+ create_mobile_bookmarks_folder,
+ client_to_server_message);
+ BuildDownloadUpdatesForRetryImpl(
+ Intersection(request_types, ProtocolTypes()),
+ session->context()->update_handler_map(),
+ client_to_server_message->mutable_get_updates());
+}
+
+void BuildDownloadUpdatesForRetryImpl(
+ ModelTypeSet proto_request_types,
+ UpdateHandlerMap* update_handler_map,
+ sync_pb::GetUpdatesMessage* get_updates) {
+ DCHECK(!proto_request_types.Empty());
+
+ InitDownloadUpdatesProgress(
+ proto_request_types,
+ update_handler_map,
+ get_updates);
+
+ // Set legacy GetUpdatesMessage.GetUpdatesCallerInfo information.
+ get_updates->mutable_caller_info()->set_source(
+ sync_pb::GetUpdatesCallerInfo::RETRY);
+
+ // Set the new and improved version of source, too.
+ get_updates->set_get_updates_origin(sync_pb::SyncEnums::RETRY);
+ get_updates->set_is_retry(true);
+}
+
SyncerError ExecuteDownloadUpdates(
ModelTypeSet request_types,
SyncSession* session,
diff --git a/sync/engine/download.h b/sync/engine/download.h
index 5bc08d4..e3230d8 100644
--- a/sync/engine/download.h
+++ b/sync/engine/download.h
@@ -75,6 +75,21 @@ SYNC_EXPORT_PRIVATE void BuildDownloadUpdatesForPollImpl(
UpdateHandlerMap* update_handler_map,
sync_pb::GetUpdatesMessage* get_updates);
+// Same as BuildDownloadUpdatesForPoll() except the update origin/source is
+// RETRY.
+SYNC_EXPORT_PRIVATE void BuildDownloadUpdatesForRetry(
+ sessions::SyncSession* session,
+ bool create_mobile_bookmarks_folder,
+ ModelTypeSet request_types,
+ sync_pb::ClientToServerMessage* client_to_server_message);
+
+// Same as BuildDownloadUpdatesForPollImpl() except the update origin/source is
+// RETRY.
+SYNC_EXPORT_PRIVATE void BuildDownloadUpdatesForRetryImpl(
+ ModelTypeSet proto_request_types,
+ UpdateHandlerMap* update_handler_map,
+ sync_pb::GetUpdatesMessage* get_updates);
+
// Sends the specified message to the server and stores the response in a member
// of the |session|'s StatusController.
SYNC_EXPORT_PRIVATE SyncerError
diff --git a/sync/engine/download_unittest.cc b/sync/engine/download_unittest.cc
index eae6277..134f441 100644
--- a/sync/engine/download_unittest.cc
+++ b/sync/engine/download_unittest.cc
@@ -219,6 +219,43 @@ TEST_F(DownloadUpdatesTest, PollTest) {
EXPECT_TRUE(proto_request_types().Equals(progress_types));
}
+TEST_F(DownloadUpdatesTest, RetryTest) {
+ sync_pb::ClientToServerMessage msg;
+ download::BuildDownloadUpdatesForRetryImpl(
+ proto_request_types(),
+ update_handler_map(),
+ msg.mutable_get_updates());
+
+ const sync_pb::GetUpdatesMessage& gu_msg = msg.get_updates();
+
+ EXPECT_EQ(sync_pb::SyncEnums::RETRY, gu_msg.get_updates_origin());
+ EXPECT_EQ(sync_pb::GetUpdatesCallerInfo::RETRY,
+ gu_msg.caller_info().source());
+ EXPECT_TRUE(gu_msg.is_retry());
+
+ ModelTypeSet progress_types;
+ for (int i = 0; i < gu_msg.from_progress_marker_size(); ++i) {
+ syncer::ModelType type = GetModelTypeFromSpecificsFieldNumber(
+ gu_msg.from_progress_marker(i).data_type_id());
+ progress_types.Put(type);
+ }
+ EXPECT_TRUE(proto_request_types().Equals(progress_types));
+}
+
+TEST_F(DownloadUpdatesTest, NudgeWithRetryTest) {
+ sessions::NudgeTracker nudge_tracker;
+ nudge_tracker.RecordLocalChange(ModelTypeSet(BOOKMARKS));
+ nudge_tracker.set_next_retry_time(
+ base::TimeTicks::Now() - base::TimeDelta::FromSeconds(1));
+
+ sync_pb::ClientToServerMessage msg;
+ download::BuildNormalDownloadUpdatesImpl(proto_request_types(),
+ update_handler_map(),
+ nudge_tracker,
+ msg.mutable_get_updates());
+ EXPECT_TRUE(msg.get_updates().is_retry());
+}
+
// Verify that a bogus response message is detected.
TEST_F(DownloadUpdatesTest, InvalidResponse) {
sync_pb::GetUpdatesResponse gu_response;
diff --git a/sync/engine/sync_scheduler_impl.cc b/sync/engine/sync_scheduler_impl.cc
index 6a4f1ef..6c784bc 100644
--- a/sync/engine/sync_scheduler_impl.cc
+++ b/sync/engine/sync_scheduler_impl.cc
@@ -234,7 +234,8 @@ void SyncSchedulerImpl::Start(Mode mode) {
if (old_mode != mode_ &&
mode_ == NORMAL_MODE &&
- nudge_tracker_.IsSyncRequired() &&
+ (nudge_tracker_.IsSyncRequired() ||
+ nudge_tracker_.IsRetryRequired(base::TimeTicks::Now())) &&
CanRunNudgeJobNow(NORMAL_PRIORITY)) {
// We just got back to normal mode. Let's try to run the work that was
// queued up while we were configuring.
@@ -469,7 +470,7 @@ void SyncSchedulerImpl::DoNudgeSyncSessionJob(JobPriority priority) {
if (success) {
// That cycle took care of any outstanding work we had.
SDVLOG(2) << "Nudge succeeded.";
- nudge_tracker_.RecordSuccessfulSyncCycle();
+ nudge_tracker_.RecordSuccessfulSyncCycle(base::TimeTicks::Now());
scheduled_nudge_time_ = base::TimeTicks();
// If we're here, then we successfully reached the server. End all backoff.
@@ -549,18 +550,8 @@ void SyncSchedulerImpl::HandleFailure(
void SyncSchedulerImpl::DoPollSyncSessionJob() {
base::AutoReset<bool> protector(&no_scheduling_allowed_, true);
- if (!CanRunJobNow(NORMAL_PRIORITY)) {
- SDVLOG(2) << "Unable to run a poll job right now.";
- return;
- }
-
- if (mode_ != NORMAL_MODE) {
- SDVLOG(2) << "Not running poll job in configure mode.";
- return;
- }
-
SDVLOG(2) << "Polling with types "
- << ModelTypeSetToString(session_context_->enabled_types());
+ << ModelTypeSetToString(GetEnabledAndUnthrottledTypes());
scoped_ptr<SyncSession> session(SyncSession::Build(session_context_, this));
syncer_->PollSyncShare(
GetEnabledAndUnthrottledTypes(),
@@ -576,6 +567,25 @@ void SyncSchedulerImpl::DoPollSyncSessionJob() {
}
}
+void SyncSchedulerImpl::DoRetrySyncSessionJob() {
+ DCHECK(CalledOnValidThread());
+ DCHECK_EQ(mode_, NORMAL_MODE);
+
+ base::AutoReset<bool> protector(&no_scheduling_allowed_, true);
+
+ SDVLOG(2) << "Retrying with types "
+ << ModelTypeSetToString(GetEnabledAndUnthrottledTypes());
+ scoped_ptr<SyncSession> session(SyncSession::Build(session_context_, this));
+ if (syncer_->RetrySyncShare(GetEnabledAndUnthrottledTypes(),
+ session.get()) &&
+ !sessions::HasSyncerError(
+ session->status_controller().model_neutral_state())) {
+ nudge_tracker_.RecordSuccessfulSyncCycle(base::TimeTicks::Now());
+ } else {
+ HandleFailure(session->status_controller().model_neutral_state());
+ }
+}
+
void SyncSchedulerImpl::UpdateNudgeTimeRecords(ModelTypeSet types) {
DCHECK(CalledOnValidThread());
base::TimeTicks now = TimeTicks::Now();
@@ -683,11 +693,12 @@ void SyncSchedulerImpl::TrySyncSessionJobImpl() {
SDVLOG(2) << "Found pending configure job";
DoConfigurationSyncSessionJob(priority);
}
- } else {
- DCHECK(mode_ == NORMAL_MODE);
- if (nudge_tracker_.IsSyncRequired() && CanRunNudgeJobNow(priority)) {
+ } else if (CanRunNudgeJobNow(priority)) {
+ if (nudge_tracker_.IsSyncRequired()) {
SDVLOG(2) << "Found pending nudge job";
DoNudgeSyncSessionJob(priority);
+ } else if (nudge_tracker_.IsRetryRequired(base::TimeTicks::Now())) {
+ DoRetrySyncSessionJob();
} else if (do_poll_after_credentials_updated_ ||
((base::TimeTicks::Now() - last_poll_reset_) >= GetPollInterval())) {
DoPollSyncSessionJob();
@@ -737,6 +748,10 @@ void SyncSchedulerImpl::PollTimerCallback() {
TrySyncSessionJob();
}
+void SyncSchedulerImpl::RetryTimerCallback() {
+ TrySyncSessionJob();
+}
+
void SyncSchedulerImpl::Unthrottle() {
DCHECK(CalledOnValidThread());
DCHECK_EQ(WaitInterval::THROTTLED, wait_interval_->mode);
@@ -890,6 +905,12 @@ void SyncSchedulerImpl::OnSyncProtocolError(
OnActionableError(snapshot);
}
+void SyncSchedulerImpl::OnReceivedGuRetryDelay(const base::TimeDelta& delay) {
+ nudge_tracker_.set_next_retry_time(base::TimeTicks::Now() + delay);
+ retry_timer_.Start(FROM_HERE, delay, this,
+ &SyncSchedulerImpl::RetryTimerCallback);
+}
+
void SyncSchedulerImpl::SetNotificationsEnabled(bool notifications_enabled) {
DCHECK(CalledOnValidThread());
session_context_->set_notifications_enabled(notifications_enabled);
diff --git a/sync/engine/sync_scheduler_impl.h b/sync/engine/sync_scheduler_impl.h
index 81a4fcf..68d2a80 100644
--- a/sync/engine/sync_scheduler_impl.h
+++ b/sync/engine/sync_scheduler_impl.h
@@ -89,6 +89,10 @@ class SYNC_EXPORT_PRIVATE SyncSchedulerImpl
virtual void OnReceivedClientInvalidationHintBufferSize(int size) OVERRIDE;
virtual void OnSyncProtocolError(
const sessions::SyncSessionSnapshot& snapshot) OVERRIDE;
+ virtual void OnReceivedGuRetryDelay(const base::TimeDelta& delay) OVERRIDE;
+
+ // Returns true if the client is currently in exponential backoff.
+ bool IsBackingOff() const;
private:
enum JobPriority {
@@ -167,6 +171,9 @@ class SYNC_EXPORT_PRIVATE SyncSchedulerImpl
// Invoke the Syncer to perform a poll job.
void DoPollSyncSessionJob();
+ // Invoke the Syncer to perform a retry job.
+ void DoRetrySyncSessionJob();
+
// Helper function to calculate poll interval.
base::TimeDelta GetPollInterval();
@@ -191,9 +198,6 @@ class SYNC_EXPORT_PRIVATE SyncSchedulerImpl
const base::TimeDelta& delay,
const tracked_objects::Location& nudge_location);
- // Returns true if the client is currently in exponential backoff.
- bool IsBackingOff() const;
-
// Helper to signal all listeners registered with |session_context_|.
void Notify(SyncEngineEvent::EventCause cause);
@@ -230,6 +234,9 @@ class SYNC_EXPORT_PRIVATE SyncSchedulerImpl
// Creates a session for a poll and performs the sync.
void PollTimerCallback();
+ // Creates a session for a retry and performs the sync.
+ void RetryTimerCallback();
+
// Returns the set of types that are enabled and not currently throttled.
ModelTypeSet GetEnabledAndUnthrottledTypes();
@@ -336,6 +343,9 @@ class SYNC_EXPORT_PRIVATE SyncSchedulerImpl
// to be const and alleviate threading concerns.
base::WeakPtrFactory<SyncSchedulerImpl> weak_ptr_factory_for_weak_handle_;
+ // One-shot timer for scheduling GU retry according to delay set by server.
+ base::OneShotTimer<SyncSchedulerImpl> retry_timer_;
+
DISALLOW_COPY_AND_ASSIGN(SyncSchedulerImpl);
};
diff --git a/sync/engine/sync_scheduler_unittest.cc b/sync/engine/sync_scheduler_unittest.cc
index 97d9ab8..62aa2c9 100644
--- a/sync/engine/sync_scheduler_unittest.cc
+++ b/sync/engine/sync_scheduler_unittest.cc
@@ -34,6 +34,7 @@ using testing::Mock;
using testing::Return;
using testing::WithArg;
using testing::WithArgs;
+using testing::WithoutArgs;
namespace syncer {
using sessions::SyncSession;
@@ -51,6 +52,7 @@ class MockSyncer : public Syncer {
sync_pb::GetUpdatesCallerInfo::GetUpdatesSource,
SyncSession*));
MOCK_METHOD2(PollSyncShare, bool(ModelTypeSet, sessions::SyncSession*));
+ MOCK_METHOD2(RetrySyncShare, bool(ModelTypeSet, sessions::SyncSession*));
};
MockSyncer::MockSyncer()
@@ -210,6 +212,11 @@ class SyncSchedulerTest : public testing::Test {
return scheduler_->nudge_tracker_.GetThrottledTypes();
}
+ base::TimeDelta GetRetryTimerDelay() {
+ EXPECT_TRUE(scheduler_->retry_timer_.IsRunning());
+ return scheduler_->retry_timer_.GetCurrentDelay();
+ }
+
private:
syncable::Directory* directory() {
return dir_maker_.directory();
@@ -539,8 +546,9 @@ TEST_F(SyncSchedulerTest, Polling) {
SyncShareTimes times;
TimeDelta poll_interval(TimeDelta::FromMilliseconds(30));
EXPECT_CALL(*syncer(), PollSyncShare(_,_)).Times(AtLeast(kMinNumSamples))
- .WillRepeatedly(DoAll(Invoke(sessions::test_util::SimulatePollSuccess),
- RecordSyncShareMultiple(&times, kMinNumSamples)));
+ .WillRepeatedly(
+ DoAll(Invoke(sessions::test_util::SimulatePollRetrySuccess),
+ RecordSyncShareMultiple(&times, kMinNumSamples)));
scheduler()->OnReceivedLongPollIntervalUpdate(poll_interval);
@@ -559,8 +567,9 @@ TEST_F(SyncSchedulerTest, PollNotificationsDisabled) {
SyncShareTimes times;
TimeDelta poll_interval(TimeDelta::FromMilliseconds(30));
EXPECT_CALL(*syncer(), PollSyncShare(_,_)).Times(AtLeast(kMinNumSamples))
- .WillRepeatedly(DoAll(Invoke(sessions::test_util::SimulatePollSuccess),
- RecordSyncShareMultiple(&times, kMinNumSamples)));
+ .WillRepeatedly(
+ DoAll(Invoke(sessions::test_util::SimulatePollRetrySuccess),
+ RecordSyncShareMultiple(&times, kMinNumSamples)));
scheduler()->OnReceivedShortPollIntervalUpdate(poll_interval);
scheduler()->SetNotificationsEnabled(false);
@@ -587,7 +596,7 @@ TEST_F(SyncSchedulerTest, PollIntervalUpdate) {
sessions::test_util::SimulatePollIntervalUpdate(poll2)),
Return(true)))
.WillRepeatedly(
- DoAll(Invoke(sessions::test_util::SimulatePollSuccess),
+ DoAll(Invoke(sessions::test_util::SimulatePollRetrySuccess),
WithArg<1>(
RecordSyncShareMultiple(&times, kMinNumSamples))));
@@ -678,8 +687,9 @@ TEST_F(SyncSchedulerTest, ThrottlingExpiresFromPoll) {
Return(true)))
.RetiresOnSaturation();
EXPECT_CALL(*syncer(), PollSyncShare(_,_))
- .WillRepeatedly(DoAll(Invoke(sessions::test_util::SimulatePollSuccess),
- RecordSyncShareMultiple(&times, kMinNumSamples)));
+ .WillRepeatedly(
+ DoAll(Invoke(sessions::test_util::SimulatePollRetrySuccess),
+ RecordSyncShareMultiple(&times, kMinNumSamples)));
TimeTicks optimal_start = TimeTicks::Now() + poll + throttle1;
StartSyncScheduler(SyncScheduler::NORMAL_MODE);
@@ -1111,7 +1121,7 @@ TEST_F(SyncSchedulerTest, BackoffRelief) {
// Now let the Poll timer do its thing.
EXPECT_CALL(*syncer(), PollSyncShare(_,_))
.WillRepeatedly(DoAll(
- Invoke(sessions::test_util::SimulatePollSuccess),
+ Invoke(sessions::test_util::SimulatePollRetrySuccess),
RecordSyncShareMultiple(&times, kMinNumSamples)));
RunLoop();
Mock::VerifyAndClearExpectations(syncer());
@@ -1134,9 +1144,9 @@ TEST_F(SyncSchedulerTest, TransientPollFailure) {
UseMockDelayProvider(); // Will cause test failure if backoff is initiated.
EXPECT_CALL(*syncer(), PollSyncShare(_,_))
- .WillOnce(DoAll(Invoke(sessions::test_util::SimulatePollFailed),
+ .WillOnce(DoAll(Invoke(sessions::test_util::SimulatePollRetryFailed),
RecordSyncShare(&times)))
- .WillOnce(DoAll(Invoke(sessions::test_util::SimulatePollSuccess),
+ .WillOnce(DoAll(Invoke(sessions::test_util::SimulatePollRetrySuccess),
RecordSyncShare(&times)));
StartSyncScheduler(SyncScheduler::NORMAL_MODE);
@@ -1269,8 +1279,9 @@ TEST_F(SyncSchedulerTest, PollFromCanaryAfterAuthError) {
::testing::InSequence seq;
EXPECT_CALL(*syncer(), PollSyncShare(_,_))
- .WillRepeatedly(DoAll(Invoke(sessions::test_util::SimulatePollSuccess),
- RecordSyncShareMultiple(&times, kMinNumSamples)));
+ .WillRepeatedly(
+ DoAll(Invoke(sessions::test_util::SimulatePollRetrySuccess),
+ RecordSyncShareMultiple(&times, kMinNumSamples)));
connection()->SetServerStatus(HttpResponse::SYNC_AUTH_ERROR);
StartSyncScheduler(SyncScheduler::NORMAL_MODE);
@@ -1282,7 +1293,7 @@ TEST_F(SyncSchedulerTest, PollFromCanaryAfterAuthError) {
// but after poll finished with auth error from poll timer it should retry
// poll once more
EXPECT_CALL(*syncer(), PollSyncShare(_,_))
- .WillOnce(DoAll(Invoke(sessions::test_util::SimulatePollSuccess),
+ .WillOnce(DoAll(Invoke(sessions::test_util::SimulatePollRetrySuccess),
RecordSyncShare(&times)));
scheduler()->OnCredentialsUpdated();
connection()->SetServerStatus(HttpResponse::SERVER_CONNECTION_OK);
@@ -1290,4 +1301,89 @@ TEST_F(SyncSchedulerTest, PollFromCanaryAfterAuthError) {
StopSyncScheduler();
}
+TEST_F(SyncSchedulerTest, SuccessfulRetry) {
+ StartSyncScheduler(SyncScheduler::NORMAL_MODE);
+
+ SyncShareTimes times;
+ base::TimeDelta delay = base::TimeDelta::FromMilliseconds(1);
+ scheduler()->OnReceivedGuRetryDelay(delay);
+ EXPECT_EQ(delay, GetRetryTimerDelay());
+
+ EXPECT_CALL(*syncer(), RetrySyncShare(_,_))
+ .WillOnce(
+ DoAll(Invoke(sessions::test_util::SimulatePollRetrySuccess),
+ RecordSyncShare(&times)));
+
+ // Run to wait for retrying.
+ RunLoop();
+
+ StopSyncScheduler();
+}
+
+TEST_F(SyncSchedulerTest, FailedRetry) {
+ UseMockDelayProvider();
+ EXPECT_CALL(*delay(), GetDelay(_))
+ .WillRepeatedly(Return(TimeDelta::FromMilliseconds(1)));
+
+ StartSyncScheduler(SyncScheduler::NORMAL_MODE);
+
+ base::TimeDelta delay = base::TimeDelta::FromMilliseconds(1);
+ scheduler()->OnReceivedGuRetryDelay(delay);
+
+ EXPECT_CALL(*syncer(), RetrySyncShare(_,_))
+ .WillOnce(
+ DoAll(Invoke(sessions::test_util::SimulatePollRetryFailed),
+ QuitLoopNowAction()));
+
+ // Run to wait for retrying.
+ RunLoop();
+
+ EXPECT_TRUE(scheduler()->IsBackingOff());
+ EXPECT_CALL(*syncer(), RetrySyncShare(_,_))
+ .WillOnce(
+ DoAll(Invoke(sessions::test_util::SimulatePollRetrySuccess),
+ QuitLoopNowAction()));
+
+ // Run to wait for second retrying.
+ RunLoop();
+
+ StopSyncScheduler();
+}
+
+ACTION_P2(VerifyRetryTimerDelay, scheduler_test, expected_delay) {
+ EXPECT_EQ(expected_delay, scheduler_test->GetRetryTimerDelay());
+}
+
+TEST_F(SyncSchedulerTest, ReceiveNewRetryDelay) {
+ StartSyncScheduler(SyncScheduler::NORMAL_MODE);
+
+ SyncShareTimes times;
+ base::TimeDelta delay1 = base::TimeDelta::FromMilliseconds(100);
+ base::TimeDelta delay2 = base::TimeDelta::FromMilliseconds(200);
+
+ scheduler()->ScheduleLocalRefreshRequest(zero(), ModelTypeSet(BOOKMARKS),
+ FROM_HERE);
+ scheduler()->OnReceivedGuRetryDelay(delay1);
+
+ EXPECT_CALL(*syncer(), NormalSyncShare(_,_,_))
+ .WillOnce(DoAll(
+ WithoutArgs(VerifyRetryTimerDelay(this, delay1)),
+ WithArg<2>(sessions::test_util::SimulateGuRetryDelayCommand(delay2)),
+ WithoutArgs(VerifyRetryTimerDelay(this, delay2)),
+ RecordSyncShare(&times)));
+
+ // Run nudge GU.
+ RunLoop();
+
+ EXPECT_CALL(*syncer(), RetrySyncShare(_,_))
+ .WillOnce(
+ DoAll(Invoke(sessions::test_util::SimulatePollRetrySuccess),
+ RecordSyncShare(&times)));
+
+ // Run to wait for retrying.
+ RunLoop();
+
+ StopSyncScheduler();
+}
+
} // namespace syncer
diff --git a/sync/engine/syncer.cc b/sync/engine/syncer.cc
index 13e1f79..e75c1c6 100644
--- a/sync/engine/syncer.cc
+++ b/sync/engine/syncer.cc
@@ -57,7 +57,7 @@ bool Syncer::NormalSyncShare(ModelTypeSet request_types,
SyncSession* session) {
HandleCycleBegin(session);
VLOG(1) << "Downloading types " << ModelTypeSetToString(request_types);
- if (nudge_tracker.IsGetUpdatesRequired() ||
+ if (nudge_tracker.IsGetUpdatesRequired(base::TimeTicks::Now()) ||
session->context()->ShouldFetchUpdatesBeforeCommit()) {
if (!DownloadAndApplyUpdates(
request_types,
@@ -109,6 +109,20 @@ bool Syncer::PollSyncShare(ModelTypeSet request_types,
return HandleCycleEnd(session, sync_pb::GetUpdatesCallerInfo::PERIODIC);
}
+bool Syncer::RetrySyncShare(ModelTypeSet request_types,
+ SyncSession* session) {
+ HandleCycleBegin(session);
+ VLOG(1) << "Retrying types " << ModelTypeSetToString(request_types);
+ DownloadAndApplyUpdates(
+ request_types,
+ session,
+ base::Bind(&download::BuildDownloadUpdatesForRetry,
+ session,
+ kCreateMobileBookmarksFolder,
+ request_types));
+ return HandleCycleEnd(session, sync_pb::GetUpdatesCallerInfo::RETRY);
+}
+
void Syncer::ApplyUpdates(SyncSession* session) {
TRACE_EVENT0("sync", "ApplyUpdates");
diff --git a/sync/engine/syncer.h b/sync/engine/syncer.h
index 6154f91..225b4e3 100644
--- a/sync/engine/syncer.h
+++ b/sync/engine/syncer.h
@@ -65,6 +65,8 @@ class SYNC_EXPORT_PRIVATE Syncer {
// in sync despite bugs or transient failures.
virtual bool PollSyncShare(ModelTypeSet request_types,
sessions::SyncSession* session);
+ virtual bool RetrySyncShare(ModelTypeSet request_types,
+ sessions::SyncSession* session);
private:
void ApplyUpdates(sessions::SyncSession* session);
diff --git a/sync/engine/syncer_proto_util.cc b/sync/engine/syncer_proto_util.cc
index bfd8151..bcc0bd4 100644
--- a/sync/engine/syncer_proto_util.cc
+++ b/sync/engine/syncer_proto_util.cc
@@ -433,6 +433,11 @@ SyncerError SyncerProtoUtil::PostClientToServerMessage(
session->delegate()->OnReceivedClientInvalidationHintBufferSize(
command.client_invalidation_hint_buffer_size());
}
+
+ if (command.has_gu_retry_delay_seconds()) {
+ session->delegate()->OnReceivedGuRetryDelay(
+ base::TimeDelta::FromSeconds(command.gu_retry_delay_seconds()));
+ }
}
// Now do any special handling for the error type and decide on the return
diff --git a/sync/engine/syncer_unittest.cc b/sync/engine/syncer_unittest.cc
index 951d443c..119deb1 100644
--- a/sync/engine/syncer_unittest.cc
+++ b/sync/engine/syncer_unittest.cc
@@ -150,6 +150,7 @@ class SyncerTest : public testing::Test,
int size) OVERRIDE {
last_client_invalidation_hint_buffer_size_ = size;
}
+ virtual void OnReceivedGuRetryDelay(const base::TimeDelta& delay) OVERRIDE {}
virtual void OnSyncProtocolError(
const sessions::SyncSessionSnapshot& snapshot) OVERRIDE {
}
@@ -465,10 +466,10 @@ class SyncerTest : public testing::Test,
void ConfigureNoGetUpdatesRequired() {
context_->set_server_enabled_pre_commit_update_avoidance(true);
nudge_tracker_.OnInvalidationsEnabled();
- nudge_tracker_.RecordSuccessfulSyncCycle();
+ nudge_tracker_.RecordSuccessfulSyncCycle(base::TimeTicks::Now());
ASSERT_FALSE(context_->ShouldFetchUpdatesBeforeCommit());
- ASSERT_FALSE(nudge_tracker_.IsGetUpdatesRequired());
+ ASSERT_FALSE(nudge_tracker_.IsGetUpdatesRequired(base::TimeTicks::Now()));
}
base::MessageLoop message_loop_;