Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(224)

Unified Diff: net/http/http_cache_unittest.cc

Issue 2721933002: HttpCache::Transaction layer allowing parallel validation (Closed)
Patch Set: Josh's latest feedback addressed Created 3 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: net/http/http_cache_unittest.cc
diff --git a/net/http/http_cache_unittest.cc b/net/http/http_cache_unittest.cc
index 27222a905449abe15cd263149bb25c373a576a89..eba3ca58127d5100358656042e2fe3a1cb74caf9 100644
--- a/net/http/http_cache_unittest.cc
+++ b/net/http/http_cache_unittest.cc
@@ -1375,7 +1375,10 @@ TEST(HttpCache, SimpleGET_ManyReaders) {
base::RunLoop().RunUntilIdle();
// The first request should be a writer at this point, and the subsequent
- // requests should be pending.
+ // requests should have passed the validation phase and waiting for the
+ // response to be written to the cache before they can read.
+ EXPECT_TRUE(cache.IsWriterPresent(kSimpleGET_Transaction.url));
+ EXPECT_EQ(4, cache.GetCountDoneHeadersQueue(kSimpleGET_Transaction.url));
EXPECT_EQ(1, cache.network_layer()->transaction_count());
EXPECT_EQ(0, cache.disk_cache()->open_count());
@@ -1392,6 +1395,12 @@ TEST(HttpCache, SimpleGET_ManyReaders) {
Context* c = context_list[i];
if (c->result == ERR_IO_PENDING)
c->result = c->callback.WaitForResult();
+
+ if (i == 1) {
+ EXPECT_FALSE(cache.IsWriterPresent(kSimpleGET_Transaction.url));
+ EXPECT_EQ(4, cache.GetCountReaders(kSimpleGET_Transaction.url));
+ }
+
ReadAndVerifyTransaction(c->trans.get(), kSimpleGET_Transaction);
}
@@ -1407,6 +1416,345 @@ TEST(HttpCache, SimpleGET_ManyReaders) {
}
}
+// Parallel validation results in 200.
+TEST(HttpCache, SimpleGET_ParallelValidationNoMatch) {
+ MockHttpCache cache;
+
+ MockHttpRequest request(kSimpleGET_Transaction);
+ request.load_flags |= LOAD_VALIDATE_CACHE;
+
+ std::vector<Context*> context_list;
+ const int kNumTransactions = 5;
+
+ for (int i = 0; i < kNumTransactions; ++i) {
+ context_list.push_back(new Context());
+ Context* c = context_list[i];
+
+ c->result = cache.CreateTransaction(&c->trans);
+ ASSERT_THAT(c->result, IsOk());
+ EXPECT_EQ(LOAD_STATE_IDLE, c->trans->GetLoadState());
+
+ c->result =
+ c->trans->Start(&request, c->callback.callback(), NetLogWithSource());
+ }
+
+ // All requests are waiting for the active entry.
+ for (int i = 0; i < kNumTransactions; ++i) {
+ Context* c = context_list[i];
+ EXPECT_EQ(LOAD_STATE_WAITING_FOR_CACHE, c->trans->GetLoadState());
+ }
+
+ // Allow all requests to move from the Create queue to the active entry.
+ base::RunLoop().RunUntilIdle();
+
+ // The first request should be a writer at this point, and the subsequent
+ // requests should have passed the validation phase and created their own
+ // entries since none of them matched the headers of the earlier one.
+ EXPECT_TRUE(cache.IsWriterPresent(kSimpleGET_Transaction.url));
+
+ EXPECT_EQ(5, cache.network_layer()->transaction_count());
+ EXPECT_EQ(0, cache.disk_cache()->open_count());
+ EXPECT_EQ(3, cache.disk_cache()->create_count());
+
+ // All requests depend on the writer, and the writer is between Start and
+ // Read, i.e. idle.
+ for (int i = 0; i < kNumTransactions; ++i) {
+ Context* c = context_list[i];
+ EXPECT_EQ(LOAD_STATE_IDLE, c->trans->GetLoadState());
+ }
+
+ for (int i = 0; i < kNumTransactions; ++i) {
+ Context* c = context_list[i];
+ if (c->result == ERR_IO_PENDING)
+ c->result = c->callback.WaitForResult();
+ ReadAndVerifyTransaction(c->trans.get(), kSimpleGET_Transaction);
+ }
+
+ EXPECT_EQ(5, cache.network_layer()->transaction_count());
+ EXPECT_EQ(0, cache.disk_cache()->open_count());
+ EXPECT_EQ(3, cache.disk_cache()->create_count());
+
+ for (int i = 0; i < kNumTransactions; ++i) {
+ Context* c = context_list[i];
+ delete c;
+ }
+}
+
+// Tests that a GET followed by a DELETE results in DELETE immediately starting
+// the headers phase and the entry is doomed.
+TEST(HttpCache, SimpleGET_ParallelValidationDelete) {
+ MockHttpCache cache;
+
+ MockHttpRequest request(kSimpleGET_Transaction);
+ request.load_flags |= LOAD_VALIDATE_CACHE;
+
+ MockHttpRequest delete_request(kSimpleGET_Transaction);
+ delete_request.method = "DELETE";
+
+ std::vector<Context*> context_list;
+ const int kNumTransactions = 2;
+
+ for (int i = 0; i < kNumTransactions; ++i) {
+ context_list.push_back(new Context());
+ Context* c = context_list[i];
+
+ MockHttpRequest* this_request = &request;
+ if (i == 1)
+ this_request = &delete_request;
+
+ c->result = cache.CreateTransaction(&c->trans);
+ ASSERT_THAT(c->result, IsOk());
+ EXPECT_EQ(LOAD_STATE_IDLE, c->trans->GetLoadState());
+
+ c->result = c->trans->Start(this_request, c->callback.callback(),
+ NetLogWithSource());
+ }
+
+ // All requests are waiting for the active entry.
+ for (int i = 0; i < kNumTransactions; ++i) {
+ Context* c = context_list[i];
+ EXPECT_EQ(LOAD_STATE_WAITING_FOR_CACHE, c->trans->GetLoadState());
+ }
+
+ // Allow all requests to move from the Create queue to the active entry.
+ base::RunLoop().RunUntilIdle();
+
+ // The first request should be a writer at this point, and the subsequent
+ // request should have passed the validation phase and doomed the existing
+ // entry.
+ EXPECT_TRUE(
+ cache.disk_cache()->IsDiskEntryDoomed(kSimpleGET_Transaction.url));
+
+ EXPECT_EQ(2, cache.network_layer()->transaction_count());
+ EXPECT_EQ(0, cache.disk_cache()->open_count());
+ EXPECT_EQ(1, cache.disk_cache()->create_count());
+
+ // All requests depend on the writer, and the writer is between Start and
+ // Read, i.e. idle.
+ for (int i = 0; i < kNumTransactions; ++i) {
+ Context* c = context_list[i];
+ EXPECT_EQ(LOAD_STATE_IDLE, c->trans->GetLoadState());
+ }
+
+ for (int i = 0; i < kNumTransactions; ++i) {
+ Context* c = context_list[i];
+ if (c->result == ERR_IO_PENDING)
+ c->result = c->callback.WaitForResult();
+ ReadAndVerifyTransaction(c->trans.get(), kSimpleGET_Transaction);
+ }
+
+ EXPECT_EQ(2, cache.network_layer()->transaction_count());
+ EXPECT_EQ(0, cache.disk_cache()->open_count());
+ EXPECT_EQ(1, cache.disk_cache()->create_count());
+
+ for (int i = 0; i < kNumTransactions; ++i) {
+ Context* c = context_list[i];
+ delete c;
+ }
+}
+
+// Tests that a transaction which is in validated queue can be destroyed without
+// any impact to other transactions.
Randy Smith (Not in Mondays) 2017/04/04 21:21:16 What if the currently active headers transaction i
shivanisha 2017/04/05 15:00:37 It seems there doesn't exist a way to pause and re
Randy Smith (Not in Mondays) 2017/04/05 18:39:17 I'm not sure that's true. Did you look at the Bef
+TEST(HttpCache, SimpleGET_ParallelValidationCancelValidated) {
+ MockHttpCache cache;
+
+ MockHttpRequest request(kSimpleGET_Transaction);
+
+ std::vector<Context*> context_list;
+ const int kNumTransactions = 2;
+
+ for (int i = 0; i < kNumTransactions; ++i) {
+ context_list.push_back(new Context());
+ Context* c = context_list[i];
+
+ c->result = cache.CreateTransaction(&c->trans);
+ ASSERT_THAT(c->result, IsOk());
+
+ c->result =
+ c->trans->Start(&request, c->callback.callback(), NetLogWithSource());
+ }
+
+ // Allow all requests to move from the Create queue to the active entry.
+ base::RunLoop().RunUntilIdle();
+
+ // The first request should be a writer at this point, and the subsequent
+ // requests should have completed validation.
+
+ EXPECT_EQ(1, cache.network_layer()->transaction_count());
+ EXPECT_EQ(0, cache.disk_cache()->open_count());
+ EXPECT_EQ(1, cache.disk_cache()->create_count());
+
+ EXPECT_TRUE(cache.IsWriterPresent(kSimpleGET_Transaction.url));
+ EXPECT_EQ(1, cache.GetCountDoneHeadersQueue(kSimpleGET_Transaction.url));
+
+ Context* c = context_list[1];
+ delete c;
+ context_list[1] = nullptr;
+
+ EXPECT_EQ(0, cache.GetCountDoneHeadersQueue(kSimpleGET_Transaction.url));
+
+ // Complete the rest of the transactions.
+ for (int i = 0; i < kNumTransactions; ++i) {
+ if (i == 1)
+ continue;
+ Context* c = context_list[i];
+ ReadAndVerifyTransaction(c->trans.get(), kSimpleGET_Transaction);
+ }
+
+ EXPECT_EQ(1, cache.network_layer()->transaction_count());
+ EXPECT_EQ(0, cache.disk_cache()->open_count());
+ EXPECT_EQ(1, cache.disk_cache()->create_count());
+
+ for (int i = 0; i < kNumTransactions; ++i) {
+ if (i == 1)
+ continue;
+ Context* c = context_list[i];
+ delete c;
+ }
+}
+
+// Tests that a transaction is in validated queue and writer is destroyed
+// leading to restarting the validated transaction.
+TEST(HttpCache, SimpleGET_ParallelValidationCancelWriter) {
+ MockHttpCache cache;
+
+ MockHttpRequest request(kSimpleGET_Transaction);
+
+ std::vector<Context*> context_list;
+ const int kNumTransactions = 2;
+
+ for (int i = 0; i < kNumTransactions; ++i) {
+ context_list.push_back(new Context());
+ Context* c = context_list[i];
+
+ c->result = cache.CreateTransaction(&c->trans);
+ ASSERT_THAT(c->result, IsOk());
+
+ c->result =
+ c->trans->Start(&request, c->callback.callback(), NetLogWithSource());
+ }
+
+ // Allow all requests to move from the Create queue to the active entry.
+ base::RunLoop().RunUntilIdle();
+
+ // The first request should be a writer at this point, and the subsequent
+ // requests should have completed validation.
+
+ EXPECT_EQ(1, cache.network_layer()->transaction_count());
+ EXPECT_EQ(0, cache.disk_cache()->open_count());
+ EXPECT_EQ(1, cache.disk_cache()->create_count());
+
+ EXPECT_TRUE(cache.IsWriterPresent(kSimpleGET_Transaction.url));
+ EXPECT_EQ(1, cache.GetCountDoneHeadersQueue(kSimpleGET_Transaction.url));
+
+ Context* c = context_list[0];
+ delete c;
+ context_list[0] = nullptr;
+
+ base::RunLoop().RunUntilIdle();
+
+ // Complete the rest of the transactions.
+ for (int i = 0; i < kNumTransactions; ++i) {
+ if (i == 0)
+ continue;
+ Context* c = context_list[i];
+ ReadAndVerifyTransaction(c->trans.get(), kSimpleGET_Transaction);
+ }
+
+ EXPECT_EQ(2, cache.network_layer()->transaction_count());
+ EXPECT_EQ(0, cache.disk_cache()->open_count());
+ EXPECT_EQ(2, cache.disk_cache()->create_count());
+
+ for (int i = 0; i < kNumTransactions; ++i) {
+ if (i == 1)
+ continue;
+ Context* c = context_list[i];
+ delete c;
+ }
+}
+// Similar to the above test, except here cache write fails and the
+// validated transactions should be restarted.
+TEST(HttpCache, SimpleGET_ParallelValidationFailWrite) {
+ MockHttpCache cache;
+
+ MockHttpRequest request(kSimpleGET_Transaction);
+
+ std::vector<Context*> context_list;
+ const int kNumTransactions = 5;
+
+ for (int i = 0; i < kNumTransactions; ++i) {
+ context_list.push_back(new Context());
+ Context* c = context_list[i];
+
+ c->result = cache.CreateTransaction(&c->trans);
+ ASSERT_THAT(c->result, IsOk());
+ EXPECT_EQ(LOAD_STATE_IDLE, c->trans->GetLoadState());
+
+ c->result =
+ c->trans->Start(&request, c->callback.callback(), NetLogWithSource());
+ }
+
+ // All requests are waiting for the active entry.
+ for (int i = 0; i < kNumTransactions; ++i) {
+ Context* c = context_list[i];
+ EXPECT_EQ(LOAD_STATE_WAITING_FOR_CACHE, c->trans->GetLoadState());
+ }
+
+ // Allow all requests to move from the Create queue to the active entry.
+ base::RunLoop().RunUntilIdle();
+
+ // The first request should be a writer at this point, and the subsequent
+ // requests should have passed the validation phase and waiting for the
+ // response to be written to the cache before they can read.
+ EXPECT_TRUE(cache.IsWriterPresent(kSimpleGET_Transaction.url));
+ EXPECT_EQ(4, cache.GetCountDoneHeadersQueue(kSimpleGET_Transaction.url));
+
+ // All requests depend on the writer, and the writer is between Start and
+ // Read, i.e. idle.
+ for (int i = 0; i < kNumTransactions; ++i) {
+ Context* c = context_list[i];
+ EXPECT_EQ(LOAD_STATE_IDLE, c->trans->GetLoadState());
+ }
+
+ // The first request should be a writer at this point, and the subsequent
+ // requests should have passed the validation phase and waiting for the
+ // response to be written to the cache before they can read.
+
+ EXPECT_EQ(1, cache.network_layer()->transaction_count());
+ EXPECT_EQ(0, cache.disk_cache()->open_count());
+ EXPECT_EQ(1, cache.disk_cache()->create_count());
+
+ // Fail the request.
+ cache.disk_cache()->set_soft_failures(true);
+ // We have to open the entry again to propagate the failure flag.
+ disk_cache::Entry* en;
+ cache.OpenBackendEntry(kSimpleGET_Transaction.url, &en);
+ en->Close();
+
+ for (int i = 0; i < kNumTransactions; ++i) {
+ Context* c = context_list[i];
+ if (c->result == ERR_IO_PENDING)
+ c->result = c->callback.WaitForResult();
+ if (i == 1) {
+ // The earlier entry must be destroyed and its disk entry doomed.
+ EXPECT_TRUE(
+ cache.disk_cache()->IsDiskEntryDoomed(kSimpleGET_Transaction.url));
+ }
+ ReadAndVerifyTransaction(c->trans.get(), kSimpleGET_Transaction);
+ }
+
+ // Since validated transactions were restarted and new entry read/write
+ // operations would also fail, all requests would have gone to the network.
+ EXPECT_EQ(5, cache.network_layer()->transaction_count());
+ EXPECT_EQ(1, cache.disk_cache()->open_count());
+ EXPECT_EQ(5, cache.disk_cache()->create_count());
+
+ for (int i = 0; i < kNumTransactions; ++i) {
+ Context* c = context_list[i];
+ delete c;
+ }
+}
Randy Smith (Not in Mondays) 2017/04/04 21:21:16 Should we also confirm that destroying a reader do
shivanisha 2017/04/05 15:00:37 Added a test for that: HttpCache.SimpleGET_Paralle
Randy Smith (Not in Mondays) 2017/04/05 18:39:17 Acknowledged.
+
// This is a test for http://code.google.com/p/chromium/issues/detail?id=4769.
// If cancelling a request is racing with another request for the same resource
// finishing, we have to make sure that we remove both transactions from the
@@ -1451,11 +1799,12 @@ TEST(HttpCache, SimpleGET_RacingReaders) {
c->result = c->callback.WaitForResult();
ReadAndVerifyTransaction(c->trans.get(), kSimpleGET_Transaction);
- // Now we have 2 active readers and two queued transactions.
-
+ // Now all transactions should be waiting for read to be invoked. Two readers
+ // are because of the load flags and remaining two transactions were converted
+ // to readers after skipping validation. Note that the remaining two went on
+ // to process the headers in parallel with readers presnt on the entry.
EXPECT_EQ(LOAD_STATE_IDLE, context_list[2]->trans->GetLoadState());
- EXPECT_EQ(LOAD_STATE_WAITING_FOR_CACHE,
- context_list[3]->trans->GetLoadState());
+ EXPECT_EQ(LOAD_STATE_IDLE, context_list[3]->trans->GetLoadState());
c = context_list[1];
ASSERT_THAT(c->result, IsError(ERR_IO_PENDING));
@@ -1520,12 +1869,16 @@ TEST(HttpCache, SimpleGET_DoomWithPending) {
NetLogWithSource());
}
+ base::RunLoop().RunUntilIdle();
+
// The first request should be a writer at this point, and the two subsequent
// requests should be pending. The last request doomed the first entry.
EXPECT_EQ(2, cache.network_layer()->transaction_count());
- // Cancel the first queued transaction.
+ // Cancel the second transaction. Note that this and the 3rd transactions
+ // would have completed their headers phase and would be waiting in the
+ // done_headers_queue when the 2nd transaction is cancelled.
context_list[1].reset();
for (int i = 0; i < kNumTransactions; ++i) {
@@ -1567,11 +1920,12 @@ TEST(HttpCache, FastNoStoreGET_DoneWithPending) {
base::RunLoop().RunUntilIdle();
// The first request should be a writer at this point, and the subsequent
- // requests should be pending.
+ // requests should have completed validation. Since the validation does not
+ // result in a match, a new entry would be created.
- EXPECT_EQ(1, cache.network_layer()->transaction_count());
+ EXPECT_EQ(3, cache.network_layer()->transaction_count());
EXPECT_EQ(0, cache.disk_cache()->open_count());
- EXPECT_EQ(1, cache.disk_cache()->create_count());
+ EXPECT_EQ(2, cache.disk_cache()->create_count());
// Now, make sure that the second request asks for the entry not to be stored.
request_handler.set_no_store(true);
@@ -1625,6 +1979,9 @@ TEST(HttpCache, SimpleGET_ManyWriters_CancelFirst) {
if (c->result == ERR_IO_PENDING)
c->result = c->callback.WaitForResult();
// Destroy only the first transaction.
+ // This should lead to all transactions to restart, even those that have
+ // validated themselves and were waiting for the writer transaction to
+ // complete writing to the cache.
if (i == 0) {
delete c;
context_list[i] = NULL;
@@ -2540,6 +2897,8 @@ TEST(HttpCache, ETagGET_ConditionalRequest_304_NoStore) {
// be returned.
// (4) loads |kUrl| from cache only -- expects |cached_response_2| to be
// returned.
+// The entry will be created once and will be opened for the 3 subsequent
+// requests.
static void ConditionalizedRequestUpdatesCacheHelper(
const Response& net_response_1,
const Response& net_response_2,
@@ -6115,12 +6474,14 @@ TEST(HttpCache, GET_IncompleteResource_Cancel) {
EXPECT_EQ(5, c->callback.GetResult(rv));
// Cancel the requests.
+ // Since |pending| is currently vaidating the already written headers
+ // it will be restarted as well.
delete c;
delete pending;
EXPECT_EQ(1, cache.network_layer()->transaction_count());
EXPECT_EQ(1, cache.disk_cache()->open_count());
- EXPECT_EQ(2, cache.disk_cache()->create_count());
+ EXPECT_EQ(1, cache.disk_cache()->create_count());
base::RunLoop().RunUntilIdle();
RemoveMockTransaction(&transaction);
@@ -6845,46 +7206,6 @@ TEST(HttpCache, WriteMetadata_Fail) {
EXPECT_EQ(1, cache.disk_cache()->create_count());
}
-// Tests that if a metadata writer transaction hits cache lock timeout, it will
-// error out.
-TEST(HttpCache, WriteMetadata_CacheLockTimeout) {
- MockHttpCache cache;
-
- // Write to the cache
- HttpResponseInfo response;
- RunTransactionTestWithResponseInfo(cache.http_cache(), kSimpleGET_Transaction,
- &response);
- EXPECT_FALSE(response.metadata.get());
-
- MockHttpRequest request(kSimpleGET_Transaction);
- Context c1;
- ASSERT_THAT(cache.CreateTransaction(&c1.trans), IsOk());
- ASSERT_EQ(ERR_IO_PENDING, c1.trans->Start(&request, c1.callback.callback(),
- NetLogWithSource()));
-
- cache.SimulateCacheLockTimeout();
-
- // Write meta data to the same entry.
- scoped_refptr<IOBufferWithSize> buf(new IOBufferWithSize(50));
- memset(buf->data(), 0, buf->size());
- base::strlcpy(buf->data(), "Hi there", buf->size());
- cache.http_cache()->WriteMetadata(GURL(kSimpleGET_Transaction.url),
- DEFAULT_PRIORITY, response.response_time,
- buf.get(), buf->size());
-
- // Release the buffer before the operation takes place.
- buf = NULL;
-
- // Makes sure we finish pending operations.
- base::RunLoop().RunUntilIdle();
-
- RunTransactionTestWithResponseInfo(cache.http_cache(), kSimpleGET_Transaction,
- &response);
-
- // The writer transaction should fail due to cache lock timeout.
- ASSERT_FALSE(response.metadata.get());
-}
-
// Tests that we ignore VARY checks when writing metadata since the request
// headers for the WriteMetadata transaction are made up.
TEST(HttpCache, WriteMetadata_IgnoreVary) {
@@ -7212,10 +7533,6 @@ TEST(HttpCache, StopCachingWithAuthDeletesEntry) {
EXPECT_THAT(callback.GetResult(rv), IsOk());
trans->StopCaching();
-
- scoped_refptr<IOBuffer> buf(new IOBuffer(256));
- rv = trans->Read(buf.get(), 10, callback.callback());
- EXPECT_EQ(callback.GetResult(rv), 10);
}
RemoveMockTransaction(&mock_transaction);

Powered by Google App Engine
This is Rietveld 408576698