Index: sync/engine/sync_scheduler_impl.cc |
diff --git a/sync/engine/sync_scheduler_impl.cc b/sync/engine/sync_scheduler_impl.cc |
deleted file mode 100644 |
index d0926fddb3c56dc45da0f082165c9c4acc608aa9..0000000000000000000000000000000000000000 |
--- a/sync/engine/sync_scheduler_impl.cc |
+++ /dev/null |
@@ -1,999 +0,0 @@ |
-// Copyright (c) 2012 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/sync_scheduler_impl.h" |
- |
-#include <algorithm> |
-#include <cstring> |
-#include <utility> |
- |
-#include "base/bind.h" |
-#include "base/bind_helpers.h" |
-#include "base/compiler_specific.h" |
-#include "base/location.h" |
-#include "base/logging.h" |
-#include "base/single_thread_task_runner.h" |
-#include "base/threading/platform_thread.h" |
-#include "base/threading/thread_task_runner_handle.h" |
-#include "sync/engine/backoff_delay_provider.h" |
-#include "sync/engine/syncer.h" |
-#include "sync/protocol/proto_enum_conversions.h" |
-#include "sync/protocol/sync.pb.h" |
-#include "sync/util/data_type_histogram.h" |
-#include "sync/util/logging.h" |
- |
-using base::TimeDelta; |
-using base::TimeTicks; |
- |
-namespace syncer { |
- |
-using sessions::SyncSession; |
-using sessions::SyncSessionSnapshot; |
-using sync_pb::GetUpdatesCallerInfo; |
- |
-namespace { |
- |
-bool IsConfigRelatedUpdateSourceValue( |
- GetUpdatesCallerInfo::GetUpdatesSource source) { |
- switch (source) { |
- case GetUpdatesCallerInfo::RECONFIGURATION: |
- case GetUpdatesCallerInfo::MIGRATION: |
- case GetUpdatesCallerInfo::NEW_CLIENT: |
- case GetUpdatesCallerInfo::NEWLY_SUPPORTED_DATATYPE: |
- case GetUpdatesCallerInfo::PROGRAMMATIC: |
- return true; |
- default: |
- return false; |
- } |
-} |
- |
-bool ShouldRequestEarlyExit(const SyncProtocolError& error) { |
- switch (error.error_type) { |
- case SYNC_SUCCESS: |
- case MIGRATION_DONE: |
- case THROTTLED: |
- case TRANSIENT_ERROR: |
- case PARTIAL_FAILURE: |
- return false; |
- case NOT_MY_BIRTHDAY: |
- case CLIENT_DATA_OBSOLETE: |
- case CLEAR_PENDING: |
- case DISABLED_BY_ADMIN: |
- // If we send terminate sync early then |sync_cycle_ended| notification |
- // would not be sent. If there were no actions then |ACTIONABLE_ERROR| |
- // notification wouldnt be sent either. Then the UI layer would be left |
- // waiting forever. So assert we would send something. |
- DCHECK_NE(error.action, UNKNOWN_ACTION); |
- return true; |
- case INVALID_CREDENTIAL: |
- // The notification for this is handled by PostAndProcessHeaders|. |
- // Server does no have to send any action for this. |
- return true; |
- // Make UNKNOWN_ERROR a NOTREACHED. All the other error should be explicitly |
- // handled. |
- case UNKNOWN_ERROR: |
- NOTREACHED(); |
- return false; |
- } |
- return false; |
-} |
- |
-bool IsActionableError( |
- const SyncProtocolError& error) { |
- return (error.action != UNKNOWN_ACTION); |
-} |
- |
-void RunAndReset(base::Closure* task) { |
- DCHECK(task); |
- if (task->is_null()) |
- return; |
- task->Run(); |
- task->Reset(); |
-} |
- |
-} // namespace |
- |
-ConfigurationParams::ConfigurationParams() |
- : source(GetUpdatesCallerInfo::UNKNOWN) {} |
-ConfigurationParams::ConfigurationParams( |
- const sync_pb::GetUpdatesCallerInfo::GetUpdatesSource& source, |
- ModelTypeSet types_to_download, |
- const ModelSafeRoutingInfo& routing_info, |
- const base::Closure& ready_task, |
- const base::Closure& retry_task) |
- : source(source), |
- types_to_download(types_to_download), |
- routing_info(routing_info), |
- ready_task(ready_task), |
- retry_task(retry_task) { |
- DCHECK(!ready_task.is_null()); |
-} |
-ConfigurationParams::ConfigurationParams(const ConfigurationParams& other) = |
- default; |
-ConfigurationParams::~ConfigurationParams() {} |
- |
-ClearParams::ClearParams(const base::Closure& report_success_task) |
- : report_success_task(report_success_task) { |
- DCHECK(!report_success_task.is_null()); |
-} |
-ClearParams::ClearParams(const ClearParams& other) = default; |
-ClearParams::~ClearParams() {} |
- |
-SyncSchedulerImpl::WaitInterval::WaitInterval() |
- : mode(UNKNOWN) {} |
- |
-SyncSchedulerImpl::WaitInterval::WaitInterval(Mode mode, TimeDelta length) |
- : mode(mode), length(length) {} |
- |
-SyncSchedulerImpl::WaitInterval::~WaitInterval() {} |
- |
-#define ENUM_CASE(x) case x: return #x; break; |
- |
-const char* SyncSchedulerImpl::WaitInterval::GetModeString(Mode mode) { |
- switch (mode) { |
- ENUM_CASE(UNKNOWN); |
- ENUM_CASE(EXPONENTIAL_BACKOFF); |
- ENUM_CASE(THROTTLED); |
- } |
- NOTREACHED(); |
- return ""; |
-} |
- |
-GetUpdatesCallerInfo::GetUpdatesSource GetUpdatesFromNudgeSource( |
- NudgeSource source) { |
- switch (source) { |
- case NUDGE_SOURCE_NOTIFICATION: |
- return GetUpdatesCallerInfo::NOTIFICATION; |
- case NUDGE_SOURCE_LOCAL: |
- return GetUpdatesCallerInfo::LOCAL; |
- case NUDGE_SOURCE_LOCAL_REFRESH: |
- return GetUpdatesCallerInfo::DATATYPE_REFRESH; |
- case NUDGE_SOURCE_UNKNOWN: |
- return GetUpdatesCallerInfo::UNKNOWN; |
- default: |
- NOTREACHED(); |
- return GetUpdatesCallerInfo::UNKNOWN; |
- } |
-} |
- |
-// Helper macros to log with the syncer thread name; useful when there |
-// are multiple syncer threads involved. |
- |
-#define SLOG(severity) LOG(severity) << name_ << ": " |
- |
-#define SDVLOG(verbose_level) DVLOG(verbose_level) << name_ << ": " |
- |
-#define SDVLOG_LOC(from_here, verbose_level) \ |
- DVLOG_LOC(from_here, verbose_level) << name_ << ": " |
- |
-SyncSchedulerImpl::SyncSchedulerImpl(const std::string& name, |
- BackoffDelayProvider* delay_provider, |
- sessions::SyncSessionContext* context, |
- Syncer* syncer) |
- : name_(name), |
- started_(false), |
- syncer_short_poll_interval_seconds_( |
- TimeDelta::FromSeconds(kDefaultShortPollIntervalSeconds)), |
- syncer_long_poll_interval_seconds_( |
- TimeDelta::FromSeconds(kDefaultLongPollIntervalSeconds)), |
- mode_(CONFIGURATION_MODE), |
- delay_provider_(delay_provider), |
- syncer_(syncer), |
- session_context_(context), |
- next_sync_session_job_priority_(NORMAL_PRIORITY), |
- weak_ptr_factory_(this), |
- weak_ptr_factory_for_weak_handle_(this) { |
- weak_handle_this_ = MakeWeakHandle( |
- weak_ptr_factory_for_weak_handle_.GetWeakPtr()); |
-} |
- |
-SyncSchedulerImpl::~SyncSchedulerImpl() { |
- DCHECK(CalledOnValidThread()); |
- Stop(); |
-} |
- |
-void SyncSchedulerImpl::OnCredentialsUpdated() { |
- DCHECK(CalledOnValidThread()); |
- |
- if (HttpResponse::SYNC_AUTH_ERROR == |
- session_context_->connection_manager()->server_status()) { |
- OnServerConnectionErrorFixed(); |
- } |
-} |
- |
-void SyncSchedulerImpl::OnConnectionStatusChange() { |
- if (HttpResponse::CONNECTION_UNAVAILABLE == |
- session_context_->connection_manager()->server_status()) { |
- // Optimistically assume that the connection is fixed and try |
- // connecting. |
- OnServerConnectionErrorFixed(); |
- } |
-} |
- |
-void SyncSchedulerImpl::OnServerConnectionErrorFixed() { |
- // There could be a pending nudge or configuration job in several cases: |
- // |
- // 1. We're in exponential backoff. |
- // 2. We're silenced / throttled. |
- // 3. A nudge was saved previously due to not having a valid auth token. |
- // 4. A nudge was scheduled + saved while in configuration mode. |
- // |
- // In all cases except (2), we want to retry contacting the server. We |
- // call TryCanaryJob to achieve this, and note that nothing -- not even a |
- // canary job -- can bypass a THROTTLED WaitInterval. The only thing that |
- // has the authority to do that is the Unthrottle timer. |
- TryCanaryJob(); |
-} |
- |
-void SyncSchedulerImpl::Start(Mode mode, base::Time last_poll_time) { |
- DCHECK(CalledOnValidThread()); |
- std::string thread_name = base::PlatformThread::GetName(); |
- if (thread_name.empty()) |
- thread_name = "<Main thread>"; |
- SDVLOG(2) << "Start called from thread " |
- << thread_name << " with mode " << GetModeString(mode); |
- if (!started_) { |
- started_ = true; |
- SendInitialSnapshot(); |
- } |
- |
- DCHECK(syncer_.get()); |
- |
- if (mode == CLEAR_SERVER_DATA_MODE) { |
- DCHECK_EQ(mode_, CONFIGURATION_MODE); |
- } |
- Mode old_mode = mode_; |
- mode_ = mode; |
- // 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)) { |
- TrySyncSessionJob(); |
- } |
- } |
-} |
- |
-ModelTypeSet SyncSchedulerImpl::GetEnabledAndUnthrottledTypes() { |
- ModelTypeSet enabled_types = session_context_->GetEnabledTypes(); |
- ModelTypeSet enabled_protocol_types = |
- Intersection(ProtocolTypes(), enabled_types); |
- ModelTypeSet throttled_types = nudge_tracker_.GetThrottledTypes(); |
- return Difference(enabled_protocol_types, throttled_types); |
-} |
- |
-void SyncSchedulerImpl::SendInitialSnapshot() { |
- DCHECK(CalledOnValidThread()); |
- std::unique_ptr<SyncSession> dummy( |
- SyncSession::Build(session_context_, this)); |
- SyncCycleEvent event(SyncCycleEvent::STATUS_CHANGED); |
- event.snapshot = dummy->TakeSnapshot(); |
- FOR_EACH_OBSERVER(SyncEngineEventListener, |
- *session_context_->listeners(), |
- OnSyncCycleEvent(event)); |
-} |
- |
-namespace { |
- |
-// Helper to extract the routing info corresponding to types in |
-// |types_to_download| from |current_routes|. |
-void BuildModelSafeParams( |
- ModelTypeSet types_to_download, |
- const ModelSafeRoutingInfo& current_routes, |
- ModelSafeRoutingInfo* result_routes) { |
- for (ModelTypeSet::Iterator iter = types_to_download.First(); iter.Good(); |
- iter.Inc()) { |
- ModelType type = iter.Get(); |
- ModelSafeRoutingInfo::const_iterator route = current_routes.find(type); |
- DCHECK(route != current_routes.end()); |
- ModelSafeGroup group = route->second; |
- (*result_routes)[type] = group; |
- } |
-} |
- |
-} // namespace. |
- |
-void SyncSchedulerImpl::ScheduleConfiguration( |
- const ConfigurationParams& params) { |
- DCHECK(CalledOnValidThread()); |
- DCHECK(IsConfigRelatedUpdateSourceValue(params.source)); |
- DCHECK_EQ(CONFIGURATION_MODE, mode_); |
- DCHECK(!params.ready_task.is_null()); |
- CHECK(started_) << "Scheduler must be running to configure."; |
- SDVLOG(2) << "Reconfiguring syncer."; |
- |
- // Only one configuration is allowed at a time. Verify we're not waiting |
- // for a pending configure job. |
- DCHECK(!pending_configure_params_); |
- |
- ModelSafeRoutingInfo restricted_routes; |
- BuildModelSafeParams(params.types_to_download, |
- params.routing_info, |
- &restricted_routes); |
- session_context_->SetRoutingInfo(restricted_routes); |
- |
- // Only reconfigure if we have types to download. |
- if (!params.types_to_download.Empty()) { |
- pending_configure_params_.reset(new ConfigurationParams(params)); |
- TrySyncSessionJob(); |
- } else { |
- SDVLOG(2) << "No change in routing info, calling ready task directly."; |
- params.ready_task.Run(); |
- } |
-} |
- |
-void SyncSchedulerImpl::ScheduleClearServerData(const ClearParams& params) { |
- DCHECK(CalledOnValidThread()); |
- DCHECK_EQ(CLEAR_SERVER_DATA_MODE, mode_); |
- DCHECK(!pending_configure_params_); |
- DCHECK(!params.report_success_task.is_null()); |
- CHECK(started_) << "Scheduler must be running to clear."; |
- pending_clear_params_.reset(new ClearParams(params)); |
- TrySyncSessionJob(); |
-} |
- |
-bool SyncSchedulerImpl::CanRunJobNow(JobPriority priority) { |
- DCHECK(CalledOnValidThread()); |
- if (IsCurrentlyThrottled()) { |
- SDVLOG(1) << "Unable to run a job because we're throttled."; |
- return false; |
- } |
- |
- if (IsBackingOff() && priority != CANARY_PRIORITY) { |
- SDVLOG(1) << "Unable to run a job because we're backing off."; |
- return false; |
- } |
- |
- if (session_context_->connection_manager()->HasInvalidAuthToken()) { |
- SDVLOG(1) << "Unable to run a job because we have no valid auth token."; |
- return false; |
- } |
- |
- return true; |
-} |
- |
-bool SyncSchedulerImpl::CanRunNudgeJobNow(JobPriority priority) { |
- DCHECK(CalledOnValidThread()); |
- |
- if (!CanRunJobNow(priority)) { |
- SDVLOG(1) << "Unable to run a nudge job right now"; |
- return false; |
- } |
- |
- const ModelTypeSet enabled_types = session_context_->GetEnabledTypes(); |
- if (nudge_tracker_.GetThrottledTypes().HasAll(enabled_types)) { |
- SDVLOG(1) << "Not running a nudge because we're fully type throttled."; |
- return false; |
- } |
- |
- if (mode_ != NORMAL_MODE) { |
- SDVLOG(1) << "Not running nudge because we're not in normal mode."; |
- return false; |
- } |
- |
- return true; |
-} |
- |
-void SyncSchedulerImpl::ScheduleLocalNudge( |
- ModelTypeSet types, |
- const tracked_objects::Location& nudge_location) { |
- DCHECK(CalledOnValidThread()); |
- DCHECK(!types.Empty()); |
- |
- SDVLOG_LOC(nudge_location, 2) |
- << "Scheduling sync because of local change to " |
- << ModelTypeSetToString(types); |
- UpdateNudgeTimeRecords(types); |
- base::TimeDelta nudge_delay = nudge_tracker_.RecordLocalChange(types); |
- ScheduleNudgeImpl(nudge_delay, nudge_location); |
-} |
- |
-void SyncSchedulerImpl::ScheduleLocalRefreshRequest( |
- ModelTypeSet types, |
- const tracked_objects::Location& nudge_location) { |
- DCHECK(CalledOnValidThread()); |
- DCHECK(!types.Empty()); |
- |
- SDVLOG_LOC(nudge_location, 2) |
- << "Scheduling sync because of local refresh request for " |
- << ModelTypeSetToString(types); |
- base::TimeDelta nudge_delay = nudge_tracker_.RecordLocalRefreshRequest(types); |
- ScheduleNudgeImpl(nudge_delay, nudge_location); |
-} |
- |
-void SyncSchedulerImpl::ScheduleInvalidationNudge( |
- syncer::ModelType model_type, |
- std::unique_ptr<InvalidationInterface> invalidation, |
- const tracked_objects::Location& nudge_location) { |
- DCHECK(CalledOnValidThread()); |
- |
- SDVLOG_LOC(nudge_location, 2) |
- << "Scheduling sync because we received invalidation for " |
- << ModelTypeToString(model_type); |
- base::TimeDelta nudge_delay = nudge_tracker_.RecordRemoteInvalidation( |
- model_type, std::move(invalidation)); |
- ScheduleNudgeImpl(nudge_delay, nudge_location); |
-} |
- |
-void SyncSchedulerImpl::ScheduleInitialSyncNudge(syncer::ModelType model_type) { |
- DCHECK(CalledOnValidThread()); |
- |
- SDVLOG(2) << "Scheduling non-blocking initial sync for " |
- << ModelTypeToString(model_type); |
- nudge_tracker_.RecordInitialSyncRequired(model_type); |
- ScheduleNudgeImpl(TimeDelta::FromSeconds(0), FROM_HERE); |
-} |
- |
-// TODO(zea): Consider adding separate throttling/backoff for datatype |
-// refresh requests. |
-void SyncSchedulerImpl::ScheduleNudgeImpl( |
- const TimeDelta& delay, |
- const tracked_objects::Location& nudge_location) { |
- DCHECK(CalledOnValidThread()); |
- CHECK(!syncer_->IsSyncing()); |
- |
- if (!started_) { |
- SDVLOG_LOC(nudge_location, 2) |
- << "Dropping nudge, scheduler is not running."; |
- return; |
- } |
- |
- SDVLOG_LOC(nudge_location, 2) |
- << "In ScheduleNudgeImpl with delay " |
- << delay.InMilliseconds() << " ms"; |
- |
- if (!CanRunNudgeJobNow(NORMAL_PRIORITY)) |
- return; |
- |
- TimeTicks incoming_run_time = TimeTicks::Now() + delay; |
- if (!scheduled_nudge_time_.is_null() && |
- (scheduled_nudge_time_ < incoming_run_time)) { |
- // Old job arrives sooner than this one. Don't reschedule it. |
- return; |
- } |
- |
- // Either there is no existing nudge in flight or the incoming nudge should be |
- // made to arrive first (preempt) the existing nudge. We reschedule in either |
- // case. |
- SDVLOG_LOC(nudge_location, 2) |
- << "Scheduling a nudge with " |
- << delay.InMilliseconds() << " ms delay"; |
- scheduled_nudge_time_ = incoming_run_time; |
- pending_wakeup_timer_.Start( |
- nudge_location, |
- delay, |
- base::Bind(&SyncSchedulerImpl::PerformDelayedNudge, |
- weak_ptr_factory_.GetWeakPtr())); |
-} |
- |
-const char* SyncSchedulerImpl::GetModeString(SyncScheduler::Mode mode) { |
- switch (mode) { |
- ENUM_CASE(CONFIGURATION_MODE); |
- ENUM_CASE(CLEAR_SERVER_DATA_MODE); |
- ENUM_CASE(NORMAL_MODE); |
- } |
- return ""; |
-} |
- |
-void SyncSchedulerImpl::SetDefaultNudgeDelay(base::TimeDelta delay_ms) { |
- DCHECK(CalledOnValidThread()); |
- nudge_tracker_.SetDefaultNudgeDelay(delay_ms); |
-} |
- |
-void SyncSchedulerImpl::DoNudgeSyncSessionJob(JobPriority priority) { |
- DCHECK(CalledOnValidThread()); |
- DCHECK(CanRunNudgeJobNow(priority)); |
- |
- DVLOG(2) << "Will run normal mode sync cycle with types " |
- << ModelTypeSetToString(session_context_->GetEnabledTypes()); |
- std::unique_ptr<SyncSession> session( |
- SyncSession::Build(session_context_, this)); |
- bool success = syncer_->NormalSyncShare( |
- GetEnabledAndUnthrottledTypes(), &nudge_tracker_, session.get()); |
- |
- if (success) { |
- // That cycle took care of any outstanding work we had. |
- SDVLOG(2) << "Nudge succeeded."; |
- nudge_tracker_.RecordSuccessfulSyncCycle(); |
- scheduled_nudge_time_ = base::TimeTicks(); |
- 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()); |
- } |
-} |
- |
-void SyncSchedulerImpl::DoConfigurationSyncSessionJob(JobPriority priority) { |
- DCHECK(CalledOnValidThread()); |
- DCHECK_EQ(mode_, CONFIGURATION_MODE); |
- DCHECK(pending_configure_params_ != NULL); |
- |
- if (!CanRunJobNow(priority)) { |
- SDVLOG(2) << "Unable to run configure job right now."; |
- RunAndReset(&pending_configure_params_->retry_task); |
- return; |
- } |
- |
- SDVLOG(2) << "Will run configure SyncShare with types " |
- << ModelTypeSetToString(session_context_->GetEnabledTypes()); |
- std::unique_ptr<SyncSession> session( |
- SyncSession::Build(session_context_, this)); |
- bool success = syncer_->ConfigureSyncShare( |
- pending_configure_params_->types_to_download, |
- pending_configure_params_->source, |
- session.get()); |
- |
- if (success) { |
- SDVLOG(2) << "Configure succeeded."; |
- pending_configure_params_->ready_task.Run(); |
- pending_configure_params_.reset(); |
- HandleSuccess(); |
- } else { |
- HandleFailure(session->status_controller().model_neutral_state()); |
- // Sync cycle might receive response from server that causes scheduler to |
- // stop and draws pending_configure_params_ invalid. |
- if (started_) |
- RunAndReset(&pending_configure_params_->retry_task); |
- } |
-} |
- |
-void SyncSchedulerImpl::DoClearServerDataSyncSessionJob(JobPriority priority) { |
- DCHECK(CalledOnValidThread()); |
- DCHECK_EQ(mode_, CLEAR_SERVER_DATA_MODE); |
- |
- if (!CanRunJobNow(priority)) { |
- SDVLOG(2) << "Unable to run clear server data job right now."; |
- RunAndReset(&pending_configure_params_->retry_task); |
- return; |
- } |
- |
- std::unique_ptr<SyncSession> session( |
- SyncSession::Build(session_context_, this)); |
- const bool success = syncer_->PostClearServerData(session.get()); |
- if (!success) { |
- HandleFailure(session->status_controller().model_neutral_state()); |
- return; |
- } |
- |
- SDVLOG(2) << "Clear succeeded."; |
- pending_clear_params_->report_success_task.Run(); |
- pending_clear_params_.reset(); |
- HandleSuccess(); |
-} |
- |
-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."; |
- } else if (!IsBackingOff()) { |
- // Setup our backoff if this is our first such failure. |
- TimeDelta length = delay_provider_->GetDelay( |
- delay_provider_->GetInitialDelay(model_neutral_state)); |
- wait_interval_.reset( |
- new WaitInterval(WaitInterval::EXPONENTIAL_BACKOFF, length)); |
- SDVLOG(2) << "Sync cycle failed. Will back off for " |
- << wait_interval_->length.InMilliseconds() << "ms."; |
- } 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() { |
- SDVLOG(2) << "Polling with types " |
- << ModelTypeSetToString(GetEnabledAndUnthrottledTypes()); |
- std::unique_ptr<SyncSession> session( |
- SyncSession::Build(session_context_, this)); |
- bool success = syncer_->PollSyncShare( |
- GetEnabledAndUnthrottledTypes(), |
- session.get()); |
- |
- // 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()); |
- } |
-} |
- |
-void SyncSchedulerImpl::UpdateNudgeTimeRecords(ModelTypeSet types) { |
- DCHECK(CalledOnValidThread()); |
- base::TimeTicks now = TimeTicks::Now(); |
- // Update timing information for how often datatypes are triggering nudges. |
- for (ModelTypeSet::Iterator iter = types.First(); iter.Good(); iter.Inc()) { |
- base::TimeTicks previous = last_local_nudges_by_model_type_[iter.Get()]; |
- last_local_nudges_by_model_type_[iter.Get()] = now; |
- if (previous.is_null()) |
- continue; |
- |
-#define PER_DATA_TYPE_MACRO(type_str) \ |
- SYNC_FREQ_HISTOGRAM("Sync.Freq" type_str, now - previous); |
- SYNC_DATA_TYPE_HISTOGRAM(iter.Get()); |
-#undef PER_DATA_TYPE_MACRO |
- } |
-} |
- |
-TimeDelta SyncSchedulerImpl::GetPollInterval() { |
- return (!session_context_->notifications_enabled() || |
- !session_context_->ShouldFetchUpdatesBeforeCommit()) ? |
- syncer_short_poll_interval_seconds_ : |
- syncer_long_poll_interval_seconds_; |
-} |
- |
-void SyncSchedulerImpl::AdjustPolling(PollAdjustType type) { |
- DCHECK(CalledOnValidThread()); |
- if (!started_) |
- return; |
- |
- 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; |
- } |
- |
- SDVLOG(1) << "Updating polling delay to " << poll_delay.InMinutes() |
- << " minutes."; |
- |
- // Adjust poll rate. Start will reset the timer if it was already running. |
- poll_timer_.Start(FROM_HERE, poll_delay, this, |
- &SyncSchedulerImpl::PollTimerCallback); |
-} |
- |
-void SyncSchedulerImpl::RestartWaiting() { |
- CHECK(wait_interval_.get()); |
- DCHECK(wait_interval_->length >= TimeDelta::FromSeconds(0)); |
- NotifyRetryTime(base::Time::Now() + wait_interval_->length); |
- SDVLOG(2) << "Starting WaitInterval timer of length " |
- << wait_interval_->length.InMilliseconds() << "ms."; |
- if (wait_interval_->mode == WaitInterval::THROTTLED) { |
- pending_wakeup_timer_.Start( |
- FROM_HERE, |
- wait_interval_->length, |
- base::Bind(&SyncSchedulerImpl::Unthrottle, |
- weak_ptr_factory_.GetWeakPtr())); |
- } else { |
- pending_wakeup_timer_.Start( |
- FROM_HERE, |
- wait_interval_->length, |
- base::Bind(&SyncSchedulerImpl::ExponentialBackoffRetry, |
- weak_ptr_factory_.GetWeakPtr())); |
- } |
-} |
- |
-void SyncSchedulerImpl::Stop() { |
- DCHECK(CalledOnValidThread()); |
- SDVLOG(2) << "Stop called"; |
- |
- // Kill any in-flight method calls. |
- weak_ptr_factory_.InvalidateWeakPtrs(); |
- wait_interval_.reset(); |
- NotifyRetryTime(base::Time()); |
- poll_timer_.Stop(); |
- pending_wakeup_timer_.Stop(); |
- pending_configure_params_.reset(); |
- pending_clear_params_.reset(); |
- if (started_) |
- started_ = false; |
-} |
- |
-// This is the only place where we invoke DoSyncSessionJob with canary |
-// privileges. Everyone else should use NORMAL_PRIORITY. |
-void SyncSchedulerImpl::TryCanaryJob() { |
- next_sync_session_job_priority_ = CANARY_PRIORITY; |
- SDVLOG(2) << "Attempting canary job"; |
- TrySyncSessionJob(); |
-} |
- |
-void SyncSchedulerImpl::TrySyncSessionJob() { |
- // Post call to TrySyncSessionJobImpl on current thread. Later request for |
- // access token will be here. |
- base::ThreadTaskRunnerHandle::Get()->PostTask( |
- FROM_HERE, base::Bind(&SyncSchedulerImpl::TrySyncSessionJobImpl, |
- weak_ptr_factory_.GetWeakPtr())); |
-} |
- |
-void SyncSchedulerImpl::TrySyncSessionJobImpl() { |
- JobPriority priority = next_sync_session_job_priority_; |
- next_sync_session_job_priority_ = NORMAL_PRIORITY; |
- |
- nudge_tracker_.SetSyncCycleStartTime(base::TimeTicks::Now()); |
- |
- DCHECK(CalledOnValidThread()); |
- if (mode_ == CONFIGURATION_MODE) { |
- if (pending_configure_params_) { |
- SDVLOG(2) << "Found pending configure job"; |
- DoConfigurationSyncSessionJob(priority); |
- } |
- } else if (mode_ == CLEAR_SERVER_DATA_MODE) { |
- if (pending_clear_params_) { |
- DoClearServerDataSyncSessionJob(priority); |
- } |
- } else if (CanRunNudgeJobNow(priority)) { |
- if (nudge_tracker_.IsSyncRequired()) { |
- SDVLOG(2) << "Found pending nudge job"; |
- DoNudgeSyncSessionJob(priority); |
- } else if (((base::TimeTicks::Now() - last_poll_reset_) >= |
- GetPollInterval())) { |
- SDVLOG(2) << "Found pending poll"; |
- DoPollSyncSessionJob(); |
- } |
- } 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()) { |
- // If we succeeded, our wait interval would have been cleared. If it hasn't |
- // been cleared, then we should 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::PollTimerCallback() { |
- DCHECK(CalledOnValidThread()); |
- CHECK(!syncer_->IsSyncing()); |
- |
- TrySyncSessionJob(); |
-} |
- |
-void SyncSchedulerImpl::RetryTimerCallback() { |
- TrySyncSessionJob(); |
-} |
- |
-void SyncSchedulerImpl::Unthrottle() { |
- DCHECK(CalledOnValidThread()); |
- DCHECK_EQ(WaitInterval::THROTTLED, wait_interval_->mode); |
- |
- // We're no longer throttled, so clear the wait interval. |
- wait_interval_.reset(); |
- NotifyRetryTime(base::Time()); |
- NotifyThrottledTypesChanged(nudge_tracker_.GetThrottledTypes()); |
- |
- // We treat this as a 'canary' in the sense that it was originally scheduled |
- // to run some time ago, failed, and we now want to retry, versus a job that |
- // was just created (e.g via ScheduleNudgeImpl). The main implication is |
- // that we're careful to update routing info (etc) with such potentially |
- // stale canary jobs. |
- TryCanaryJob(); |
-} |
- |
-void SyncSchedulerImpl::TypeUnthrottle(base::TimeTicks unthrottle_time) { |
- DCHECK(CalledOnValidThread()); |
- nudge_tracker_.UpdateTypeThrottlingState(unthrottle_time); |
- NotifyThrottledTypesChanged(nudge_tracker_.GetThrottledTypes()); |
- |
- if (nudge_tracker_.IsAnyTypeThrottled()) { |
- const base::TimeTicks now = base::TimeTicks::Now(); |
- base::TimeDelta time_until_next_unthrottle = |
- nudge_tracker_.GetTimeUntilNextUnthrottle(now); |
- type_unthrottle_timer_.Start( |
- FROM_HERE, |
- time_until_next_unthrottle, |
- base::Bind(&SyncSchedulerImpl::TypeUnthrottle, |
- weak_ptr_factory_.GetWeakPtr(), |
- now + time_until_next_unthrottle)); |
- } |
- |
- // Maybe this is a good time to run a nudge job. Let's try it. |
- if (nudge_tracker_.IsSyncRequired() && CanRunNudgeJobNow(NORMAL_PRIORITY)) |
- TrySyncSessionJob(); |
-} |
- |
-void SyncSchedulerImpl::PerformDelayedNudge() { |
- // Circumstances may have changed since we scheduled this delayed nudge. |
- // We must check to see if it's OK to run the job before we do so. |
- if (CanRunNudgeJobNow(NORMAL_PRIORITY)) |
- TrySyncSessionJob(); |
- |
- // We're not responsible for setting up any retries here. The functions that |
- // first put us into a state that prevents successful sync cycles (eg. global |
- // throttling, type throttling, network errors, transient errors) will also |
- // setup the appropriate retry logic (eg. retry after timeout, exponential |
- // backoff, retry when the network changes). |
-} |
- |
-void SyncSchedulerImpl::ExponentialBackoffRetry() { |
- TryCanaryJob(); |
-} |
- |
-void SyncSchedulerImpl::NotifyRetryTime(base::Time retry_time) { |
- FOR_EACH_OBSERVER(SyncEngineEventListener, |
- *session_context_->listeners(), |
- OnRetryTimeChanged(retry_time)); |
-} |
- |
-void SyncSchedulerImpl::NotifyThrottledTypesChanged(ModelTypeSet types) { |
- FOR_EACH_OBSERVER(SyncEngineEventListener, |
- *session_context_->listeners(), |
- OnThrottledTypesChanged(types)); |
-} |
- |
-bool SyncSchedulerImpl::IsBackingOff() const { |
- DCHECK(CalledOnValidThread()); |
- return wait_interval_.get() && wait_interval_->mode == |
- WaitInterval::EXPONENTIAL_BACKOFF; |
-} |
- |
-void SyncSchedulerImpl::OnThrottled(const base::TimeDelta& throttle_duration) { |
- DCHECK(CalledOnValidThread()); |
- wait_interval_.reset(new WaitInterval(WaitInterval::THROTTLED, |
- throttle_duration)); |
- NotifyRetryTime(base::Time::Now() + wait_interval_->length); |
- NotifyThrottledTypesChanged(ModelTypeSet::All()); |
-} |
- |
-void SyncSchedulerImpl::OnTypesThrottled( |
- ModelTypeSet types, |
- 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); |
- type_unthrottle_timer_.Start( |
- FROM_HERE, |
- time_until_next_unthrottle, |
- base::Bind(&SyncSchedulerImpl::TypeUnthrottle, |
- weak_ptr_factory_.GetWeakPtr(), |
- now + time_until_next_unthrottle)); |
- NotifyThrottledTypesChanged(nudge_tracker_.GetThrottledTypes()); |
-} |
- |
-bool SyncSchedulerImpl::IsCurrentlyThrottled() { |
- DCHECK(CalledOnValidThread()); |
- return wait_interval_.get() && wait_interval_->mode == |
- WaitInterval::THROTTLED; |
-} |
- |
-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( |
- const std::map<ModelType, base::TimeDelta>& nudge_delays) { |
- DCHECK(CalledOnValidThread()); |
- nudge_tracker_.OnReceivedCustomNudgeDelays(nudge_delays); |
-} |
- |
-void SyncSchedulerImpl::OnReceivedClientInvalidationHintBufferSize(int size) { |
- if (size > 0) |
- nudge_tracker_.SetHintBufferSize(size); |
- else |
- NOTREACHED() << "Hint buffer size should be > 0."; |
-} |
- |
-void SyncSchedulerImpl::OnSyncProtocolError( |
- const SyncProtocolError& sync_protocol_error) { |
- DCHECK(CalledOnValidThread()); |
- if (ShouldRequestEarlyExit(sync_protocol_error)) { |
- SDVLOG(2) << "Sync Scheduler requesting early exit."; |
- Stop(); |
- } |
- if (IsActionableError(sync_protocol_error)) { |
- SDVLOG(2) << "OnActionableError"; |
- FOR_EACH_OBSERVER(SyncEngineEventListener, |
- *session_context_->listeners(), |
- OnActionableError(sync_protocol_error)); |
- } |
-} |
- |
-void SyncSchedulerImpl::OnReceivedGuRetryDelay(const base::TimeDelta& delay) { |
- nudge_tracker_.SetNextRetryTime(TimeTicks::Now() + delay); |
- retry_timer_.Start(FROM_HERE, delay, this, |
- &SyncSchedulerImpl::RetryTimerCallback); |
-} |
- |
-void SyncSchedulerImpl::OnReceivedMigrationRequest(ModelTypeSet types) { |
- FOR_EACH_OBSERVER(SyncEngineEventListener, |
- *session_context_->listeners(), |
- OnMigrationRequested(types)); |
-} |
- |
-void SyncSchedulerImpl::SetNotificationsEnabled(bool notifications_enabled) { |
- DCHECK(CalledOnValidThread()); |
- session_context_->set_notifications_enabled(notifications_enabled); |
- if (notifications_enabled) |
- nudge_tracker_.OnInvalidationsEnabled(); |
- else |
- nudge_tracker_.OnInvalidationsDisabled(); |
-} |
- |
-#undef SDVLOG_LOC |
- |
-#undef SDVLOG |
- |
-#undef SLOG |
- |
-#undef ENUM_CASE |
- |
-} // namespace syncer |