Skip to content
This repository was archived by the owner on Apr 10, 2025. It is now read-only.

Commit 834c20f

Browse files
committed
Before rewriting metadata cache entries for on-the-fly fetches, do a
read to see if the new value is different. This means we will do more cache reads but fewer cache writes most of the time. Skip optimization-locks for on-the-fly resources, which makes no sense for cheap uncacheable optimizations.
1 parent bb6b1d4 commit 834c20f

File tree

7 files changed

+97
-29
lines changed

7 files changed

+97
-29
lines changed

net/instaweb/rewriter/css_embedded_config_test.cc

+6-1
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,12 @@ class CssEmbeddedConfigTest : public CssRewriteTestBase {
102102
ResponseHeaders image_headers;
103103
GoogleString image;
104104
EXPECT_TRUE(FetchResourceUrl(image_url, &image, &image_headers));
105-
EXPECT_EQ(1, lru_cache()->num_hits());
105+
bool is_on_the_fly = image_url.find(".ce.") != StringPiece::npos;
106+
if (is_on_the_fly) {
107+
EXPECT_EQ(2, lru_cache()->num_hits());
108+
} else {
109+
EXPECT_EQ(1, lru_cache()->num_hits());
110+
}
106111
EXPECT_EQ(0, lru_cache()->num_misses());
107112
return image;
108113
}

net/instaweb/rewriter/public/rewrite_context.h

+2-1
Original file line numberDiff line numberDiff line change
@@ -611,7 +611,7 @@ class RewriteContext {
611611
virtual int64 GetRewriteDeadlineAlarmMs() const;
612612

613613
// Should the context call LockForCreation before checking the cache?
614-
virtual bool CreationLockBeforeStartFetch() const { return true; }
614+
virtual bool CreationLockBeforeStartFetch() const;
615615

616616
// Should the context fail to serve the rewritten resource if the hash
617617
// doesn't match user requested hash?
@@ -637,6 +637,7 @@ class RewriteContext {
637637
class DistributedRewriteCallback;
638638
class DistributedRewriteFetch;
639639
class OutputCacheCallback;
640+
class WriteIfChanged;
640641
class LookupMetadataForOutputResourceCallback;
641642
class HTTPCacheCallback;
642643
class ResourceCallbackUtils;

net/instaweb/rewriter/rewrite_context.cc

+55-1
Original file line numberDiff line numberDiff line change
@@ -571,6 +571,46 @@ class RewriteContext::OutputCacheCallback : public CacheInterface::Callback {
571571
scoped_ptr<CacheLookupResult> cache_result_;
572572
};
573573

574+
// When serving on-the-fly resources, our system rewrites the metadata
575+
// cache entry on each request, which is necessary if during the
576+
// serving we've detected any expirations or cache mismatches. To reduce
577+
// the number of cache-writes (which may write-through an L1 to a slower L2),
578+
// we first read the existing entry (possibly from L1) and compare it to what
579+
// we intend to write.
580+
//
581+
// This callback manages that flow.
582+
class RewriteContext::WriteIfChanged : public CacheInterface::Callback {
583+
public:
584+
// Reads value of key in cache, checking against *val. If different,
585+
// *val is put back into the cache.
586+
//
587+
// Note that *val will be cleared at the call-site (by swapping with an empty
588+
// string) when this call is made.
589+
static void ReadCheckAndWrite(const GoogleString& key, GoogleString* val,
590+
CacheInterface* cache) {
591+
cache->Get(key, new WriteIfChanged(key, val, cache));
592+
}
593+
594+
virtual void Done(CacheInterface::KeyState state) {
595+
if ((state != CacheInterface::kAvailable) || (value()->Value() != value_)) {
596+
cache_->PutSwappingString(key_, &value_);
597+
}
598+
delete this;
599+
}
600+
601+
private:
602+
WriteIfChanged(const GoogleString& key, GoogleString* value,
603+
CacheInterface* cache)
604+
: key_(key),
605+
cache_(cache) {
606+
value_.swap(*value);
607+
}
608+
609+
const GoogleString key_;
610+
GoogleString value_;
611+
CacheInterface* cache_;
612+
};
613+
574614
// Like OutputCacheCallback but forwarding info to an external user rather
575615
// than to RewriteContext
576616
class RewriteContext::LookupMetadataForOutputResourceCallback
@@ -2162,7 +2202,16 @@ void RewriteContext::WritePartition() {
21622202
StringOutputStream sstream(&buf); // finalizes buf in destructor
21632203
partitions_->SerializeToZeroCopyStream(&sstream);
21642204
}
2165-
metadata_cache->PutSwappingString(partition_key_, &buf);
2205+
2206+
// Unchanged on-the-fly resources usually have their metadata
2207+
// rewritten needlessly on fetches, so in that case do a Read
2208+
// first and check whether the new bits are any different, as in
2209+
// most cases a read is cheaper than a write.
2210+
if (IsFetchRewrite() && (kind() == kOnTheFlyResource)) {
2211+
WriteIfChanged::ReadCheckAndWrite(partition_key_, &buf, metadata_cache);
2212+
} else {
2213+
metadata_cache->PutSwappingString(partition_key_, &buf);
2214+
}
21662215
}
21672216
} else {
21682217
// TODO(jmarantz): if our rewrite failed due to lock contention or
@@ -3165,6 +3214,11 @@ int64 RewriteContext::GetRewriteDeadlineAlarmMs() const {
31653214
return Driver()->rewrite_deadline_ms();
31663215
}
31673216

3217+
bool RewriteContext::CreationLockBeforeStartFetch() const {
3218+
// Don't take rewrite-locks for on-the-fly resources.
3219+
return (kind() != kOnTheFlyResource);
3220+
}
3221+
31683222
namespace {
31693223

31703224
void AppendBool(GoogleString* out, const char* name, bool val,

net/instaweb/rewriter/rewrite_context_test.cc

+13-11
Original file line numberDiff line numberDiff line change
@@ -986,8 +986,8 @@ TEST_F(RewriteContextTest, TrimFetchOnTheFly) {
986986
"a.css", "css", &content));
987987
EXPECT_EQ("a", content);
988988
EXPECT_EQ(0, lru_cache()->num_hits());
989-
EXPECT_EQ(1, lru_cache()->num_misses()); // 1 because output is not saved
990-
EXPECT_EQ(2, lru_cache()->num_inserts()); // input, metadata
989+
EXPECT_EQ(2, lru_cache()->num_misses()); // output + metadata.
990+
EXPECT_EQ(2, lru_cache()->num_inserts()); // input + metadata.
991991
EXPECT_EQ(1, counting_url_async_fetcher()->fetch_count());
992992
ClearStats();
993993
content.clear();
@@ -997,7 +997,7 @@ TEST_F(RewriteContextTest, TrimFetchOnTheFly) {
997997
kTestDomain, TrimWhitespaceRewriter::kFilterId, "a.css", "css",
998998
&content));
999999
EXPECT_EQ("a", content);
1000-
EXPECT_EQ(1, lru_cache()->num_hits());
1000+
EXPECT_EQ(2, lru_cache()->num_hits());
10011001
EXPECT_EQ(0, lru_cache()->num_misses());
10021002
EXPECT_EQ(0, lru_cache()->num_inserts());
10031003
EXPECT_EQ(0, counting_url_async_fetcher()->fetch_count());
@@ -1303,7 +1303,7 @@ TEST_F(RewriteContextTest, FetchUncacheableWithRewritesInLineOfServing) {
13031303
&content));
13041304
EXPECT_EQ("a", content);
13051305
EXPECT_EQ(0, lru_cache()->num_hits());
1306-
EXPECT_EQ(1, lru_cache()->num_misses());
1306+
EXPECT_EQ(2, lru_cache()->num_misses());
13071307
EXPECT_EQ(2, lru_cache()->num_inserts()); // name mapping & uncacheable memo
13081308
EXPECT_EQ(1, counting_url_async_fetcher()->fetch_count());
13091309

@@ -1318,7 +1318,7 @@ TEST_F(RewriteContextTest, FetchUncacheableWithRewritesInLineOfServing) {
13181318
"css",
13191319
&content));
13201320
EXPECT_EQ("a", content);
1321-
EXPECT_EQ(1, lru_cache()->num_hits());
1321+
EXPECT_EQ(2, lru_cache()->num_hits());
13221322
EXPECT_EQ(0, lru_cache()->num_misses());
13231323
EXPECT_EQ(0, lru_cache()->num_inserts());
13241324
EXPECT_EQ(1, counting_url_async_fetcher()->fetch_count());
@@ -1348,7 +1348,7 @@ TEST_F(RewriteContextTest, FetchUncacheableWithRewritesInLineOfServing) {
13481348
"css",
13491349
&content));
13501350
EXPECT_EQ("b", content);
1351-
EXPECT_EQ(1, lru_cache()->num_hits());
1351+
EXPECT_EQ(2, lru_cache()->num_hits());
13521352
EXPECT_EQ(0, lru_cache()->num_misses());
13531353
EXPECT_EQ(0, lru_cache()->num_inserts());
13541354
EXPECT_EQ(1, counting_url_async_fetcher()->fetch_count());
@@ -1364,7 +1364,7 @@ TEST_F(RewriteContextTest, FetchUncacheableWithRewritesInLineOfServing) {
13641364
"css",
13651365
&content));
13661366
EXPECT_EQ("b", content);
1367-
EXPECT_EQ(1, lru_cache()->num_hits());
1367+
EXPECT_EQ(2, lru_cache()->num_hits());
13681368
EXPECT_EQ(0, lru_cache()->num_misses());
13691369
EXPECT_EQ(2, lru_cache()->num_inserts());
13701370
EXPECT_EQ(1, counting_url_async_fetcher()->fetch_count());
@@ -1669,7 +1669,7 @@ TEST_F(RewriteContextTest, CacheControlWithMultipleInputResources) {
16691669
EXPECT_EQ(" a b a ", content);
16701670

16711671
EXPECT_EQ(0, lru_cache()->num_hits());
1672-
EXPECT_EQ(3, lru_cache()->num_misses()); // 3 inputs.
1672+
EXPECT_EQ(4, lru_cache()->num_misses()); // 3 inputs.
16731673
EXPECT_EQ(4, lru_cache()->num_inserts()) <<
16741674
"partition, 2 inputs, 1 non-cacheability note";
16751675
EXPECT_EQ(3, counting_url_async_fetcher()->fetch_count());
@@ -1771,7 +1771,7 @@ TEST_F(RewriteContextTest, CacheControlWithMultipleInputResourcesAndNoStore) {
17711771
EXPECT_EQ(" a b a a ", content);
17721772

17731773
EXPECT_EQ(0, lru_cache()->num_hits());
1774-
EXPECT_EQ(4, lru_cache()->num_misses()); // 4 inputs.
1774+
EXPECT_EQ(5, lru_cache()->num_misses()); // 4 inputs + metadata.
17751775
EXPECT_EQ(5, lru_cache()->num_inserts())
17761776
<< "partition, 2 inputs, 2 non-cacheability notes";
17771777
EXPECT_EQ(4, counting_url_async_fetcher()->fetch_count());
@@ -2057,18 +2057,20 @@ TEST_F(RewriteContextTest, FetchColdCacheOnTheFlyNotFound) {
20572057
EXPECT_FALSE(FetchResource(kTestDomain, TrimWhitespaceRewriter::kFilterId,
20582058
"a.css", "css", &content));
20592059
EXPECT_EQ(0, lru_cache()->num_hits());
2060-
EXPECT_EQ(1, lru_cache()->num_misses());
2060+
EXPECT_EQ(2, lru_cache()->num_misses());
20612061
EXPECT_EQ(2, lru_cache()->num_inserts()); // fetch failure, metadata.
2062+
EXPECT_EQ(0, lru_cache()->num_identical_reinserts());
20622063
EXPECT_EQ(1, counting_url_async_fetcher()->fetch_count());
20632064
ClearStats();
20642065

20652066
// Try it again with a warm cache. We'll get a 'hit' which will inform us
20662067
// that this resource is not fetchable.
20672068
EXPECT_FALSE(FetchResource(kTestDomain, TrimWhitespaceRewriter::kFilterId,
20682069
"a.css", "css", &content));
2069-
EXPECT_EQ(1, lru_cache()->num_hits());
2070+
EXPECT_EQ(2, lru_cache()->num_hits());
20702071
EXPECT_EQ(0, lru_cache()->num_misses());
20712072
EXPECT_EQ(0, lru_cache()->num_inserts()); // We "remember" the fetch failure
2073+
EXPECT_EQ(0, lru_cache()->num_identical_reinserts());
20722074
EXPECT_EQ(0, counting_url_async_fetcher()->fetch_count());
20732075
}
20742076

net/instaweb/rewriter/rewrite_driver_test.cc

+8-4
Original file line numberDiff line numberDiff line change
@@ -702,10 +702,12 @@ TEST_F(RewriteDriverTest, TestCacheUseOnTheFly) {
702702
int cold_num_inserts = lru_cache()->num_inserts();
703703
EXPECT_EQ(2, cold_num_inserts);
704704

705-
// Warm load. This one re-inserts in the rname entry, without changing it.
705+
// Warm load. This one does a read-check to avoid a re-inserts in the
706+
// rname entry.
706707
EXPECT_TRUE(TryFetchResource(cache_extended_url));
707708
EXPECT_EQ(cold_num_inserts, lru_cache()->num_inserts());
708-
EXPECT_EQ(1, lru_cache()->num_identical_reinserts());
709+
EXPECT_EQ(2, lru_cache()->num_hits());
710+
EXPECT_EQ(0, lru_cache()->num_identical_reinserts());
709711
}
710712

711713
// Verifies that the computed rewrite delay agrees with expectations
@@ -766,10 +768,12 @@ TEST_F(RewriteDriverTest, TestCacheUseOnTheFlyWithInvalidation) {
766768
int cold_num_inserts = lru_cache()->num_inserts();
767769
EXPECT_EQ(2, cold_num_inserts);
768770

769-
// Warm load. This one re-inserts in the rname entry, without changing it.
771+
// Warm load. This one does a read-check to avoid a re-inserts in the
772+
// rname entry.
770773
EXPECT_TRUE(TryFetchResource(cache_extended_url));
771774
EXPECT_EQ(cold_num_inserts, lru_cache()->num_inserts());
772-
EXPECT_EQ(1, lru_cache()->num_identical_reinserts());
775+
EXPECT_EQ(0, lru_cache()->num_identical_reinserts());
776+
EXPECT_EQ(2, lru_cache()->num_hits());
773777

774778
// Set cache invalidation timestamp (to now, so that response date header is
775779
// in the "past") and load.

net/instaweb/rewriter/rewrite_test_base.cc

+6-2
Original file line numberDiff line numberDiff line change
@@ -607,8 +607,13 @@ void RewriteTestBase::TestServeFiles(
607607
EXPECT_EQ(0U, lru_cache()->num_hits());
608608
EXPECT_TRUE(FetchResource(kTestDomain, filter_id,
609609
rewritten_name, rewritten_ext, &content));
610+
RewriteFilter* filter = rewrite_driver_->FindFilter(filter_id);
610611
if (lru_cache()->IsHealthy()) {
611-
EXPECT_EQ(1U, lru_cache()->num_hits());
612+
if (filter->ComputeOnTheFly()) {
613+
EXPECT_EQ(2U, lru_cache()->num_hits());
614+
} else {
615+
EXPECT_EQ(1U, lru_cache()->num_hits());
616+
}
612617
}
613618
EXPECT_STREQ(rewritten_content, content);
614619

@@ -621,7 +626,6 @@ void RewriteTestBase::TestServeFiles(
621626
EXPECT_EQ(rewritten_content, content);
622627

623628
// Now we expect the cache entry to be there.
624-
RewriteFilter* filter = rewrite_driver_->FindFilter(filter_id);
625629
if (!filter->ComputeOnTheFly() && lru_cache()->IsHealthy()) {
626630
HTTPValue value;
627631
ResponseHeaders response_headers;

pagespeed/automatic/proxy_interface_test.cc

+7-9
Original file line numberDiff line numberDiff line change
@@ -2476,7 +2476,7 @@ TEST_F(ProxyInterfaceTest, UncacheableResourcesNotCachedOnResourceFetch) {
24762476
EXPECT_EQ("a", out_text);
24772477
EXPECT_EQ(1, lru_cache()->num_hits()); // input uncacheable memo
24782478
EXPECT_EQ(0, http_cache()->cache_hits()->Get());
2479-
EXPECT_EQ(0, lru_cache()->num_misses());
2479+
EXPECT_EQ(1, lru_cache()->num_misses());
24802480
EXPECT_EQ(1, http_cache()->cache_misses()->Get()); // input uncacheable memo
24812481
EXPECT_EQ(1, lru_cache()->num_inserts()); // mapping
24822482
EXPECT_EQ(1, lru_cache()->num_identical_reinserts()); // uncacheable memo
@@ -2492,13 +2492,12 @@ TEST_F(ProxyInterfaceTest, UncacheableResourcesNotCachedOnResourceFetch) {
24922492
false /* proxy_fetch_property_callback_collector_created */);
24932493
EXPECT_TRUE(out_headers.HasValue(HttpAttributes::kCacheControl, "private"));
24942494
EXPECT_EQ("a", out_text);
2495-
EXPECT_EQ(1, lru_cache()->num_hits()); // uncacheable memo
2495+
EXPECT_EQ(2, lru_cache()->num_hits()); // uncacheable memo & modified check
24962496
EXPECT_EQ(0, http_cache()->cache_hits()->Get());
24972497
EXPECT_EQ(0, lru_cache()->num_misses());
24982498
EXPECT_EQ(1, http_cache()->cache_misses()->Get()); // uncacheable memo
24992499
EXPECT_EQ(0, lru_cache()->num_inserts());
2500-
EXPECT_EQ(2, lru_cache()->num_identical_reinserts())
2501-
<< "uncacheable memo, metadata";
2500+
EXPECT_EQ(1, lru_cache()->num_identical_reinserts()) << "uncacheable memo";
25022501
EXPECT_EQ(1, http_cache()->cache_inserts()->Get()); // uncacheable memo
25032502

25042503
// Since the original response is not cached, we should pick up changes in the
@@ -2514,14 +2513,13 @@ TEST_F(ProxyInterfaceTest, UncacheableResourcesNotCachedOnResourceFetch) {
25142513
false /* proxy_fetch_property_callback_collector_created */);
25152514
EXPECT_TRUE(out_headers.HasValue(HttpAttributes::kCacheControl, "private"));
25162515
EXPECT_EQ("b", out_text);
2517-
EXPECT_EQ(1, lru_cache()->num_hits()); // uncacheable memo
2516+
EXPECT_EQ(2, lru_cache()->num_hits()); // uncacheable memo & modified-check
25182517
EXPECT_EQ(0, http_cache()->cache_hits()->Get());
25192518
EXPECT_EQ(0, lru_cache()->num_misses());
2520-
EXPECT_EQ(1, http_cache()->cache_misses()->Get()); // uncacheable memo
2519+
EXPECT_EQ(1, http_cache()->cache_misses()->Get()) << "uncacheable memo";
25212520
EXPECT_EQ(0, lru_cache()->num_inserts());
2522-
EXPECT_EQ(2, lru_cache()->num_identical_reinserts())
2523-
<< "uncacheable memo, metadata";
2524-
EXPECT_EQ(1, http_cache()->cache_inserts()->Get()); // uncacheable memo
2521+
EXPECT_EQ(1, lru_cache()->num_identical_reinserts()) << "uncacheable memo";
2522+
EXPECT_EQ(1, http_cache()->cache_inserts()->Get()) << "uncacheable memo";
25252523
}
25262524

25272525
// No matter what options->respect_vary() is set to we will respect HTML Vary

0 commit comments

Comments
 (0)