blob: ea3882d8e5bbd307fc423a65129fe52810b7db11 [file] [log] [blame]
// Copyright 2020 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 "components/query_tiles/internal/tile_service_scheduler_impl.h"
#include <memory>
#include <utility>
#include "base/command_line.h"
#include "base/rand_util.h"
#include "base/time/default_tick_clock.h"
#include "components/prefs/pref_service.h"
#include "components/query_tiles/internal/stats.h"
#include "components/query_tiles/internal/tile_config.h"
#include "components/query_tiles/switches.h"
#include "net/base/backoff_entry_serializer.h"
namespace query_tiles {
namespace {
// Schedule window params in instant schedule mode.
const int kInstantScheduleWindowStartMs = 10 * 1000; // 10 seconds
const int kInstantScheduleWindowEndMs = 20 * 1000; // 20 seconds
bool IsInstantFetchMode() {
return base::CommandLine::ForCurrentProcess()->HasSwitch(
switches::kQueryTilesInstantBackgroundTask);
}
} // namespace
TileServiceSchedulerImpl::TileServiceSchedulerImpl(
background_task::BackgroundTaskScheduler* scheduler,
PrefService* prefs,
base::Clock* clock,
const base::TickClock* tick_clock,
std::unique_ptr<net::BackoffEntry::Policy> backoff_policy,
LogSink* log_sink)
: scheduler_(scheduler),
prefs_(prefs),
clock_(clock),
tick_clock_(tick_clock),
backoff_policy_(std::move(backoff_policy)),
is_suspend_(false),
delegate_(nullptr),
fetcher_status_(TileInfoRequestStatus::kInit),
group_status_(TileGroupStatus::kUninitialized),
log_sink_(log_sink) {}
TileServiceSchedulerImpl::~TileServiceSchedulerImpl() = default;
void TileServiceSchedulerImpl::CancelTask() {
scheduler_->Cancel(
static_cast<int>(background_task::TaskIds::QUERY_TILE_JOB_ID));
ResetBackoff();
MarkFirstRunFinished();
}
void TileServiceSchedulerImpl::OnFetchStarted() {
fetcher_status_ = TileInfoRequestStatus::kInit;
if (!IsInstantFetchMode()) {
base::Time::Exploded local_explode;
base::Time::Now().LocalExplode(&local_explode);
stats::RecordExplodeOnFetchStarted(local_explode.hour);
}
}
void TileServiceSchedulerImpl::OnFetchCompleted(TileInfoRequestStatus status) {
auto first_schedule_time = prefs_->GetTime(kFirstScheduleTimeKey);
MarkFirstRunFinished();
fetcher_status_ = status;
if (IsInstantFetchMode())
return;
// If this task was marked at first attempting flow, record the duration, and
// mark the flow is finished now.
if (first_schedule_time != base::Time()) {
auto hours_past = (clock_->Now() - first_schedule_time).InHours();
if (hours_past >= 0) {
stats::RecordFirstFetchFlowDuration(hours_past);
}
}
if (status == TileInfoRequestStatus::kShouldSuspend) {
ResetBackoff();
is_suspend_ = true;
} else if (status == TileInfoRequestStatus::kFailure) {
AddBackoff();
ScheduleTask(false);
} else if (status == TileInfoRequestStatus::kSuccess && !is_suspend_) {
ResetBackoff();
ScheduleTask(true);
}
stats::RecordTileRequestStatus(status);
PingLogSink();
}
void TileServiceSchedulerImpl::OnDbPurged(TileGroupStatus status) {
CancelTask();
group_status_ = status;
PingLogSink();
}
void TileServiceSchedulerImpl::OnGroupDataSaved(TileGroupStatus status) {
group_status_ = status;
PingLogSink();
}
void TileServiceSchedulerImpl::OnTileManagerInitialized(
TileGroupStatus status) {
group_status_ = status;
if (IsInstantFetchMode()) {
ResetBackoff();
ScheduleTask(true);
return;
}
if (status == TileGroupStatus::kNoTiles && !is_suspend_ &&
!IsDuringFirstFlow()) {
ResetBackoff();
ScheduleTask(true);
MarkFirstRunScheduled();
} else if (status == TileGroupStatus::kFailureDbOperation) {
ResetBackoff();
is_suspend_ = true;
}
stats::RecordTileGroupStatus(status);
PingLogSink();
}
TileInfoRequestStatus TileServiceSchedulerImpl::GetFetcherStatus() {
return fetcher_status_;
}
TileGroupStatus TileServiceSchedulerImpl::GetGroupStatus() {
return group_status_;
}
TileGroup* TileServiceSchedulerImpl::GetTileGroup() {
return delegate_ ? delegate_->GetTileGroup() : nullptr;
}
std::unique_ptr<net::BackoffEntry> TileServiceSchedulerImpl::GetBackoff() {
std::unique_ptr<net::BackoffEntry> result;
const base::ListValue* value = prefs_->GetList(kBackoffEntryKey);
if (value) {
result = net::BackoffEntrySerializer::DeserializeFromValue(
*value, backoff_policy_.get(), tick_clock_, clock_->Now());
}
if (!result) {
return std::make_unique<net::BackoffEntry>(backoff_policy_.get(),
tick_clock_);
}
return result;
}
void TileServiceSchedulerImpl::ScheduleTask(bool is_init_schedule) {
background_task::OneOffInfo one_off_task_info;
if (IsInstantFetchMode()) {
GetInstantTaskWindow(&one_off_task_info.window_start_time_ms,
&one_off_task_info.window_end_time_ms,
is_init_schedule);
} else {
GetTaskWindow(&one_off_task_info.window_start_time_ms,
&one_off_task_info.window_end_time_ms, is_init_schedule);
}
background_task::TaskInfo task_info(
static_cast<int>(background_task::TaskIds::QUERY_TILE_JOB_ID),
one_off_task_info);
task_info.is_persisted = true;
task_info.update_current = true;
task_info.network_type =
TileConfig::GetIsUnMeteredNetworkRequired()
? background_task::TaskInfo::NetworkType::UNMETERED
: background_task::TaskInfo::NetworkType::ANY;
scheduler_->Schedule(task_info);
}
void TileServiceSchedulerImpl::AddBackoff() {
std::unique_ptr<net::BackoffEntry> current = GetBackoff();
current->InformOfRequest(false);
UpdateBackoff(current.get());
}
void TileServiceSchedulerImpl::ResetBackoff() {
std::unique_ptr<net::BackoffEntry> current = GetBackoff();
current->Reset();
UpdateBackoff(current.get());
}
int64_t TileServiceSchedulerImpl::GetDelaysFromBackoff() {
return GetBackoff()->GetTimeUntilRelease().InMilliseconds();
}
// Schedule next task in next 10 to 20 seconds in debug mode.
void TileServiceSchedulerImpl::GetInstantTaskWindow(int64_t* start_time_ms,
int64_t* end_time_ms,
bool is_init_schedule) {
if (is_init_schedule) {
*start_time_ms = kInstantScheduleWindowStartMs;
} else {
*start_time_ms = GetDelaysFromBackoff();
}
*end_time_ms = kInstantScheduleWindowEndMs;
}
void TileServiceSchedulerImpl::GetTaskWindow(int64_t* start_time_ms,
int64_t* end_time_ms,
bool is_init_schedule) {
if (is_init_schedule) {
*start_time_ms = TileConfig::GetScheduleIntervalInMs() +
base::RandGenerator(TileConfig::GetMaxRandomWindowInMs());
} else {
*start_time_ms = GetDelaysFromBackoff();
}
*end_time_ms = *start_time_ms + TileConfig::GetOneoffTaskWindowInMs();
}
void TileServiceSchedulerImpl::UpdateBackoff(net::BackoffEntry* backoff) {
std::unique_ptr<base::Value> value =
net::BackoffEntrySerializer::SerializeToValue(*backoff, clock_->Now());
prefs_->Set(kBackoffEntryKey, *value);
}
void TileServiceSchedulerImpl::MarkFirstRunScheduled() {
prefs_->SetTime(kFirstScheduleTimeKey, clock_->Now());
}
void TileServiceSchedulerImpl::MarkFirstRunFinished() {
prefs_->SetTime(kFirstScheduleTimeKey, base::Time());
}
bool TileServiceSchedulerImpl::IsDuringFirstFlow() {
return prefs_->GetTime(kFirstScheduleTimeKey) != base::Time();
}
void TileServiceSchedulerImpl::SetDelegate(Delegate* delegate) {
delegate_ = delegate;
}
void TileServiceSchedulerImpl::PingLogSink() {
log_sink_->OnServiceStatusChanged();
log_sink_->OnTileDataAvailable();
}
} // namespace query_tiles