blob: 1a8f5740503bb8150325cc8c74ca5a78de7fffda [file] [log] [blame]
// Copyright 2012 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "components/sync/engine/backoff_delay_provider.h"
#include <stdint.h>
#include <algorithm>
#include "base/memory/ptr_util.h"
#include "base/rand_util.h"
#include "components/sync/base/syncer_error.h"
#include "components/sync/engine/cycle/model_neutral_state.h"
#include "components/sync/engine/polling_constants.h"
namespace syncer {
namespace {
// This calculates approx. last_delay * kBackoffMultiplyFactor +/- last_delay
// * kBackoffJitterFactor. |jitter_sign| must be -1 or 1 and determines whether
// the jitter in the delay will be positive or negative.
base::TimeDelta GetDelayImpl(base::TimeDelta last_delay, int jitter_sign) {
DCHECK(jitter_sign == -1 || jitter_sign == 1);
if (last_delay >= kMaxBackoffTime)
return kMaxBackoffTime;
const base::TimeDelta backoff =
std::max(kMinBackoffTime, last_delay * kBackoffMultiplyFactor) +
jitter_sign * kBackoffJitterFactor * last_delay;
// Clamp backoff between 1 second and |kMaxBackoffTime|.
return std::max(kMinBackoffTime, std::min(backoff, kMaxBackoffTime));
}
} // namespace
// static
std::unique_ptr<BackoffDelayProvider> BackoffDelayProvider::FromDefaults() {
// base::WrapUnique() used because the constructor is private.
return base::WrapUnique(new BackoffDelayProvider(
kInitialBackoffRetryTime, kInitialBackoffImmediateRetryTime));
}
// static
std::unique_ptr<BackoffDelayProvider>
BackoffDelayProvider::WithShortInitialRetryOverride() {
// base::WrapUnique() used because the constructor is private.
return base::WrapUnique(new BackoffDelayProvider(
kInitialBackoffShortRetryTime, kInitialBackoffImmediateRetryTime));
}
BackoffDelayProvider::BackoffDelayProvider(
const base::TimeDelta& default_initial_backoff,
const base::TimeDelta& short_initial_backoff)
: default_initial_backoff_(default_initial_backoff),
short_initial_backoff_(short_initial_backoff) {}
BackoffDelayProvider::~BackoffDelayProvider() = default;
base::TimeDelta BackoffDelayProvider::GetDelay(
const base::TimeDelta& last_delay) {
// Flip a coin to randomize backoff interval by +/- kBackoffJitterFactor.
const int jitter_sign = base::RandInt(0, 1) * 2 - 1;
return GetDelayImpl(last_delay, jitter_sign);
}
base::TimeDelta BackoffDelayProvider::GetInitialDelay(
const ModelNeutralState& state) const {
// NETWORK_CONNECTION_UNAVAILABLE implies we did not receive HTTP response
// from server because of some network error. If network is unavailable then
// on next connection type or address change scheduler will run canary job.
// Otherwise we'll retry in 30 seconds.
if (state.commit_result.value() ==
SyncerError::NETWORK_CONNECTION_UNAVAILABLE ||
state.last_download_updates_result.value() ==
SyncerError::NETWORK_CONNECTION_UNAVAILABLE) {
return default_initial_backoff_;
}
if (state.last_get_key_failed) {
return default_initial_backoff_;
}
// Note: If we received a MIGRATION_DONE on download updates, then commit
// should not have taken place. Moreover, if we receive a MIGRATION_DONE
// on commit, it means that download updates succeeded. Therefore, we only
// need to check if either code is equal to SERVER_RETURN_MIGRATION_DONE,
// and not if there were any more serious errors requiring the long retry.
if (state.last_download_updates_result.value() ==
SyncerError::SERVER_RETURN_MIGRATION_DONE ||
state.commit_result.value() ==
SyncerError::SERVER_RETURN_MIGRATION_DONE) {
return short_initial_backoff_;
}
// When the server tells us we have a conflict, then we should download the
// latest updates so we can see the conflict ourselves, resolve it locally,
// then try again to commit. Running another sync cycle will do all these
// things. There's no need to back off, we can do this immediately.
//
// TODO(sync): We shouldn't need to handle this in BackoffDelayProvider.
// There should be a way to deal with protocol errors before we get to this
// point.
if (state.commit_result.value() == SyncerError::SERVER_RETURN_CONFLICT)
return short_initial_backoff_;
return default_initial_backoff_;
}
base::TimeDelta BackoffDelayProvider::GetDelayForTesting(
base::TimeDelta last_delay,
int jitter_sign) {
return GetDelayImpl(last_delay, jitter_sign);
}
} // namespace syncer