Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(26)

Unified Diff: sync/engine/sync_scheduler_impl.cc

Issue 1132013004: [Sync] Refactoring polling to be reliable. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Full patch Created 5 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « sync/engine/sync_scheduler_impl.h ('k') | sync/engine/sync_scheduler_unittest.cc » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: sync/engine/sync_scheduler_impl.cc
diff --git a/sync/engine/sync_scheduler_impl.cc b/sync/engine/sync_scheduler_impl.cc
index 6f76be0918cf930c80ff57493fb3070e352c7321..a1394fbe4d87a62af2c1f4895fd34e6d5a070f10 100644
--- a/sync/engine/sync_scheduler_impl.cc
+++ b/sync/engine/sync_scheduler_impl.cc
@@ -7,7 +7,6 @@
#include <algorithm>
#include <cstring>
-#include "base/auto_reset.h"
#include "base/bind.h"
#include "base/bind_helpers.h"
#include "base/compiler_specific.h"
@@ -156,12 +155,10 @@ SyncSchedulerImpl::SyncSchedulerImpl(const std::string& name,
TimeDelta::FromSeconds(kDefaultShortPollIntervalSeconds)),
syncer_long_poll_interval_seconds_(
TimeDelta::FromSeconds(kDefaultLongPollIntervalSeconds)),
- mode_(NORMAL_MODE),
+ mode_(CONFIGURATION_MODE),
delay_provider_(delay_provider),
syncer_(syncer),
session_context_(context),
- no_scheduling_allowed_(false),
- do_poll_after_credentials_updated_(false),
next_sync_session_job_priority_(NORMAL_PRIORITY),
weak_ptr_factory_(this),
weak_ptr_factory_for_weak_handle_(this) {
@@ -207,7 +204,7 @@ void SyncSchedulerImpl::OnServerConnectionErrorFixed() {
TryCanaryJob();
}
-void SyncSchedulerImpl::Start(Mode mode) {
+void SyncSchedulerImpl::Start(Mode mode, base::Time last_poll_time) {
DCHECK(CalledOnValidThread());
std::string thread_name = base::MessageLoop::current()->thread_name();
if (thread_name.empty())
@@ -223,12 +220,24 @@ void SyncSchedulerImpl::Start(Mode mode) {
DCHECK(syncer_.get());
Mode old_mode = mode_;
mode_ = mode;
- AdjustPolling(UPDATE_INTERVAL); // Will kick start poll timer if needed.
+ // Only adjust the poll reset time if it was valid and in the past.
+ if (!last_poll_time.is_null() && last_poll_time < base::Time::Now()) {
+ // Convert from base::Time to base::TimeTicks. The reason we use Time
+ // for persisting is that TimeTicks can stop making forward progress when
+ // the machine is suspended. This implies that on resume the client might
+ // actually have miss the real poll, unless the client is restarted. Fixing
+ // that would require using an AlarmTimer though, which is only supported
+ // on certain platforms.
+ last_poll_reset_ =
+ base::TimeTicks::Now() - (base::Time::Now() - last_poll_time);
+ }
if (old_mode != mode_ && mode_ == NORMAL_MODE) {
// We just got back to normal mode. Let's try to run the work that was
// queued up while we were configuring.
+ AdjustPolling(UPDATE_INTERVAL); // Will kick start poll timer if needed.
+
// Update our current time before checking IsRetryRequired().
nudge_tracker_.SetSyncCycleStartTime(base::TimeTicks::Now());
if (nudge_tracker_.IsSyncRequired() && CanRunNudgeJobNow(NORMAL_PRIORITY)) {
@@ -306,14 +315,12 @@ void SyncSchedulerImpl::ScheduleConfiguration(
bool SyncSchedulerImpl::CanRunJobNow(JobPriority priority) {
DCHECK(CalledOnValidThread());
- if (wait_interval_ && wait_interval_->mode == WaitInterval::THROTTLED) {
+ if (IsCurrentlyThrottled()) {
SDVLOG(1) << "Unable to run a job because we're throttled.";
return false;
}
- if (wait_interval_
- && wait_interval_->mode == WaitInterval::EXPONENTIAL_BACKOFF
- && priority != CANARY_PRIORITY) {
+ if (IsBackingOff() && priority != CANARY_PRIORITY) {
SDVLOG(1) << "Unable to run a job because we're backing off.";
return false;
}
@@ -404,11 +411,7 @@ void SyncSchedulerImpl::ScheduleNudgeImpl(
const TimeDelta& delay,
const tracked_objects::Location& nudge_location) {
DCHECK(CalledOnValidThread());
-
- if (no_scheduling_allowed_) {
- NOTREACHED() << "Illegal to schedule job while session in progress.";
- return;
- }
+ CHECK(!syncer_->IsSyncing());
if (!started_) {
SDVLOG_LOC(nudge_location, 2)
@@ -464,25 +467,23 @@ void SyncSchedulerImpl::DoNudgeSyncSessionJob(JobPriority priority) {
DVLOG(2) << "Will run normal mode sync cycle with types "
<< ModelTypeSetToString(session_context_->GetEnabledTypes());
scoped_ptr<SyncSession> session(SyncSession::Build(session_context_, this));
- bool premature_exit = !syncer_->NormalSyncShare(
+ bool success = syncer_->NormalSyncShare(
GetEnabledAndUnthrottledTypes(), &nudge_tracker_, session.get());
- AdjustPolling(FORCE_RESET);
- // Don't run poll job till the next time poll timer fires.
- do_poll_after_credentials_updated_ = false;
-
- bool success = !premature_exit
- && !sessions::HasSyncerError(
- session->status_controller().model_neutral_state());
if (success) {
// That cycle took care of any outstanding work we had.
SDVLOG(2) << "Nudge succeeded.";
nudge_tracker_.RecordSuccessfulSyncCycle();
scheduled_nudge_time_ = base::TimeTicks();
-
- // If we're here, then we successfully reached the server. End all backoff.
- wait_interval_.reset();
- NotifyRetryTime(base::Time());
+ HandleSuccess();
+
+ // If this was a canary, we may need to restart the poll timer (the poll
+ // timer may have fired while the scheduler was in an error state, ignoring
+ // the poll).
+ if (!poll_timer_.IsRunning()) {
+ SDVLOG(1) << "Canary succeeded, restarting polling.";
+ AdjustPolling(UPDATE_INTERVAL);
+ }
} else {
HandleFailure(session->status_controller().model_neutral_state());
}
@@ -505,26 +506,16 @@ void SyncSchedulerImpl::DoConfigurationSyncSessionJob(JobPriority priority) {
SDVLOG(2) << "Will run configure SyncShare with types "
<< ModelTypeSetToString(session_context_->GetEnabledTypes());
scoped_ptr<SyncSession> session(SyncSession::Build(session_context_, this));
- bool premature_exit = !syncer_->ConfigureSyncShare(
+ bool success = syncer_->ConfigureSyncShare(
pending_configure_params_->types_to_download,
pending_configure_params_->source,
session.get());
- AdjustPolling(FORCE_RESET);
- // Don't run poll job till the next time poll timer fires.
- do_poll_after_credentials_updated_ = false;
-
- bool success = !premature_exit
- && !sessions::HasSyncerError(
- session->status_controller().model_neutral_state());
if (success) {
SDVLOG(2) << "Configure succeeded.";
pending_configure_params_->ready_task.Run();
pending_configure_params_.reset();
-
- // If we're here, then we successfully reached the server. End all backoff.
- wait_interval_.reset();
- NotifyRetryTime(base::Time());
+ HandleSuccess();
} else {
HandleFailure(session->status_controller().model_neutral_state());
// Sync cycle might receive response from server that causes scheduler to
@@ -536,11 +527,16 @@ void SyncSchedulerImpl::DoConfigurationSyncSessionJob(JobPriority priority) {
}
}
+void SyncSchedulerImpl::HandleSuccess() {
+ // If we're here, then we successfully reached the server. End all backoff.
+ wait_interval_.reset();
+ NotifyRetryTime(base::Time());
+}
+
void SyncSchedulerImpl::HandleFailure(
const sessions::ModelNeutralState& model_neutral_state) {
if (IsCurrentlyThrottled()) {
SDVLOG(2) << "Was throttled during previous sync cycle.";
- RestartWaiting();
} else if (!IsBackingOff()) {
// Setup our backoff if this is our first such failure.
TimeDelta length = delay_provider_->GetDelay(
@@ -549,27 +545,32 @@ void SyncSchedulerImpl::HandleFailure(
new WaitInterval(WaitInterval::EXPONENTIAL_BACKOFF, length));
SDVLOG(2) << "Sync cycle failed. Will back off for "
<< wait_interval_->length.InMilliseconds() << "ms.";
- RestartWaiting();
+ } else {
+ // Increase our backoff interval and schedule another retry.
+ TimeDelta length = delay_provider_->GetDelay(wait_interval_->length);
+ wait_interval_.reset(
+ new WaitInterval(WaitInterval::EXPONENTIAL_BACKOFF, length));
+ SDVLOG(2) << "Sync cycle failed. Will back off for "
+ << wait_interval_->length.InMilliseconds() << "ms.";
}
+ RestartWaiting();
}
void SyncSchedulerImpl::DoPollSyncSessionJob() {
- base::AutoReset<bool> protector(&no_scheduling_allowed_, true);
-
SDVLOG(2) << "Polling with types "
<< ModelTypeSetToString(GetEnabledAndUnthrottledTypes());
scoped_ptr<SyncSession> session(SyncSession::Build(session_context_, this));
- syncer_->PollSyncShare(
+ bool success = syncer_->PollSyncShare(
GetEnabledAndUnthrottledTypes(),
session.get());
- AdjustPolling(FORCE_RESET);
-
- if (IsCurrentlyThrottled()) {
- SDVLOG(2) << "Poll request got us throttled.";
- // The OnSilencedUntil() call set up the WaitInterval for us. All we need
- // to do is start the timer.
- RestartWaiting();
+ // Only restart the timer if the poll succeeded. Otherwise rely on normal
+ // failure handling to retry with backoff.
+ if (success) {
+ AdjustPolling(FORCE_RESET);
+ HandleSuccess();
+ } else {
+ HandleFailure(session->status_controller().model_neutral_state());
}
}
@@ -599,23 +600,44 @@ TimeDelta SyncSchedulerImpl::GetPollInterval() {
void SyncSchedulerImpl::AdjustPolling(PollAdjustType type) {
DCHECK(CalledOnValidThread());
+ if (!started_)
+ return;
- TimeDelta poll = GetPollInterval();
- bool rate_changed = !poll_timer_.IsRunning() ||
- poll != poll_timer_.GetCurrentDelay();
-
- if (type == FORCE_RESET) {
- last_poll_reset_ = base::TimeTicks::Now();
- if (!rate_changed)
- poll_timer_.Reset();
+ TimeDelta poll_interval = GetPollInterval();
+ TimeDelta poll_delay = poll_interval;
+ const TimeTicks now = TimeTicks::Now();
+
+ if (type == UPDATE_INTERVAL) {
+ if (!last_poll_reset_.is_null()) {
+ // Override the delay based on the last successful poll time (if it was
+ // set).
+ TimeTicks new_poll_time = poll_interval + last_poll_reset_;
+ poll_delay = new_poll_time - TimeTicks::Now();
+
+ if (poll_delay < TimeDelta()) {
+ // The desired poll time was in the past, so trigger a poll now (the
+ // timer will post the task asynchronously, so re-entrancy isn't an
+ // issue).
+ poll_delay = TimeDelta();
+ }
+ } else {
+ // There was no previous poll. Keep the delay set to the normal interval,
+ // as if we had just completed a poll.
+ DCHECK_EQ(GetPollInterval(), poll_delay);
+ last_poll_reset_ = now;
+ }
+ } else {
+ // Otherwise just restart the timer.
+ DCHECK_EQ(FORCE_RESET, type);
+ DCHECK_EQ(GetPollInterval(), poll_delay);
+ last_poll_reset_ = now;
}
- if (!rate_changed)
- return;
+ SDVLOG(1) << "Updating polling delay to " << poll_delay.InMinutes()
+ << " minutes.";
- // Adjust poll rate.
- poll_timer_.Stop();
- poll_timer_.Start(FROM_HERE, poll, this,
+ // Adjust poll rate. Start will reset the timer if it was already running.
+ poll_timer_.Start(FROM_HERE, poll_delay, this,
&SyncSchedulerImpl::PollTimerCallback);
}
@@ -659,6 +681,7 @@ void SyncSchedulerImpl::Stop() {
// privileges. Everyone else should use NORMAL_PRIORITY.
void SyncSchedulerImpl::TryCanaryJob() {
next_sync_session_job_priority_ = CANARY_PRIORITY;
+ SDVLOG(2) << "Attempting canary job";
TrySyncSessionJob();
}
@@ -686,24 +709,16 @@ void SyncSchedulerImpl::TrySyncSessionJobImpl() {
if (nudge_tracker_.IsSyncRequired()) {
SDVLOG(2) << "Found pending nudge job";
DoNudgeSyncSessionJob(priority);
- } else if (do_poll_after_credentials_updated_ ||
- ((base::TimeTicks::Now() - last_poll_reset_) >= GetPollInterval())) {
+ } else if (((base::TimeTicks::Now() - last_poll_reset_) >=
+ GetPollInterval())) {
+ SDVLOG(2) << "Found pending poll";
DoPollSyncSessionJob();
- // Poll timer fires infrequently. Usually by this time access token is
- // already expired and poll job will fail with auth error. Set flag to
- // retry poll once ProfileSyncService gets new access token, TryCanaryJob
- // will be called after access token is retrieved.
- if (HttpResponse::SYNC_AUTH_ERROR ==
- session_context_->connection_manager()->server_status()) {
- do_poll_after_credentials_updated_ = true;
- }
}
- }
-
- if (priority == CANARY_PRIORITY) {
- // If this is canary job then whatever result was don't run poll job till
- // the next time poll timer fires.
- do_poll_after_credentials_updated_ = false;
+ } else {
+ // We must be in an error state. Transitioning out of each of these
+ // error states should trigger a canary job.
+ DCHECK(IsCurrentlyThrottled() || IsBackingOff() ||
+ session_context_->connection_manager()->HasInvalidAuthToken());
}
if (IsBackingOff() && !pending_wakeup_timer_.IsRunning()) {
@@ -712,7 +727,7 @@ void SyncSchedulerImpl::TrySyncSessionJobImpl() {
// another retry.
TimeDelta length = delay_provider_->GetDelay(wait_interval_->length);
wait_interval_.reset(
- new WaitInterval(WaitInterval::EXPONENTIAL_BACKOFF, length));
+ new WaitInterval(WaitInterval::EXPONENTIAL_BACKOFF, length));
SDVLOG(2) << "Sync cycle failed. Will back off for "
<< wait_interval_->length.InMilliseconds() << "ms.";
RestartWaiting();
@@ -721,16 +736,7 @@ void SyncSchedulerImpl::TrySyncSessionJobImpl() {
void SyncSchedulerImpl::PollTimerCallback() {
DCHECK(CalledOnValidThread());
- if (no_scheduling_allowed_) {
- // The no_scheduling_allowed_ flag is set by a function-scoped AutoReset in
- // functions that are called only on the sync thread. This function is also
- // called only on the sync thread, and only when it is posted by an expiring
- // timer. If we find that no_scheduling_allowed_ is set here, then
- // something is very wrong. Maybe someone mistakenly called us directly, or
- // mishandled the book-keeping for no_scheduling_allowed_.
- NOTREACHED() << "Illegal to schedule job while session in progress.";
- return;
- }
+ CHECK(!syncer_->IsSyncing());
TrySyncSessionJob();
}
@@ -826,6 +832,9 @@ void SyncSchedulerImpl::OnTypesThrottled(
const base::TimeDelta& throttle_duration) {
base::TimeTicks now = base::TimeTicks::Now();
+ SDVLOG(1) << "Throttling " << ModelTypeSetToString(types) << " for "
+ << throttle_duration.InMinutes() << " minutes.";
+
nudge_tracker_.SetTypesThrottledUntil(types, throttle_duration, now);
base::TimeDelta time_until_next_unthrottle =
nudge_tracker_.GetTimeUntilNextUnthrottle(now);
@@ -847,13 +856,23 @@ bool SyncSchedulerImpl::IsCurrentlyThrottled() {
void SyncSchedulerImpl::OnReceivedShortPollIntervalUpdate(
const base::TimeDelta& new_interval) {
DCHECK(CalledOnValidThread());
+ if (new_interval == syncer_short_poll_interval_seconds_)
+ return;
+ SDVLOG(1) << "Updating short poll interval to " << new_interval.InMinutes()
+ << " minutes.";
syncer_short_poll_interval_seconds_ = new_interval;
+ AdjustPolling(UPDATE_INTERVAL);
}
void SyncSchedulerImpl::OnReceivedLongPollIntervalUpdate(
const base::TimeDelta& new_interval) {
DCHECK(CalledOnValidThread());
+ if (new_interval == syncer_long_poll_interval_seconds_)
+ return;
+ SDVLOG(1) << "Updating long poll interval to " << new_interval.InMinutes()
+ << " minutes.";
syncer_long_poll_interval_seconds_ = new_interval;
+ AdjustPolling(UPDATE_INTERVAL);
}
void SyncSchedulerImpl::OnReceivedCustomNudgeDelays(
« no previous file with comments | « sync/engine/sync_scheduler_impl.h ('k') | sync/engine/sync_scheduler_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698