blob: 39bb4f78e5b948d1dcea9e8ec2c90f7d2b0d369e [file] [log] [blame]
// Copyright 2013 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/cycle/nudge_tracker.h"
#include <algorithm>
#include <utility>
#include "components/sync/protocol/data_type_progress_marker.pb.h"
#include "components/sync/protocol/sync_enums.pb.h"
namespace syncer {
namespace {
// Nudge delay for local refresh. Common to all data types.
constexpr base::TimeDelta kLocalRefreshDelay = base::Milliseconds(500);
} // namespace
NudgeTracker::NudgeTracker() {
for (DataType type : DataTypeSet::All()) {
type_trackers_[type] = std::make_unique<DataTypeTracker>(type);
}
}
NudgeTracker::~NudgeTracker() = default;
bool NudgeTracker::IsSyncRequired(DataTypeSet types) const {
for (DataType type : types) {
TypeTrackerMap::const_iterator tracker_it = type_trackers_.find(type);
CHECK(tracker_it != type_trackers_.end()) << DataTypeToDebugString(type);
if (tracker_it->second->IsSyncRequired()) {
return true;
}
}
return false;
}
bool NudgeTracker::IsGetUpdatesRequired(DataTypeSet types) const {
if (invalidations_out_of_sync_) {
return true;
}
for (DataType type : types) {
TypeTrackerMap::const_iterator tracker_it = type_trackers_.find(type);
CHECK(tracker_it != type_trackers_.end()) << DataTypeToDebugString(type);
if (tracker_it->second->IsGetUpdatesRequired()) {
return true;
}
}
return false;
}
void NudgeTracker::RecordSuccessfulCommitMessage(DataTypeSet types) {
for (DataType type : types) {
TypeTrackerMap::const_iterator tracker_it = type_trackers_.find(type);
CHECK(tracker_it != type_trackers_.end()) << DataTypeToDebugString(type);
tracker_it->second->RecordSuccessfulCommitMessage();
}
}
void NudgeTracker::RecordSuccessfulSyncCycleIfNotBlocked(DataTypeSet types) {
// A successful cycle while invalidations are enabled puts us back into sync.
invalidations_out_of_sync_ = !invalidations_enabled_;
for (DataType type : types) {
TypeTrackerMap::const_iterator tracker_it = type_trackers_.find(type);
CHECK(tracker_it != type_trackers_.end()) << DataTypeToDebugString(type);
tracker_it->second->RecordSuccessfulSyncCycleIfNotBlocked();
}
}
void NudgeTracker::RecordInitialSyncDone(DataTypeSet types) {
for (DataType type : types) {
TypeTrackerMap::const_iterator tracker_it = type_trackers_.find(type);
CHECK(tracker_it != type_trackers_.end()) << DataTypeToDebugString(type);
tracker_it->second->RecordInitialSyncDone();
}
}
base::TimeDelta NudgeTracker::RecordLocalChange(DataType type,
bool is_single_client) {
DCHECK(type_trackers_.contains(type));
type_trackers_[type]->RecordLocalChange();
return type_trackers_[type]->GetLocalChangeNudgeDelay(is_single_client);
}
base::TimeDelta NudgeTracker::RecordLocalRefreshRequest(DataTypeSet types) {
for (DataType type : types) {
TypeTrackerMap::const_iterator tracker_it = type_trackers_.find(type);
CHECK(tracker_it != type_trackers_.end()) << DataTypeToDebugString(type);
tracker_it->second->RecordLocalRefreshRequest();
}
return kLocalRefreshDelay;
}
base::TimeDelta NudgeTracker::GetRemoteInvalidationDelay(DataType type) const {
TypeTrackerMap::const_iterator tracker_it = type_trackers_.find(type);
CHECK(tracker_it != type_trackers_.end());
return tracker_it->second->GetRemoteInvalidationDelay();
}
void NudgeTracker::RecordInitialSyncRequired(DataType type) {
TypeTrackerMap::const_iterator tracker_it = type_trackers_.find(type);
CHECK(tracker_it != type_trackers_.end());
tracker_it->second->RecordInitialSyncRequired();
}
void NudgeTracker::RecordCommitConflict(DataType type) {
TypeTrackerMap::const_iterator tracker_it = type_trackers_.find(type);
CHECK(tracker_it != type_trackers_.end());
tracker_it->second->RecordCommitConflict();
}
void NudgeTracker::OnInvalidationsEnabled() {
invalidations_enabled_ = true;
}
void NudgeTracker::OnInvalidationsDisabled() {
invalidations_enabled_ = false;
invalidations_out_of_sync_ = true;
}
void NudgeTracker::SetTypesThrottledUntil(DataTypeSet types,
base::TimeDelta length,
base::TimeTicks now) {
for (DataType type : types) {
TypeTrackerMap::const_iterator tracker_it = type_trackers_.find(type);
tracker_it->second->ThrottleType(length, now);
}
}
void NudgeTracker::SetTypeBackedOff(DataType type,
base::TimeDelta length,
base::TimeTicks now) {
TypeTrackerMap::const_iterator tracker_it = type_trackers_.find(type);
CHECK(tracker_it != type_trackers_.end());
tracker_it->second->BackOffType(length, now);
}
void NudgeTracker::UpdateTypeThrottlingAndBackoffState() {
for (const auto& [type, tracker] : type_trackers_) {
tracker->UpdateThrottleOrBackoffState();
}
}
void NudgeTracker::SetHasPendingInvalidations(DataType type,
bool has_invalidation) {
TypeTrackerMap::const_iterator tracker_it = type_trackers_.find(type);
CHECK(tracker_it != type_trackers_.end());
tracker_it->second->SetHasPendingInvalidations(has_invalidation);
}
bool NudgeTracker::IsAnyTypeBlocked() const {
for (const auto& [type, tracker] : type_trackers_) {
if (tracker->IsBlocked()) {
return true;
}
}
return false;
}
bool NudgeTracker::IsTypeBlocked(DataType type) const {
DCHECK(type_trackers_.find(type) != type_trackers_.end())
<< DataTypeToDebugString(type);
return type_trackers_.find(type)->second->IsBlocked();
}
WaitInterval::BlockingMode NudgeTracker::GetTypeBlockingMode(
DataType type) const {
DCHECK(type_trackers_.find(type) != type_trackers_.end());
return type_trackers_.find(type)->second->GetBlockingMode();
}
base::TimeDelta NudgeTracker::GetTimeUntilNextUnblock() const {
DCHECK(IsAnyTypeBlocked()) << "This function requires a pending unblock.";
// Return min of GetTimeUntilUnblock() values for all IsBlocked() types.
base::TimeDelta time_until_next_unblock = base::TimeDelta::Max();
for (const auto& [type, tracker] : type_trackers_) {
if (tracker->IsBlocked()) {
time_until_next_unblock =
std::min(time_until_next_unblock, tracker->GetTimeUntilUnblock());
}
}
DCHECK(!time_until_next_unblock.is_max());
return time_until_next_unblock;
}
base::TimeDelta NudgeTracker::GetTypeLastBackoffInterval(DataType type) const {
auto tracker_it = type_trackers_.find(type);
CHECK(tracker_it != type_trackers_.end());
return tracker_it->second->GetLastBackoffInterval();
}
DataTypeSet NudgeTracker::GetBlockedTypes() const {
DataTypeSet result;
for (const auto& [type, tracker] : type_trackers_) {
if (tracker->IsBlocked()) {
result.Put(type);
}
}
return result;
}
DataTypeSet NudgeTracker::GetNudgedTypes() const {
DataTypeSet result;
for (const auto& [type, tracker] : type_trackers_) {
if (tracker->HasLocalChangePending()) {
result.Put(type);
}
}
return result;
}
DataTypeSet NudgeTracker::GetNotifiedTypes() const {
DataTypeSet result;
for (const auto& [type, tracker] : type_trackers_) {
if (tracker->HasPendingInvalidation()) {
result.Put(type);
}
}
return result;
}
DataTypeSet NudgeTracker::GetRefreshRequestedTypes() const {
DataTypeSet result;
for (const auto& [type, tracker] : type_trackers_) {
if (tracker->HasRefreshRequestPending()) {
result.Put(type);
}
}
return result;
}
sync_pb::SyncEnums::GetUpdatesOrigin NudgeTracker::GetOrigin() const {
// TODO(crbug.com/40252048): This appears trivial after removing GU_RETRY, either
// simplify the code around or add an explanation why this is needed.
for (const auto& [type, tracker] : type_trackers_) {
if (!tracker->IsBlocked() && (tracker->HasPendingInvalidation() ||
tracker->HasRefreshRequestPending() ||
tracker->HasLocalChangePending() ||
tracker->IsInitialSyncRequired())) {
return sync_pb::SyncEnums::GU_TRIGGER;
}
}
return sync_pb::SyncEnums::UNKNOWN_ORIGIN;
}
void NudgeTracker::FillProtoMessage(DataType type,
sync_pb::GetUpdateTriggers* msg) const {
DCHECK(type_trackers_.find(type) != type_trackers_.end());
// Fill what we can from the global data.
msg->set_invalidations_out_of_sync(invalidations_out_of_sync_);
// Delegate the type-specific work to the DataTypeTracker class.
type_trackers_.find(type)->second->FillGetUpdatesTriggersMessage(msg);
}
void NudgeTracker::UpdateLocalChangeDelay(DataType type,
const base::TimeDelta& delay) {
if (type_trackers_.contains(type)) {
type_trackers_[type]->UpdateLocalChangeNudgeDelay(delay);
}
}
void NudgeTracker::SetLocalChangeDelayIgnoringMinForTest(
DataType type,
const base::TimeDelta& delay) {
DCHECK(type_trackers_.contains(type));
type_trackers_[type]->SetLocalChangeNudgeDelayIgnoringMinForTest(delay);
}
void NudgeTracker::SetQuotaParamsForExtensionTypes(
std::optional<int> max_tokens,
std::optional<base::TimeDelta> refill_interval,
std::optional<base::TimeDelta> depleted_quota_nudge_delay) {
for (const auto& [type, tracker] : type_trackers_) {
tracker->SetQuotaParamsIfExtensionType(max_tokens, refill_interval,
depleted_quota_nudge_delay);
}
}
} // namespace syncer