Skip to content

Commit 3b72254

Browse files
author
Gavin Peters
committed
SimpleCache: open with fewer seeks.
There's usually no reason to read the file header before reading the stream 0 (headers) data when opening a simple cache entry. By not doing so, seeks in the critical path of the loading headers are reduced, which should speed up http caching performance. To be careful, the headers are still checked when the first read of data (if any) is performed. This delays detecting hash collisions in the cache, because the header data is required to compare keys (instead of just hashes). To compensate for the later key comparison, a SHA256 of the key is stored as an optional prefix to the end of file record. This is used to validate the key is correct before returning headers to the client. Because the key checking logic is now totally migrated back into the SimpleSynchronousEntry, we can return to reporting on key matching in the SyncOpenResult histograms. [email protected],[email protected],[email protected] BUG=611732 Review-Url: https://codereview.chromium.org/1977863003 Cr-Commit-Position: refs/heads/master@{#395083} (cherry picked from commit 9f15f22) Review URL: https://codereview.chromium.org/2004913002 . Cr-Commit-Position: refs/branch-heads/2743@{#4} Cr-Branched-From: 2b3ae3b-refs/heads/master@{#394939}
1 parent 6c49b69 commit 3b72254

14 files changed

+561
-231
lines changed

net/disk_cache/backend_unittest.cc

Lines changed: 30 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
#include "net/disk_cache/simple/simple_backend_impl.h"
3434
#include "net/disk_cache/simple/simple_entry_format.h"
3535
#include "net/disk_cache/simple/simple_index.h"
36+
#include "net/disk_cache/simple/simple_synchronous_entry.h"
3637
#include "net/disk_cache/simple/simple_test_util.h"
3738
#include "net/disk_cache/simple/simple_util.h"
3839
#include "testing/gtest/include/gtest/gtest.h"
@@ -253,16 +254,19 @@ void DiskCacheBackendTest::InitSparseCache(base::Time* doomed_start,
253254
bool DiskCacheBackendTest::CreateSetOfRandomEntries(
254255
std::set<std::string>* key_pool) {
255256
const int kNumEntries = 10;
257+
const int initial_entry_count = cache_->GetEntryCount();
256258

257259
for (int i = 0; i < kNumEntries; ++i) {
258260
std::string key = GenerateKey(true);
259261
disk_cache::Entry* entry;
260-
if (CreateEntry(key, &entry) != net::OK)
262+
if (CreateEntry(key, &entry) != net::OK) {
261263
return false;
264+
}
262265
key_pool->insert(key);
263266
entry->Close();
264267
}
265-
return key_pool->size() == static_cast<size_t>(cache_->GetEntryCount());
268+
return key_pool->size() ==
269+
static_cast<size_t>(cache_->GetEntryCount() - initial_entry_count);
266270
}
267271

268272
// Performs iteration over the backend and checks that the keys of entries
@@ -3688,9 +3692,6 @@ TEST_F(DiskCacheBackendTest, SimpleCacheEnumerationWhileDoomed) {
36883692
TEST_F(DiskCacheBackendTest, SimpleCacheEnumerationCorruption) {
36893693
SetSimpleCacheMode();
36903694
InitCache();
3691-
std::set<std::string> key_pool;
3692-
ASSERT_TRUE(CreateSetOfRandomEntries(&key_pool));
3693-
36943695
// Create a corrupt entry. The write/read sequence ensures that the entry will
36953696
// have been created before corrupting the platform files, in the case of
36963697
// optimistic operations.
@@ -3707,6 +3708,9 @@ TEST_F(DiskCacheBackendTest, SimpleCacheEnumerationCorruption) {
37073708
ASSERT_EQ(kSize, ReadData(corrupted_entry, 0, 0, buffer.get(), kSize));
37083709
corrupted_entry->Close();
37093710

3711+
std::set<std::string> key_pool;
3712+
ASSERT_TRUE(CreateSetOfRandomEntries(&key_pool));
3713+
37103714
EXPECT_TRUE(disk_cache::simple_util::CreateCorruptFileForTests(
37113715
key, cache_path_));
37123716
EXPECT_EQ(key_pool.size() + 1, static_cast<size_t>(cache_->GetEntryCount()));
@@ -3740,6 +3744,27 @@ TEST_F(DiskCacheBackendTest, SimpleCacheEnumerationDestruction) {
37403744
// This test passes if we don't leak memory.
37413745
}
37423746

3747+
// Tests that enumerations include entries with long keys.
3748+
TEST_F(DiskCacheBackendTest, SimpleCacheEnumerationLongKeys) {
3749+
SetSimpleCacheMode();
3750+
InitCache();
3751+
std::set<std::string> key_pool;
3752+
ASSERT_TRUE(CreateSetOfRandomEntries(&key_pool));
3753+
3754+
const size_t long_key_length =
3755+
disk_cache::SimpleSynchronousEntry::kInitialHeaderRead + 10;
3756+
std::string long_key(long_key_length, 'X');
3757+
key_pool.insert(long_key);
3758+
disk_cache::Entry* entry = NULL;
3759+
ASSERT_EQ(net::OK, CreateEntry(long_key.c_str(), &entry));
3760+
entry->Close();
3761+
3762+
std::unique_ptr<TestIterator> iter = CreateIterator();
3763+
size_t count = 0;
3764+
EXPECT_TRUE(EnumerateAndMatchKeys(-1, iter.get(), &key_pool, &count));
3765+
EXPECT_TRUE(key_pool.empty());
3766+
}
3767+
37433768
// Tests that a SimpleCache doesn't crash when files are deleted very quickly
37443769
// after closing.
37453770
// NOTE: IF THIS TEST IS FLAKY THEN IT IS FAILING. See https://crbug.com/416940

net/disk_cache/entry_unittest.cc

Lines changed: 79 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
#include "base/files/file.h"
1010
#include "base/files/file_util.h"
1111
#include "base/macros.h"
12+
#include "base/run_loop.h"
1213
#include "base/strings/string_number_conversions.h"
1314
#include "base/strings/string_util.h"
1415
#include "base/threading/platform_thread.h"
@@ -21,6 +22,7 @@
2122
#include "net/disk_cache/disk_cache_test_base.h"
2223
#include "net/disk_cache/disk_cache_test_util.h"
2324
#include "net/disk_cache/memory/mem_entry_impl.h"
25+
#include "net/disk_cache/simple/simple_backend_impl.h"
2426
#include "net/disk_cache/simple/simple_entry_format.h"
2527
#include "net/disk_cache/simple/simple_entry_impl.h"
2628
#include "net/disk_cache/simple/simple_synchronous_entry.h"
@@ -2708,7 +2710,7 @@ TEST_F(DiskCacheEntryTest, SimpleCacheNoEOF) {
27082710
SetSimpleCacheMode();
27092711
InitCache();
27102712

2711-
const char key[] = "the first key";
2713+
const std::string key("the first key");
27122714

27132715
disk_cache::Entry* entry = NULL;
27142716
ASSERT_EQ(net::OK, CreateEntry(key, &entry));
@@ -2728,9 +2730,8 @@ TEST_F(DiskCacheEntryTest, SimpleCacheNoEOF) {
27282730
int kTruncationBytes = -static_cast<int>(sizeof(disk_cache::SimpleFileEOF));
27292731
const base::FilePath entry_path = cache_path_.AppendASCII(
27302732
disk_cache::simple_util::GetFilenameFromKeyAndFileIndex(key, 0));
2731-
const int64_t invalid_size =
2732-
disk_cache::simple_util::GetFileSizeFromKeyAndDataSize(key,
2733-
kTruncationBytes);
2733+
const int64_t invalid_size = disk_cache::simple_util::GetFileSizeFromDataSize(
2734+
key.size(), kTruncationBytes);
27342735
EXPECT_TRUE(TruncatePath(entry_path, invalid_size));
27352736
EXPECT_EQ(net::ERR_FAILED, OpenEntry(key, &entry));
27362737
DisableIntegrityCheck();
@@ -3688,7 +3689,7 @@ TEST_F(DiskCacheEntryTest, SimpleCacheStream1SizeChanges) {
36883689
SetSimpleCacheMode();
36893690
InitCache();
36903691
disk_cache::Entry* entry = NULL;
3691-
const char key[] = "the key";
3692+
const std::string key("the key");
36923693
const int kSize = 100;
36933694
scoped_refptr<net::IOBuffer> buffer(new net::IOBuffer(kSize));
36943695
scoped_refptr<net::IOBuffer> buffer_read(new net::IOBuffer(kSize));
@@ -3726,7 +3727,7 @@ TEST_F(DiskCacheEntryTest, SimpleCacheStream1SizeChanges) {
37263727
int sparse_data_size = 0;
37273728
disk_cache::SimpleEntryStat entry_stat(
37283729
base::Time::Now(), base::Time::Now(), data_size, sparse_data_size);
3729-
int eof_offset = entry_stat.GetEOFOffsetInFile(key, 0);
3730+
int eof_offset = entry_stat.GetEOFOffsetInFile(key.size(), 0);
37303731
disk_cache::SimpleFileEOF eof_record;
37313732
ASSERT_EQ(static_cast<int>(sizeof(eof_record)),
37323733
entry_file0.Read(eof_offset, reinterpret_cast<char*>(&eof_record),
@@ -4165,3 +4166,75 @@ TEST_F(DiskCacheEntryTest, SimpleCacheTruncateLargeSparseFile) {
41654166

41664167
entry->Close();
41674168
}
4169+
4170+
TEST_F(DiskCacheEntryTest, SimpleCacheReadWithoutKeySHA256) {
4171+
// This test runs as APP_CACHE to make operations more synchronous.
4172+
SetCacheType(net::APP_CACHE);
4173+
SetSimpleCacheMode();
4174+
InitCache();
4175+
disk_cache::Entry* entry;
4176+
std::string key("a key");
4177+
ASSERT_EQ(net::OK, CreateEntry(key, &entry));
4178+
4179+
const std::string stream_0_data = "data for stream zero";
4180+
scoped_refptr<net::IOBuffer> stream_0_iobuffer(
4181+
new net::StringIOBuffer(stream_0_data));
4182+
EXPECT_EQ(static_cast<int>(stream_0_data.size()),
4183+
WriteData(entry, 0, 0, stream_0_iobuffer.get(),
4184+
stream_0_data.size(), false));
4185+
const std::string stream_1_data = "FOR STREAM ONE, QUITE DIFFERENT THINGS";
4186+
scoped_refptr<net::IOBuffer> stream_1_iobuffer(
4187+
new net::StringIOBuffer(stream_1_data));
4188+
EXPECT_EQ(static_cast<int>(stream_1_data.size()),
4189+
WriteData(entry, 1, 0, stream_1_iobuffer.get(),
4190+
stream_1_data.size(), false));
4191+
entry->Close();
4192+
4193+
base::RunLoop().RunUntilIdle();
4194+
disk_cache::SimpleBackendImpl::FlushWorkerPoolForTesting();
4195+
base::RunLoop().RunUntilIdle();
4196+
4197+
EXPECT_TRUE(
4198+
disk_cache::simple_util::RemoveKeySHA256FromEntry(key, cache_path_));
4199+
ASSERT_EQ(net::OK, OpenEntry(key, &entry));
4200+
ScopedEntryPtr entry_closer(entry);
4201+
4202+
EXPECT_EQ(static_cast<int>(stream_0_data.size()), entry->GetDataSize(0));
4203+
scoped_refptr<net::IOBuffer> check_stream_0_data(
4204+
new net::IOBuffer(stream_0_data.size()));
4205+
EXPECT_EQ(
4206+
static_cast<int>(stream_0_data.size()),
4207+
ReadData(entry, 0, 0, check_stream_0_data.get(), stream_0_data.size()));
4208+
EXPECT_EQ(0, stream_0_data.compare(0, std::string::npos,
4209+
check_stream_0_data->data(),
4210+
stream_0_data.size()));
4211+
4212+
EXPECT_EQ(static_cast<int>(stream_1_data.size()), entry->GetDataSize(1));
4213+
scoped_refptr<net::IOBuffer> check_stream_1_data(
4214+
new net::IOBuffer(stream_1_data.size()));
4215+
EXPECT_EQ(
4216+
static_cast<int>(stream_1_data.size()),
4217+
ReadData(entry, 1, 0, check_stream_1_data.get(), stream_1_data.size()));
4218+
EXPECT_EQ(0, stream_1_data.compare(0, std::string::npos,
4219+
check_stream_1_data->data(),
4220+
stream_1_data.size()));
4221+
}
4222+
4223+
TEST_F(DiskCacheEntryTest, SimpleCacheReadCorruptKeySHA256) {
4224+
// This test runs as APP_CACHE to make operations more synchronous.
4225+
SetCacheType(net::APP_CACHE);
4226+
SetSimpleCacheMode();
4227+
InitCache();
4228+
disk_cache::Entry* entry;
4229+
std::string key("a key");
4230+
ASSERT_EQ(net::OK, CreateEntry(key, &entry));
4231+
entry->Close();
4232+
4233+
base::RunLoop().RunUntilIdle();
4234+
disk_cache::SimpleBackendImpl::FlushWorkerPoolForTesting();
4235+
base::RunLoop().RunUntilIdle();
4236+
4237+
EXPECT_TRUE(
4238+
disk_cache::simple_util::CorruptKeySHA256FromEntry(key, cache_path_));
4239+
EXPECT_NE(net::OK, OpenEntry(key, &entry));
4240+
}

net/disk_cache/simple/simple_backend_impl.cc

Lines changed: 1 addition & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -390,14 +390,7 @@ int SimpleBackendImpl::OpenEntry(const std::string& key,
390390
}
391391
scoped_refptr<SimpleEntryImpl> simple_entry =
392392
CreateOrFindActiveEntry(entry_hash, key);
393-
CompletionCallback backend_callback =
394-
base::Bind(&SimpleBackendImpl::OnEntryOpenedFromKey,
395-
AsWeakPtr(),
396-
key,
397-
entry,
398-
simple_entry,
399-
callback);
400-
return simple_entry->OpenEntry(entry, backend_callback);
393+
return simple_entry->OpenEntry(entry, callback);
401394
}
402395

403396
int SimpleBackendImpl::CreateEntry(const std::string& key,
@@ -713,29 +706,6 @@ void SimpleBackendImpl::OnEntryOpenedFromHash(
713706
}
714707
}
715708

716-
void SimpleBackendImpl::OnEntryOpenedFromKey(
717-
const std::string key,
718-
Entry** entry,
719-
const scoped_refptr<SimpleEntryImpl>& simple_entry,
720-
const CompletionCallback& callback,
721-
int error_code) {
722-
int final_code = error_code;
723-
if (final_code == net::OK) {
724-
bool key_matches = key.compare(simple_entry->key()) == 0;
725-
if (!key_matches) {
726-
// TODO(clamy): Add a unit test to check this code path.
727-
DLOG(WARNING) << "Key mismatch on open.";
728-
simple_entry->Doom();
729-
simple_entry->Close();
730-
final_code = net::ERR_FAILED;
731-
} else {
732-
DCHECK_EQ(simple_entry->entry_hash(), simple_util::GetEntryHashKey(key));
733-
}
734-
SIMPLE_CACHE_UMA(BOOLEAN, "KeyMatchedOnOpen", cache_type_, key_matches);
735-
}
736-
callback.Run(final_code);
737-
}
738-
739709
void SimpleBackendImpl::DoomEntriesComplete(
740710
std::unique_ptr<std::vector<uint64_t>> entry_hashes,
741711
const net::CompletionCallback& callback,

net/disk_cache/simple/simple_entry_format.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,15 @@ const uint64_t kSimpleSparseRangeMagicNumber = UINT64_C(0xeb97bf016553676b);
2525
// - the data from stream 1.
2626
// - a SimpleFileEOF record for stream 1.
2727
// - the data from stream 0.
28+
// - (optionally) the SHA256 of the key.
2829
// - a SimpleFileEOF record for stream 0.
30+
//
31+
// Because stream 0 data (typically HTTP headers) is on the critical path of
32+
// requests, on open, the cache reads the end of the record and does not
33+
// read the SimpleFileHeader. If the key can be validated with a SHA256, then
34+
// the stream 0 data can be returned to the caller without reading the
35+
// SimpleFileHeader. If the key SHA256 is not present, then the cache must
36+
// read the SimpleFileHeader to confirm key equality.
2937

3038
// A file containing stream 2 in the Simple cache consists of:
3139
// - a SimpleFileHeader.
@@ -47,6 +55,7 @@ struct NET_EXPORT_PRIVATE SimpleFileHeader {
4755
struct NET_EXPORT_PRIVATE SimpleFileEOF {
4856
enum Flags {
4957
FLAG_HAS_CRC32 = (1U << 0),
58+
FLAG_HAS_KEY_SHA256 = (1U << 1), // Preceding the record if present.
5059
};
5160

5261
SimpleFileEOF();

net/disk_cache/simple/simple_entry_impl.cc

Lines changed: 13 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -696,19 +696,13 @@ void SimpleEntryImpl::OpenEntryInternal(bool have_index,
696696
std::unique_ptr<SimpleEntryCreationResults> results(
697697
new SimpleEntryCreationResults(SimpleEntryStat(
698698
last_used_, last_modified_, data_size_, sparse_data_size_)));
699-
Closure task = base::Bind(&SimpleSynchronousEntry::OpenEntry,
700-
cache_type_,
701-
path_,
702-
entry_hash_,
703-
have_index,
704-
results.get());
705-
Closure reply = base::Bind(&SimpleEntryImpl::CreationOperationComplete,
706-
this,
707-
callback,
708-
start_time,
709-
base::Passed(&results),
710-
out_entry,
711-
net::NetLog::TYPE_SIMPLE_CACHE_ENTRY_OPEN_END);
699+
Closure task =
700+
base::Bind(&SimpleSynchronousEntry::OpenEntry, cache_type_, path_, key_,
701+
entry_hash_, have_index, results.get());
702+
Closure reply =
703+
base::Bind(&SimpleEntryImpl::CreationOperationComplete, this, callback,
704+
start_time, base::Passed(&results), out_entry,
705+
net::NetLog::TYPE_SIMPLE_CACHE_ENTRY_OPEN_END);
712706
worker_pool_->PostTaskAndReply(FROM_HERE, task, reply);
713707
}
714708

@@ -1147,11 +1141,14 @@ void SimpleEntryImpl::CreationOperationComplete(
11471141
crc32s_[0] = in_results->stream_0_crc32;
11481142
crc32s_end_offset_[0] = in_results->entry_stat.data_size(0);
11491143
}
1144+
// If this entry was opened by hash, key_ could still be empty. If so, update
1145+
// it with the key read from the synchronous entry.
11501146
if (key_.empty()) {
11511147
SetKey(synchronous_entry_->key());
11521148
} else {
1153-
// This should only be triggered when creating an entry. The key check in
1154-
// the open case is handled in SimpleBackendImpl.
1149+
// This should only be triggered when creating an entry. In the open case
1150+
// the key is either copied from the arguments to open, or checked
1151+
// in the synchronous entry.
11551152
DCHECK_EQ(key_, synchronous_entry_->key());
11561153
}
11571154
UpdateDataFromEntryStat(in_results->entry_stat);
@@ -1403,7 +1400,7 @@ int64_t SimpleEntryImpl::GetDiskUsage() const {
14031400
int64_t file_size = 0;
14041401
for (int i = 0; i < kSimpleEntryStreamCount; ++i) {
14051402
file_size +=
1406-
simple_util::GetFileSizeFromKeyAndDataSize(key_, data_size_[i]);
1403+
simple_util::GetFileSizeFromDataSize(key_.size(), data_size_[i]);
14071404
}
14081405
file_size += sparse_data_size_;
14091406
return file_size;

net/disk_cache/simple/simple_entry_impl.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,11 @@ class NET_EXPORT_PRIVATE SimpleEntryImpl : public Entry,
7979

8080
const std::string& key() const { return key_; }
8181
uint64_t entry_hash() const { return entry_hash_; }
82+
83+
// The key is not a constructor parameter to the SimpleEntryImpl, because
84+
// during cache iteration, it's necessary to open entries by their hash
85+
// alone. In that case, the SimpleSynchronousEntry will read the key from disk
86+
// and it will be set.
8287
void SetKey(const std::string& key);
8388

8489
// From Entry:

0 commit comments

Comments
 (0)