Commit 305b67f0 authored by jamartin's avatar jamartin Committed by Commit bot

Do not precache when the cache size is small

BUG=649495

Review-Url: https://codereview.chromium.org/2507753003
Cr-Commit-Position: refs/heads/master@{#437985}
parent 55b7fff6
......@@ -39,6 +39,7 @@ source_set("unit_tests") {
"//components/history/core/browser",
"//components/precache/core",
"//components/precache/core:proto",
"//components/variations:test_support",
"//content/public/browser",
"//content/test:test_support",
"//net:test_support",
......
......@@ -5,12 +5,14 @@ include_rules = [
"+components/variations",
"+content/public/browser",
"+net/base",
"+net/disk_cache",
"+net/http",
"+net/url_request",
]
specific_include_rules = {
'.*_[a-z]*test\.cc': [
"+content/public/test",
"+net/http",
"+net/url_request",
"+net/test",
],
}
......@@ -11,7 +11,10 @@
#include "base/bind.h"
#include "base/command_line.h"
#include "base/logging.h"
#include "base/memory/ref_counted.h"
#include "base/metrics/field_trial.h"
#include "base/metrics/histogram_macros.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_util.h"
#include "base/time/time.h"
#include "components/data_reduction_proxy/core/browser/data_reduction_proxy_settings.h"
......@@ -27,12 +30,19 @@
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/storage_partition.h"
#include "net/base/network_change_notifier.h"
#include "net/http/http_cache.h"
#include "net/url_request/url_request_context.h"
#include "net/url_request/url_request_context_getter.h"
using content::BrowserThread;
namespace {
namespace precache {
const char kPrecacheFieldTrialName[] = "Precache";
const char kMinCacheSizeParam[] = "min_cache_size";
namespace {
const char kPrecacheFieldTrialEnabledGroup[] = "Enabled";
const char kPrecacheFieldTrialControlGroup[] = "Control";
const char kConfigURLParam[] = "config_url";
......@@ -42,8 +52,6 @@ const size_t kNumTopHosts = 100;
} // namespace
namespace precache {
size_t NumTopHosts() {
return kNumTopHosts;
}
......@@ -123,6 +131,94 @@ PrecacheManager::AllowedType PrecacheManager::PrecachingAllowed() const {
return AllowedType::DISALLOWED;
}
void PrecacheManager::OnCacheBackendReceived(int net_error_code) {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
if (net_error_code != net::OK) {
// Assume there is no cache.
cache_backend_ = nullptr;
OnCacheSizeReceived(0);
return;
}
DCHECK(cache_backend_);
int result = cache_backend_->CalculateSizeOfAllEntries(base::Bind(
&PrecacheManager::OnCacheSizeReceived, base::Unretained(this)));
if (result == net::ERR_IO_PENDING) {
// Wait for the callback.
} else if (result >= 0) {
// The result is the expected bytes already.
OnCacheSizeReceived(result);
} else {
// Error occurred. Couldn't get the size. Assume there is no cache.
OnCacheSizeReceived(0);
}
cache_backend_ = nullptr;
}
void PrecacheManager::OnCacheSizeReceived(int cache_size_bytes) {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
BrowserThread::PostTask(
BrowserThread::UI, FROM_HERE,
base::Bind(&PrecacheManager::OnCacheSizeReceivedInUIThread,
base::Unretained(this), cache_size_bytes));
}
void PrecacheManager::OnCacheSizeReceivedInUIThread(int cache_size_bytes) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
UMA_HISTOGRAM_MEMORY_KB("Precache.CacheSize.AllEntries",
cache_size_bytes / 1024);
if (cache_size_bytes < min_cache_size_bytes_) {
OnDone(); // Do not continue.
} else {
BrowserThread::PostTaskAndReplyWithResult(
BrowserThread::DB, FROM_HERE,
base::Bind(&PrecacheDatabase::GetUnfinishedWork,
base::Unretained(precache_database_.get())),
base::Bind(&PrecacheManager::OnGetUnfinishedWorkDone, AsWeakPtr()));
}
}
void PrecacheManager::PrecacheIfCacheIsBigEnough(
scoped_refptr<net::URLRequestContextGetter> url_request_context_getter) {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
CHECK(url_request_context_getter);
// Continue with OnGetUnfinishedWorkDone only if the size of the cache is
// at least min_cache_size_bytes_.
// Class disk_cache::Backend does not expose its maximum size. However, caches
// are usually full, so we can use the size of all the entries stored in the
// cache (via CalculateSizeOfAllEntries) as a proxy of its maximum size.
net::URLRequestContext* context =
url_request_context_getter->GetURLRequestContext();
if (!context) {
OnCacheSizeReceived(0);
return;
}
net::HttpTransactionFactory* factory = context->http_transaction_factory();
if (!factory) {
OnCacheSizeReceived(0);
return;
}
net::HttpCache* cache = factory->GetCache();
if (!cache) {
// There is no known cache. Assume that there is no cache.
// TODO(jamartin): I'm not sure this can be an actual posibility. Consider
// making this a CHECK(cache).
OnCacheSizeReceived(0);
return;
}
const int net_error_code = cache->GetBackend(
&cache_backend_, base::Bind(&PrecacheManager::OnCacheBackendReceived,
base::Unretained(this)));
if (net_error_code != net::ERR_IO_PENDING) {
// No need to wait for the callback. The callback hasn't been called with
// the appropriate code, so we call it directly.
OnCacheBackendReceived(net_error_code);
}
}
void PrecacheManager::StartPrecaching(
const PrecacheCompletionCallback& precache_completion_callback) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
......@@ -140,12 +236,30 @@ void PrecacheManager::StartPrecaching(
base::Bind(&PrecacheDatabase::SetLastPrecacheTimestamp,
base::Unretained(precache_database_.get()),
base::Time::Now()));
BrowserThread::PostTaskAndReplyWithResult(
BrowserThread::DB,
FROM_HERE,
base::Bind(&PrecacheDatabase::GetUnfinishedWork,
base::Unretained(precache_database_.get())),
base::Bind(&PrecacheManager::OnGetUnfinishedWorkDone, AsWeakPtr()));
// Ignore boolean return value. In all documented failure cases, it sets the
// int to a reasonable value.
base::StringToInt(variations::GetVariationParamValue(kPrecacheFieldTrialName,
kMinCacheSizeParam),
&min_cache_size_bytes_);
if (min_cache_size_bytes_ <= 0) {
// Skip looking up the cache size, because it doesn't matter.
OnCacheSizeReceivedInUIThread(0);
return;
}
scoped_refptr<net::URLRequestContextGetter> url_request_context_getter(
content::BrowserContext::GetDefaultStoragePartition(browser_context_)
->GetURLRequestContext());
if (!url_request_context_getter) {
OnCacheSizeReceivedInUIThread(0);
return;
}
BrowserThread::PostTask(
BrowserThread::IO, FROM_HERE,
base::Bind(&PrecacheManager::PrecacheIfCacheIsBigEnough, AsWeakPtr(),
std::move(url_request_context_getter)));
}
void PrecacheManager::OnGetUnfinishedWorkDone(
......
......@@ -17,10 +17,13 @@
#include "base/callback.h"
#include "base/compiler_specific.h"
#include "base/macros.h"
#include "base/memory/ref_counted.h"
#include "base/memory/weak_ptr.h"
#include "components/history/core/browser/history_types.h"
#include "components/keyed_service/core/keyed_service.h"
#include "components/precache/core/precache_fetcher.h"
#include "net/disk_cache/disk_cache.h"
#include "net/http/http_cache.h"
#include "url/gurl.h"
namespace base {
......@@ -55,6 +58,8 @@ class PrecacheDatabase;
class PrecacheUnfinishedWork;
// Visible for test.
extern const char kPrecacheFieldTrialName[];
extern const char kMinCacheSizeParam[];
size_t NumTopHosts();
// Class that manages all precaching-related activities. Owned by the
......@@ -162,6 +167,14 @@ class PrecacheManager : public KeyedService,
// gets the list of TopHosts for metrics purposes, but otherwise does nothing.
void OnHostsReceivedThenDone(const history::TopHostsList& host_counts);
// Chain of callbacks for StartPrecaching that make sure that we only precache
// if there is a cache big enough.
void PrecacheIfCacheIsBigEnough(
scoped_refptr<net::URLRequestContextGetter> url_request_context_getter);
void OnCacheBackendReceived(int net_error_code);
void OnCacheSizeReceived(int cache_size_bytes);
void OnCacheSizeReceivedInUIThread(int cache_size_bytes);
// Returns true if precaching is allowed for the browser context.
AllowedType PrecachingAllowed() const;
......@@ -214,6 +227,15 @@ class PrecacheManager : public KeyedService,
// Flag indicating whether or not precaching is currently in progress.
bool is_precaching_;
// Pointer to the backend of the cache. Required to get the size of the cache.
// It is not owned and it is reset on demand via callbacks.
// It should only be accessed from the IO thread.
disk_cache::Backend* cache_backend_;
// The minimum cache size allowed for precaching. Initialized by
// StartPrecaching and read by OnCacheSizeReceivedInUIThread.
int min_cache_size_bytes_;
// Work that hasn't yet finished.
std::unique_ptr<PrecacheUnfinishedWork> unfinished_work_;
......
......@@ -28,12 +28,17 @@
#include "components/precache/core/precache_database.h"
#include "components/precache/core/precache_switches.h"
#include "components/precache/core/proto/unfinished_work.pb.h"
#include "components/variations/variations_params_manager.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/storage_partition.h"
#include "content/public/test/test_browser_context.h"
#include "content/public/test/test_browser_thread_bundle.h"
#include "net/base/test_completion_callback.h"
#include "net/disk_cache/simple/simple_backend_impl.h"
#include "net/http/http_response_headers.h"
#include "net/http/http_response_info.h"
#include "net/http/http_status_code.h"
#include "net/test/gtest_util.h"
#include "net/url_request/test_url_fetcher_factory.h"
#include "net/url_request/url_request_status.h"
#include "net/url_request/url_request_test_util.h"
......@@ -223,6 +228,7 @@ class PrecacheManagerTest : public testing::Test {
testing::NiceMock<MockHistoryService> history_service_;
base::HistogramTester histograms_;
net::HttpResponseInfo info_;
variations::testing::VariationParamsManager variation_params_;
};
TEST_F(PrecacheManagerTest, StartAndFinishPrecaching) {
......@@ -253,6 +259,75 @@ TEST_F(PrecacheManagerTest, StartAndFinishPrecaching) {
EXPECT_EQ(expected_requested_urls, url_callback_.requested_urls());
}
TEST_F(PrecacheManagerTest, StartPrecachingWithGoodSizedCache) {
variation_params_.SetVariationParams(kPrecacheFieldTrialName,
{{kMinCacheSizeParam, "1"}});
// Let's store something in the cache so we pass the min_cache_size threshold.
disk_cache::Backend* cache_backend;
{
// Get the CacheBackend.
net::TestCompletionCallback cb;
net::HttpCache* cache =
content::BrowserContext::GetDefaultStoragePartition(&browser_context_)
->GetURLRequestContext()
->GetURLRequestContext()
->http_transaction_factory()
->GetCache();
CHECK_NE(nullptr, cache);
int rv = cache->GetBackend(&cache_backend, cb.callback());
CHECK_EQ(net::OK, cb.GetResult(rv));
CHECK_NE(nullptr, cache_backend);
CHECK_EQ(cache_backend, cache->GetCurrentBackend());
}
disk_cache::Entry* entry = nullptr;
{
// Create a cache Entry.
net::TestCompletionCallback cb;
int rv = cache_backend->CreateEntry("key", &entry, cb.callback());
CHECK_EQ(net::OK, cb.GetResult(rv));
CHECK_NE(nullptr, entry);
}
{
// Store some data in the cache Entry.
const std::string data(1, 'a');
scoped_refptr<net::StringIOBuffer> buffer(new net::StringIOBuffer(data));
net::TestCompletionCallback cb;
int rv = entry->WriteData(0, 0, buffer.get(), buffer->size(), cb.callback(),
false);
entry->Close();
CHECK_EQ(buffer->size(), cb.GetResult(rv));
}
{
// Make sure everything went according to plan.
net::TestCompletionCallback cb;
int rv = cache_backend->CalculateSizeOfAllEntries(cb.callback());
CHECK_LE(1, cb.GetResult(rv));
}
EXPECT_FALSE(precache_manager_->IsPrecaching());
precache_manager_->StartPrecaching(precache_callback_.GetCallback());
base::RunLoop().RunUntilIdle();
EXPECT_TRUE(precache_manager_->IsPrecaching());
// Now it should be waiting for the top hosts.
}
TEST_F(PrecacheManagerTest, StartPrecachingStopsOnSmallCaches) {
// We don't have any entry in the cache, so the reported cache_size = 0 and
// thus it will fall below the threshold of 1.
variation_params_.SetVariationParams(kPrecacheFieldTrialName,
{{kMinCacheSizeParam, "1"}});
EXPECT_FALSE(precache_manager_->IsPrecaching());
precache_manager_->StartPrecaching(precache_callback_.GetCallback());
base::RunLoop().RunUntilIdle();
EXPECT_FALSE(precache_manager_->IsPrecaching());
EXPECT_TRUE(precache_callback_.was_on_done_called());
EXPECT_TRUE(url_callback_.requested_urls().empty());
}
TEST_F(PrecacheManagerTest, StartAndFinishPrecachingWithUnfinishedHosts) {
std::unique_ptr<PrecacheUnfinishedWork> unfinished_work(
new PrecacheUnfinishedWork());
......@@ -355,7 +430,9 @@ TEST_F(PrecacheManagerTest, StartAndCancelPrecachingAfterURLsReceived) {
EXPECT_TRUE(precache_manager_->IsPrecaching());
// Run a task to get unfinished work, and to get hosts.
for (int i = 0; i < 2; ++i) {
// We need to call run_loop.Run as many times as needed to go through the
// chain of callbacks :-(.
for (int i = 0; i < 4; ++i) {
base::RunLoop run_loop;
base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
run_loop.QuitClosure());
......@@ -418,7 +495,8 @@ TEST_F(PrecacheManagerTest, RecordStatsForFetchDuringPrecaching) {
base::RunLoop().RunUntilIdle();
EXPECT_THAT(
histograms_.GetTotalCountsForPrefix("Precache."),
UnorderedElementsAre(Pair("Precache.DownloadedPrecacheMotivated", 1),
UnorderedElementsAre(Pair("Precache.CacheSize.AllEntries", 1),
Pair("Precache.DownloadedPrecacheMotivated", 1),
Pair("Precache.Fetch.PercentCompleted", 1),
Pair("Precache.Fetch.ResponseBytes.Network", 1),
Pair("Precache.Fetch.ResponseBytes.Total", 1),
......@@ -498,6 +576,7 @@ TEST_F(PrecacheManagerTest, DeleteExpiredPrecacheHistory) {
RecordStatsForPrecacheFetch(
GURL("http://yesterday-fetch.com"), std::string(), base::TimeDelta(),
kCurrentTime - base::TimeDelta::FromDays(1), info_, 1000);
expected_histogram_count_map["Precache.CacheSize.AllEntries"]++;
expected_histogram_count_map["Precache.DownloadedPrecacheMotivated"] += 3;
expected_histogram_count_map["Precache.Fetch.PercentCompleted"]++;
expected_histogram_count_map["Precache.Fetch.ResponseBytes.Network"]++;
......@@ -527,6 +606,7 @@ TEST_F(PrecacheManagerTest, DeleteExpiredPrecacheHistory) {
// The precache fetcher runs until done, which records these histograms,
// and then cancels precaching, which records these histograms again.
// In practice
expected_histogram_count_map["Precache.CacheSize.AllEntries"]++;
expected_histogram_count_map["Precache.Fetch.PercentCompleted"]++;
expected_histogram_count_map["Precache.Fetch.ResponseBytes.Network"]++;
expected_histogram_count_map["Precache.Fetch.ResponseBytes.Total"]++;
......
......@@ -48362,6 +48362,15 @@ http://cs/file:chrome/histograms.xml - but prefer this file for new entries.
</summary>
</histogram>
<histogram name="Precache.CacheSize.AllEntries" units="KB">
<owner>jamartin@chromium.org</owner>
<owner>bengr@chromium.org</owner>
<summary>
The size in kilobytes occupied by all the entries existing in the cache at
the time of the last precache.
</summary>
</histogram>
<histogram name="Precache.CacheStatus.NonPrefetch" enum="HttpCachePattern">
<owner>jamartin@chromium.org</owner>
<owner>bengr@chromium.org</owner>
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment