Repository: knative/serving
Pull request #8799 (2020-07-24 09:56:11)

Allow more than 1000 concurrent revisions when CC!=0
We need to set some sort of limit on the revision throttler because the breaker is backed by a chan struct{}, but it can and should be higher than 1000 concurrent requests.

Also added some more comments and renamed `breakerParams` to `revisionBreakerParams` since this was super-confusing to me, but this file could probably do with some more cleanup.

/assign @markusthoemmes @vagababov 

<!--
Request Prow to automatically lint any go code in this PR:

/lint
-->

knative-prow-robot: [APPROVALNOTIFIER] This PR is **APPROVED**

This pull-request has been approved by: *<a href="https://github.com/knative/serving/pull/8799#" title="Author self-approved">julz</a>*

The full list of commands accepted by this bot can be found [here](https://go.k8s.io/bot-commands?repo=knative%2Fserving).

The pull request process is described [here](https://git.k8s.io/community/contributors/guide/owners.md#the-code-review-process)

<details >
Needs approval from an approver in each of these files:

- ~~[pkg/activator/OWNERS](https://github.com/knative/serving/blob/master/pkg/activator/OWNERS)~~ [julz]

Approvers can indicate their approval by writing `/approve` in a comment
Approvers can cancel approval by writing `/approve cancel` in a comment
</details>
<!-- META={"approvers":[]} -->

Patch:
diff --git a/cmd/activator/main.go b/cmd/activator/main.go
index 81c4172a5..37ad183cb 100644
--- a/cmd/activator/main.go
+++ b/cmd/activator/main.go
@@ -68,9 +68,6 @@ import (
 const (
 	component = "activator"
 
-	// Add enough buffer to not block request serving on stats collection
-	requestCountingQueueLength = 100
-
 	// The port on which autoscaler WebSocket server listens.
 	autoscalerPort = ":8080"
 )
@@ -168,9 +165,6 @@ func main() {
 	statCh := make(chan []asmetrics.StatMessage)
 	defer close(statCh)
 
-	reqCh := make(chan network.ReqEvent, requestCountingQueueLength)
-	defer close(reqCh)
-
 	// Start throttler.
 	throttler := activatornet.NewThrottler(ctx, env.PodIP)
 	go throttler.Run(ctx)
@@ -198,13 +192,13 @@ func main() {
 	go statReporter(statSink, statCh, logger)
 
 	// Create and run our concurrency reporter
-	cr := activatorhandler.NewConcurrencyReporter(ctx, env.PodName, reqCh, statCh)
-	go cr.Run(ctx.Done())
+	concurrencyReporter := activatorhandler.NewConcurrencyReporter(ctx, env.PodName, statCh)
+	go concurrencyReporter.Run(ctx.Done())
 
 	// Create activation handler chain
 	// Note: innermost handlers are specified first, ie. the last handler in the chain will be executed first
 	var ah http.Handler = activatorhandler.New(ctx, throttler)
-	ah = activatorhandler.NewRequestEventHandler(reqCh, ah)
+	ah = concurrencyReporter.Handler(ah)
 	ah = tracing.HTTPSpanMiddleware(ah)
 	ah = configStore.HTTPMiddleware(ah)
 	reqLogHandler, err := pkghttp.NewRequestLogHandler(ah, logging.NewSyncFileWriter(os.Stdout), "",
diff --git a/cmd/queue/main.go b/cmd/queue/main.go
index 39215cc18..b91e18d64 100644
--- a/cmd/queue/main.go
+++ b/cmd/queue/main.go
@@ -329,7 +329,7 @@ func buildServer(env config, healthState *health.State, rp *readiness.Probe, sta
 	httpProxy.Transport = buildTransport(env, logger)
 	httpProxy.ErrorHandler = pkgnet.ErrorHandler(logger)
 	httpProxy.BufferPool = network.NewBufferPool()
-	httpProxy.FlushInterval = -1
+	httpProxy.FlushInterval = network.FlushInterval
 	activatorutil.SetupHeaderPruning(httpProxy)
 
 	breaker := buildBreaker(env)
diff --git a/go.mod b/go.mod
index 56ee6a681..e67cba532 100644
--- a/go.mod
+++ b/go.mod
@@ -38,10 +38,10 @@ require (
 	k8s.io/client-go v11.0.1-0.20190805182717-6502b5e7b1b5+incompatible
 	k8s.io/code-generator v0.18.0
 	k8s.io/kube-openapi v0.0.0-20200410145947-bcb3869e6f29
-	knative.dev/caching v0.0.0-20200723145958-d031ce9de55d
-	knative.dev/networking v0.0.0-20200723155758-cc457d7322d6
-	knative.dev/pkg v0.0.0-20200723172857-6167cd9c1f2b
-	knative.dev/test-infra v0.0.0-20200723141258-383af8175387
+	knative.dev/caching v0.0.0-20200724160858-bf86c4f25164
+	knative.dev/networking v0.0.0-20200724160858-a48770266a29
+	knative.dev/pkg v0.0.0-20200724211057-f21f66204a5c
+	knative.dev/test-infra v0.0.0-20200724213858-d5ec9cdc6b33
 )
 
 replace (
diff --git a/go.sum b/go.sum
index 9d6397b1b..b0df169cc 100644
--- a/go.sum
+++ b/go.sum
@@ -1387,8 +1387,6 @@ golang.org/x/tools v0.0.0-20200626171337-aa94e735be7f/go.mod h1:EkVYQZoAsY45+roY
 golang.org/x/tools v0.0.0-20200706234117-b22de6825cf7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
 golang.org/x/tools v0.0.0-20200709181711-e327e1019dfe/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
 golang.org/x/tools v0.0.0-20200713011307-fd294ab11aed/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
-golang.org/x/tools v0.0.0-20200721032237-77f530d86f9a h1:kVMPw4f6EVqYdfGQTedjrpw1dbE2PEMfw4jwXsNdn9s=
-golang.org/x/tools v0.0.0-20200721032237-77f530d86f9a/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
 golang.org/x/tools v0.0.0-20200721223218-6123e77877b2/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
 golang.org/x/tools v0.0.0-20200723000907-a7c6fd066f6d h1:7k9BKfwmdbykG6l5ztniTrH0TP25yel8O7l26/yovMU=
 golang.org/x/tools v0.0.0-20200723000907-a7c6fd066f6d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
@@ -1479,8 +1477,6 @@ google.golang.org/genproto v0.0.0-20200626011028-ee7919e894b5/go.mod h1:FWY/as6D
 google.golang.org/genproto v0.0.0-20200707001353-8e8330bf89df/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
 google.golang.org/genproto v0.0.0-20200709005830-7a2ca40e9dc3/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
 google.golang.org/genproto v0.0.0-20200711021454-869866162049/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
-google.golang.org/genproto v0.0.0-20200721032028-5044d0edf986 h1:10ohwcLf82I55O/aQxYqmWKoOdNbQTYYComeP1KDOS4=
-google.golang.org/genproto v0.0.0-20200721032028-5044d0edf986/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
 google.golang.org/genproto v0.0.0-20200722002428-88e341933a54 h1:ASrBgpl9XvkNTP0m39/j18mid7aoF21npu2ioIBxYnY=
 google.golang.org/genproto v0.0.0-20200722002428-88e341933a54/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
 google.golang.org/grpc v0.0.0-20160317175043-d3ddb4469d5a/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw=
@@ -1625,27 +1621,27 @@ k8s.io/legacy-cloud-providers v0.17.4/go.mod h1:FikRNoD64ECjkxO36gkDgJeiQWwyZTuB
 k8s.io/metrics v0.17.2/go.mod h1:3TkNHET4ROd+NfzNxkjoVfQ0Ob4iZnaHmSEA4vYpwLw=
 k8s.io/test-infra v0.0.0-20200514184223-ba32c8aae783/go.mod h1:bW6thaPZfL2hW7ecjx2WYwlP9KQLM47/xIJyttkVk5s=
 k8s.io/test-infra v0.0.0-20200617221206-ea73eaeab7ff/go.mod h1:L3+cRvwftUq8IW1TrHji5m3msnc4uck/7LsE/GR/aZk=
-k8s.io/test-infra v0.0.0-20200721115715-1af01ef6b4c8/go.mod h1:4cRZlOy5Ka3Ym/orCmNWL2dsE39pN0xHFT0WFrZe2HQ=
 k8s.io/test-infra v0.0.0-20200722010006-526277bee528/go.mod h1:4cRZlOy5Ka3Ym/orCmNWL2dsE39pN0xHFT0WFrZe2HQ=
 k8s.io/test-infra v0.0.0-20200723132140-b02d194a2d64/go.mod h1:4cRZlOy5Ka3Ym/orCmNWL2dsE39pN0xHFT0WFrZe2HQ=
 k8s.io/utils v0.0.0-20191114184206-e782cd3c129f/go.mod h1:sZAwmy6armz5eXlNoLmJcl4F1QuKu7sr+mFQ0byX7Ew=
 k8s.io/utils v0.0.0-20200124190032-861946025e34 h1:HjlUD6M0K3P8nRXmr2B9o4F9dUy9TCj/aEpReeyi6+k=
 k8s.io/utils v0.0.0-20200124190032-861946025e34/go.mod h1:sZAwmy6armz5eXlNoLmJcl4F1QuKu7sr+mFQ0byX7Ew=
 knative.dev/caching v0.0.0-20200116200605-67bca2c83dfa/go.mod h1:dHXFU6CGlLlbzaWc32g80cR92iuBSpsslDNBWI8C7eg=
-knative.dev/caching v0.0.0-20200723145958-d031ce9de55d h1:5/Qh8F7aL7W3svM++jxu1sgH5WCG8oBdys5DhrVM9qg=
-knative.dev/caching v0.0.0-20200723145958-d031ce9de55d/go.mod h1:z0tB8jJWOJj4SfbWGps++hjMQHSxB5XEiTyaRuyRtfE=
+knative.dev/caching v0.0.0-20200724160858-bf86c4f25164 h1:PryiLYxQp17UoxmUm7iUx4nLfoevrrmSX3lC+OghynI=
+knative.dev/caching v0.0.0-20200724160858-bf86c4f25164/go.mod h1:ynA34AdJ/8sxpUxUUcel/cVUesoIG80S3qyclqth7xQ=
 knative.dev/eventing-contrib v0.11.2/go.mod h1:SnXZgSGgMSMLNFTwTnpaOH7hXDzTFtw0J8OmHflNx3g=
-knative.dev/networking v0.0.0-20200723155758-cc457d7322d6 h1:E3icpGu2JG84UXvrG/36FovPwVmBWs1q6R6xZzcO6dM=
-knative.dev/networking v0.0.0-20200723155758-cc457d7322d6/go.mod h1:Xd6H9nzGwHI/tfEnvrm7dQjYP1s/CbZL8J941HyUaSE=
+knative.dev/networking v0.0.0-20200724160858-a48770266a29 h1:qBxok3mlcLsFPKWL/qLdIdBiRrIuNHtuTaHC4yeOloQ=
+knative.dev/networking v0.0.0-20200724160858-a48770266a29/go.mod h1:NYdbzk8QhBB3O6BtJaw76Wjelyw8hLZVqGxd1702gF8=
 knative.dev/pkg v0.0.0-20200207155214-fef852970f43/go.mod h1:pgODObA1dTyhNoFxPZTTjNWfx6F0aKsKzn+vaT9XO/Q=
-knative.dev/pkg v0.0.0-20200723060257-ae9c3f7fa8d3/go.mod h1:pOJ+tu5k/SgHPGmecWqFguxJcIZozyDWlTlyldGcYGM=
-knative.dev/pkg v0.0.0-20200723172857-6167cd9c1f2b h1:H3BgBC4oIKseilY6RaCl8f3H0JiG3LeR9L79NvbIbtw=
-knative.dev/pkg v0.0.0-20200723172857-6167cd9c1f2b/go.mod h1:GtoTEupsOzadgRKT4GgPWukbhAcINwDDGcKibTyd1Fk=
-knative.dev/test-infra v0.0.0-20200721175154-c98db9bd4d5d h1:GFzUmKNa8SVL3uMwa3R+NOjaSY1oUzxNdNWW3j5HrZ4=
-knative.dev/test-infra v0.0.0-20200721175154-c98db9bd4d5d/go.mod h1:kzRhTm5L08eDQFRl8NKSAN93lz6IZWQMs+2TjTCN+VA=
+knative.dev/pkg v0.0.0-20200724055557-c36f46cc8c80 h1:rA0vidKv+Xr4hVsFV4KSenD0Ytalwlbs83RPuvTIBnI=
+knative.dev/pkg v0.0.0-20200724055557-c36f46cc8c80/go.mod h1:GtoTEupsOzadgRKT4GgPWukbhAcINwDDGcKibTyd1Fk=
+knative.dev/pkg v0.0.0-20200724211057-f21f66204a5c h1:jJs9rQhkCexFr9m6+sYIPq/0DtRe+typQB64ffbXydM=
+knative.dev/pkg v0.0.0-20200724211057-f21f66204a5c/go.mod h1:jVP9M8uft3EYhWF8K00zKiNCvyfGy3tm0L2w9edCRqw=
 knative.dev/test-infra v0.0.0-20200722142057-3ca910b5a25e/go.mod h1:oHmDsPmq+zcc3b+Z94Kgmrz1JnmZEz36jmKuvL2Lw7o=
-knative.dev/test-infra v0.0.0-20200723141258-383af8175387 h1:9asfovmD7n5kydyrUTV9XWbyu2LKfPCR1w9b3vycnck=
-knative.dev/test-infra v0.0.0-20200723141258-383af8175387/go.mod h1:Y3sNbLSTA11kRcLvTzRsZCxO+GQuw7KV7+PL+iD+9CA=
+knative.dev/test-infra v0.0.0-20200723182457-517b66ba19c1 h1:auvu/bG10qo06+InTTHMa0BO0P++Pe937kjRi8zGFps=
+knative.dev/test-infra v0.0.0-20200723182457-517b66ba19c1/go.mod h1:Y3sNbLSTA11kRcLvTzRsZCxO+GQuw7KV7+PL+iD+9CA=
+knative.dev/test-infra v0.0.0-20200724213858-d5ec9cdc6b33 h1:IhDg9AfaH9xgPv2KS0Lso5zLBXf9NSD9G+NdEznaYDI=
+knative.dev/test-infra v0.0.0-20200724213858-d5ec9cdc6b33/go.mod h1:Y3sNbLSTA11kRcLvTzRsZCxO+GQuw7KV7+PL+iD+9CA=
 modernc.org/cc v1.0.0/go.mod h1:1Sk4//wdnYJiUIxnW8ddKpaOJCF37yAdqYnkxUpaYxw=
 modernc.org/golex v1.0.0/go.mod h1:b/QX9oBD/LhixY6NDh+IdGv17hgB+51fET1i2kPSmvk=
 modernc.org/mathutil v1.0.0/go.mod h1:wU0vUrJsVWBZ4P6e7xtFJEhFSNsfRLJ8H458uRjg03k=
diff --git a/pkg/activator/handler/concurrency_reporter.go b/pkg/activator/handler/concurrency_reporter.go
index ccad0640b..3621d52db 100644
--- a/pkg/activator/handler/concurrency_reporter.go
+++ b/pkg/activator/handler/concurrency_reporter.go
@@ -19,6 +19,7 @@ package handler
 import (
 	"context"
 	"math"
+	"net/http"
 	"sync"
 	"time"
 
@@ -29,6 +30,7 @@ import (
 	"knative.dev/pkg/logging"
 	pkgmetrics "knative.dev/pkg/metrics"
 	"knative.dev/serving/pkg/activator"
+	"knative.dev/serving/pkg/activator/util"
 	"knative.dev/serving/pkg/apis/serving"
 	asmetrics "knative.dev/serving/pkg/autoscaler/metrics"
 	revisioninformer "knative.dev/serving/pkg/client/injection/informers/serving/v1/revision"
@@ -44,8 +46,6 @@ type ConcurrencyReporter struct {
 	logger  *zap.SugaredLogger
 	podName string
 
-	// Ticks with every request arrived/completed respectively
-	reqCh chan network.ReqEvent
 	// Stat reporting channel
 	statCh chan []asmetrics.StatMessage
 
@@ -65,12 +65,10 @@ type ConcurrencyReporter struct {
 
 // NewConcurrencyReporter creates a ConcurrencyReporter which listens to incoming
 // ReqEvents on reqCh and ticks on reportCh and reports stats on statCh.
-func NewConcurrencyReporter(ctx context.Context, podName string,
-	reqCh chan network.ReqEvent, statCh chan []asmetrics.StatMessage) *ConcurrencyReporter {
+func NewConcurrencyReporter(ctx context.Context, podName string, statCh chan []asmetrics.StatMessage) *ConcurrencyReporter {
 	return &ConcurrencyReporter{
 		logger:  logging.FromContext(ctx),
 		podName: podName,
-		reqCh:   reqCh,
 		statCh:  statCh,
 		rl:      revisioninformer.Get(ctx).Lister(),
 
@@ -154,13 +152,11 @@ func (cr *ConcurrencyReporter) report(now time.Time) []asmetrics.StatMessage {
 		messages = append(messages, asmetrics.StatMessage{
 			Key: key,
 			Stat: asmetrics.Stat{
-				// Stat time is unset by design. The receiver will set the time.
 				PodName:                   cr.podName,
 				AverageConcurrentRequests: adjustedConcurrency,
 				RequestCount:              adjustedCount,
 			},
 		})
-		cr.reportToMetricsBackend(key, report.AverageConcurrency)
 	}
 	cr.reportedFirstRequest = make(map[types.NamespacedName]float64)
 
@@ -192,10 +188,11 @@ func (cr *ConcurrencyReporter) Run(stopCh <-chan struct{}) {
 func (cr *ConcurrencyReporter) run(stopCh <-chan struct{}, reportCh <-chan time.Time) {
 	for {
 		select {
-		case event := <-cr.reqCh:
-			cr.handleEvent(event)
 		case now := <-reportCh:
 			msgs := cr.report(now)
+			for _, msg := range msgs {
+				cr.reportToMetricsBackend(msg.Key, msg.Stat.AverageConcurrentRequests)
+			}
 			if len(msgs) > 0 {
 				cr.statCh <- msgs
 			}
@@ -204,3 +201,17 @@ func (cr *ConcurrencyReporter) run(stopCh <-chan struct{}, reportCh <-chan time.
 		}
 	}
 }
+
+// Handler returns a handler that records requests coming in/being finished in the stats
+// machinery.
+func (cr *ConcurrencyReporter) Handler(next http.Handler) http.HandlerFunc {
+	return func(w http.ResponseWriter, r *http.Request) {
+		revisionKey := util.RevIDFrom(r.Context())
+		cr.handleEvent(network.ReqEvent{Key: revisionKey, Type: network.ReqIn, Time: time.Now()})
+		defer func() {
+			cr.handleEvent(network.ReqEvent{Key: revisionKey, Type: network.ReqOut, Time: time.Now()})
+		}()
+
+		next.ServeHTTP(w, r)
+	}
+}
diff --git a/pkg/activator/handler/handler.go b/pkg/activator/handler/handler.go
index 9d789a024..819e5091f 100644
--- a/pkg/activator/handler/handler.go
+++ b/pkg/activator/handler/handler.go
@@ -53,7 +53,9 @@ type activationHandler struct {
 
 // New constructs a new http.Handler that deals with revision activation.
 func New(ctx context.Context, t Throttler) http.Handler {
-	defaultTransport := pkgnet.AutoTransport
+	// TODO: run loadtests to determine the optimal values here.
+	defaultTransport := pkgnet.NewAutoTransport(1000, /*maxidleconnes*/
+		100 /*maxidleperhos*/)
 	return &activationHandler{
 		transport: defaultTransport,
 		tracingTransport: &ochttp.Transport{
@@ -115,7 +117,7 @@ func (a *activationHandler) proxyRequest(logger *zap.SugaredLogger, w http.Respo
 	if tracingEnabled {
 		proxy.Transport = a.tracingTransport
 	}
-	proxy.FlushInterval = -1
+	proxy.FlushInterval = network.FlushInterval
 	proxy.ErrorHandler = pkgnet.ErrorHandler(logger)
 	util.SetupHeaderPruning(proxy)
 
diff --git a/pkg/activator/handler/requestevent_handler.go b/pkg/activator/handler/requestevent_handler.go
deleted file mode 100644
index ab29a2f00..000000000
--- a/pkg/activator/handler/requestevent_handler.go
+++ /dev/null
@@ -1,52 +0,0 @@
-/*
-Copyright 2018 The Knative Authors
-
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
-    http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
-*/
-
-package handler
-
-import (
-	"net/http"
-	"time"
-
-	"knative.dev/serving/pkg/activator/util"
-	"knative.dev/serving/pkg/network"
-)
-
-// NewRequestEventHandler creates a handler that sends events
-// about incoming/closed http connections to the given channel.
-func NewRequestEventHandler(reqChan chan network.ReqEvent, next http.Handler) *RequestEventHandler {
-	handler := &RequestEventHandler{
-		nextHandler: next,
-		reqChan:     reqChan,
-	}
-
-	return handler
-}
-
-// RequestEventHandler sends events to the given channel.
-type RequestEventHandler struct {
-	nextHandler http.Handler
-	reqChan     chan network.ReqEvent
-}
-
-func (h *RequestEventHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
-	revisionKey := util.RevIDFrom(r.Context())
-
-	h.reqChan <- network.ReqEvent{Key: revisionKey, Type: network.ReqIn, Time: time.Now()}
-	defer func() {
-		h.reqChan <- network.ReqEvent{Key: revisionKey, Type: network.ReqOut, Time: time.Now()}
-	}()
-	h.nextHandler.ServeHTTP(w, r)
-}
diff --git a/pkg/activator/net/throttler.go b/pkg/activator/net/throttler.go
index 12577e1ee..373597af5 100644
--- a/pkg/activator/net/throttler.go
+++ b/pkg/activator/net/throttler.go
@@ -49,18 +49,23 @@ import (
 )
 
 const (
-
 	// The number of requests that are queued on the breaker before the 503s are sent.
 	// The value must be adjusted depending on the actual production requirements.
 	breakerQueueDepth = 10000
 
 	// The upper bound for concurrent requests sent to the revision.
-	// As new endpoints show up, the Breakers concurrency increases up to this value.
-	breakerMaxConcurrency = 1000
+	// As new endpoints show up, the revisionThrottler's concurrency increases up
+	// to this value.  We need to set some value here since the breaker requires
+	// an explicit buffer size (it's backed by a chan struct{}), but
+	// queue.MaxBreakerCapacity is very large.
+	breakerMaxConcurrency = queue.MaxBreakerCapacity
 )
 
 var (
-	breakerParams = queue.BreakerParams{
+	// These params are used to build the breaker that throttles across the entire revision.
+	// This is used when CC!=0 because we need to queue requests in case no
+	// individual podTracker is free.
+	revisionBreakerParams = queue.BreakerParams{
 		QueueDepth:      breakerQueueDepth,
 		MaxConcurrency:  breakerMaxConcurrency,
 		InitialCapacity: 0,
@@ -293,11 +298,11 @@ func (rt *revisionThrottler) updateCapacity(backendCount int) {
 	if numTrackers > 0 {
 		// Capacity is computed based off of number of trackers,
 		// when using pod direct routing.
-		capacity = rt.calculateCapacity(len(rt.podTrackers), ac, breakerParams.MaxConcurrency)
+		capacity = rt.calculateCapacity(len(rt.podTrackers), ac, revisionBreakerParams.MaxConcurrency)
 	} else {
 		// Capacity is computed off of number of ready backends,
 		// when we are using clusterIP routing.
-		capacity = rt.calculateCapacity(backendCount, ac, breakerParams.MaxConcurrency)
+		capacity = rt.calculateCapacity(backendCount, ac, revisionBreakerParams.MaxConcurrency)
 	}
 	rt.logger.Infof("Set capacity to %d (backends: %d, index: %d/%d)",
 		capacity, backendCount, ai, ac)
@@ -416,7 +421,7 @@ func (rt *revisionThrottler) handleUpdate(throttler *Throttler, update revisionD
 					tracker = &podTracker{
 						dest: newDest,
 						b: queue.NewBreaker(queue.BreakerParams{
-							QueueDepth:      breakerParams.QueueDepth,
+							QueueDepth:      revisionBreakerParams.QueueDepth,
 							MaxConcurrency:  rt.containerConcurrency,
 							InitialCapacity: rt.containerConcurrency, // Presume full unused capacity.
 						}),
@@ -533,7 +538,7 @@ func (t *Throttler) getOrCreateRevisionThrottler(revID types.NamespacedName) (*r
 			return nil, err
 		}
 		revThrottler = newRevisionThrottler(revID, int(rev.Spec.GetContainerConcurrency()),
-			networking.ServicePortName(rev.GetProtocol()), breakerParams, t.logger)
+			networking.ServicePortName(rev.GetProtocol()), revisionBreakerParams, t.logger)
 		t.revisionThrottlers[revID] = revThrottler
 	}
 	return revThrottler, nil
diff --git a/pkg/apis/serving/metadata_validation.go b/pkg/apis/serving/metadata_validation.go
index a4eef1b33..0af0e595a 100644
--- a/pkg/apis/serving/metadata_validation.go
+++ b/pkg/apis/serving/metadata_validation.go
@@ -40,6 +40,7 @@ var (
 		RoutingStateModifiedAnnotationKey,
 		GroupNamePrefix+"forceUpgrade",
 		RevisionPreservedAnnotationKey,
+		RoutesAnnotationKey,
 	)
 )
 
diff --git a/pkg/apis/serving/register.go b/pkg/apis/serving/register.go
index 48120c817..94eb21cef 100644
--- a/pkg/apis/serving/register.go
+++ b/pkg/apis/serving/register.go
@@ -47,6 +47,10 @@ const (
 	// triggered their creation.
 	RouteLabelKey = GroupName + "/route"
 
+	// RoutesAnnotationKey is an annotation attached to a Revision to indicate that it is
+	// referenced by one or many routes. The value is a comma separated list of Route names.
+	RoutesAnnotationKey = GroupName + "/routes"
+
 	// RoutingStateLabelKey is the label attached to a Revision indicating
 	// its state in relation to serving a Route.
 	RoutingStateLabelKey = GroupName + "/routingState"
diff --git a/pkg/apis/serving/v1/revision_helpers.go b/pkg/apis/serving/v1/revision_helpers.go
index a87d8614e..990380670 100644
--- a/pkg/apis/serving/v1/revision_helpers.go
+++ b/pkg/apis/serving/v1/revision_helpers.go
@@ -152,7 +152,8 @@ func (r *Revision) GetRoutingStateModified() time.Time {
 
 // IsReachable returns whether or not the revision can be reached by a route.
 func (r *Revision) IsReachable() bool {
-	return r.ObjectMeta.Labels[serving.RouteLabelKey] != ""
+	return r.Labels[serving.RouteLabelKey] != "" ||
+		RoutingState(r.Labels[serving.RoutingStateLabelKey]) == RoutingStateActive
 }
 
 // GetProtocol returns the app level network protocol.
diff --git a/pkg/apis/serving/v1alpha1/revision_lifecycle.go b/pkg/apis/serving/v1alpha1/revision_lifecycle.go
index 811a81193..654c02e76 100644
--- a/pkg/apis/serving/v1alpha1/revision_lifecycle.go
+++ b/pkg/apis/serving/v1alpha1/revision_lifecycle.go
@@ -320,11 +320,6 @@ func (r *Revision) GetLastPinned() (time.Time, error) {
 	return time.Unix(secs, 0), nil
 }
 
-// IsReachable returns whether or not the revision can be reached by a route.
-func (r *Revision) IsReachable() bool {
-	return r.ObjectMeta.Labels[serving.RouteLabelKey] != ""
-}
-
 // PropagateDeploymentStatus takes the Deployment status and applies its values
 // to the Revision status.
 func (rs *RevisionStatus) PropagateDeploymentStatus(original *appsv1.DeploymentStatus) {
diff --git a/pkg/network/network.go b/pkg/network/network.go
index 818b77d5d..bd0424116 100644
--- a/pkg/network/network.go
+++ b/pkg/network/network.go
@@ -26,6 +26,7 @@ import (
 	"net/url"
 	"strings"
 	"text/template"
+	"time"
 
 	lru "github.com/hashicorp/golang-lru"
 	corev1 "k8s.io/api/core/v1"
@@ -157,6 +158,13 @@ const (
 
 	// ProtoAcceptContent is the content type to be used when autoscaler scrapes metrics from the QP
 	ProtoAcceptContent = "application/protobuf"
+
+	// FlushInterval controls the time when we flush the connection in the
+	// reverse proxies (Activator, QP).
+	// NB: having it equal to 0 is a problem for streaming requests
+	// since the data won't be transferred in chunks less than 4kb, if the
+	// reverse proxy fails to detect streaming (gRPC, e.g.).
+	FlushInterval = 20 * time.Millisecond
 )
 
 // DomainTemplateValues are the available properties people can choose from
diff --git a/pkg/queue/breaker.go b/pkg/queue/breaker.go
index d1b55f50a..e96b9d7ef 100644
--- a/pkg/queue/breaker.go
+++ b/pkg/queue/breaker.go
@@ -20,6 +20,7 @@ import (
 	"context"
 	"errors"
 	"fmt"
+	"math"
 	"sync"
 	"sync/atomic"
 )
@@ -33,6 +34,10 @@ var (
 	ErrRequestQueueFull = errors.New("pending request queue full")
 )
 
+// MaxBreakerCapacity is the largest valid value for the MaxConcurrency value of BreakerParams.
+// This is limited by the maximum size of a chan struct{} in the current implementation.
+const MaxBreakerCapacity = math.MaxInt32
+
 // BreakerParams defines the parameters of the breaker.
 type BreakerParams struct {
 	QueueDepth      int
diff --git a/pkg/reconciler/revision/resources/deploy.go b/pkg/reconciler/revision/resources/deploy.go
index a62b3673b..fc7df2186 100644
--- a/pkg/reconciler/revision/resources/deploy.go
+++ b/pkg/reconciler/revision/resources/deploy.go
@@ -27,7 +27,6 @@ import (
 	"knative.dev/pkg/ptr"
 	tracingconfig "knative.dev/pkg/tracing/config"
 	"knative.dev/serving/pkg/apis/autoscaling"
-	"knative.dev/serving/pkg/apis/serving"
 	v1 "knative.dev/serving/pkg/apis/serving/v1"
 	asconfig "knative.dev/serving/pkg/autoscaler/config"
 	"knative.dev/serving/pkg/deployment"
@@ -221,10 +220,6 @@ func MakeDeployment(rev *v1.Revision,
 	observabilityConfig *metrics.ObservabilityConfig, deploymentConfig *deployment.Config,
 	autoscalerConfig *asconfig.Config) (*appsv1.Deployment, error) {
 
-	podTemplateAnnotations := kmeta.FilterMap(rev.GetAnnotations(), func(k string) bool {
-		return k == serving.RevisionLastPinnedAnnotationKey
-	})
-
 	podSpec, err := makePodSpec(rev, loggingConfig, tracingConfig, observabilityConfig, deploymentConfig)
 	if err != nil {
 		return nil, fmt.Errorf("failed to create PodSpec: %w", err)
@@ -237,15 +232,15 @@ func MakeDeployment(rev *v1.Revision,
 		replicaCount, _ = strconv.Atoi(ann)
 	}
 
+	labels := makeLabels(rev)
+	anns := makeAnnotations(rev)
+
 	return &appsv1.Deployment{
 		ObjectMeta: metav1.ObjectMeta{
-			Name:      names.Deployment(rev),
-			Namespace: rev.Namespace,
-			Labels:    makeLabels(rev),
-			Annotations: kmeta.FilterMap(rev.GetAnnotations(), func(k string) bool {
-				// Exclude the heartbeat label, which can have high variance.
-				return k == serving.RevisionLastPinnedAnnotationKey
-			}),
+			Name:            names.Deployment(rev),
+			Namespace:       rev.Namespace,
+			Labels:          labels,
+			Annotations:     anns,
 			OwnerReferences: []metav1.OwnerReference{*kmeta.NewControllerRef(rev)},
 		},
 		Spec: appsv1.DeploymentSpec{
@@ -254,8 +249,8 @@ func MakeDeployment(rev *v1.Revision,
 			ProgressDeadlineSeconds: ptr.Int32(int32(deploymentConfig.ProgressDeadline.Seconds())),
 			Template: corev1.PodTemplateSpec{
 				ObjectMeta: metav1.ObjectMeta{
-					Labels:      makeLabels(rev),
-					Annotations: podTemplateAnnotations,
+					Labels:      labels,
+					Annotations: anns,
 				},
 				Spec: *podSpec,
 			},
diff --git a/pkg/reconciler/revision/resources/imagecache.go b/pkg/reconciler/revision/resources/imagecache.go
index 1355705f3..7d871016e 100644
--- a/pkg/reconciler/revision/resources/imagecache.go
+++ b/pkg/reconciler/revision/resources/imagecache.go
@@ -21,7 +21,6 @@ import (
 
 	caching "knative.dev/caching/pkg/apis/caching/v1alpha1"
 	"knative.dev/pkg/kmeta"
-	"knative.dev/serving/pkg/apis/serving"
 	v1 "knative.dev/serving/pkg/apis/serving/v1"
 	"knative.dev/serving/pkg/reconciler/revision/resources/names"
 )
@@ -30,13 +29,10 @@ import (
 func MakeImageCache(rev *v1.Revision, containerName, image string) *caching.Image {
 	img := &caching.Image{
 		ObjectMeta: metav1.ObjectMeta{
-			Name:      kmeta.ChildName(names.ImageCache(rev), "-"+containerName),
-			Namespace: rev.Namespace,
-			Labels:    makeLabels(rev),
-			Annotations: kmeta.FilterMap(rev.GetAnnotations(), func(k string) bool {
-				// Ignore last pinned annotation.
-				return k == serving.RevisionLastPinnedAnnotationKey
-			}),
+			Name:            kmeta.ChildName(names.ImageCache(rev), "-"+containerName),
+			Namespace:       rev.Namespace,
+			Labels:          makeLabels(rev),
+			Annotations:     makeAnnotations(rev),
 			OwnerReferences: []metav1.OwnerReference{*kmeta.NewControllerRef(rev)},
 		},
 		Spec: caching.ImageSpec{
diff --git a/pkg/reconciler/revision/resources/meta.go b/pkg/reconciler/revision/resources/meta.go
index a6ab4c3f5..fcc6183fc 100644
--- a/pkg/reconciler/revision/resources/meta.go
+++ b/pkg/reconciler/revision/resources/meta.go
@@ -18,17 +18,30 @@ package resources
 
 import (
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+	"k8s.io/apimachinery/pkg/util/sets"
 	"knative.dev/pkg/kmeta"
 	"knative.dev/serving/pkg/apis/serving"
 	v1 "knative.dev/serving/pkg/apis/serving/v1"
 )
 
+var (
+	excludeLabels = sets.NewString(
+		serving.RouteLabelKey,
+		serving.RoutingStateLabelKey,
+	)
+
+	excludeAnnotations = sets.NewString(
+		serving.RevisionLastPinnedAnnotationKey,
+		serving.RevisionPreservedAnnotationKey,
+		serving.RoutingStateModifiedAnnotationKey,
+		serving.RoutesAnnotationKey,
+	)
+)
+
 // makeLabels constructs the labels we will apply to K8s resources.
 func makeLabels(revision *v1.Revision) map[string]string {
 	labels := kmeta.FilterMap(revision.GetLabels(), func(k string) bool {
-		// Exclude the Route label so that a Revision becoming routable
-		// doesn't trigger deployment updates.
-		return k == serving.RouteLabelKey
+		return excludeLabels.Has(k)
 	})
 	labels = kmeta.UnionMaps(labels, map[string]string{
 		serving.RevisionLabelKey: revision.Name,
@@ -44,6 +57,12 @@ func makeLabels(revision *v1.Revision) map[string]string {
 	return labels
 }
 
+func makeAnnotations(revision *v1.Revision) map[string]string {
+	return kmeta.FilterMap(revision.GetAnnotations(), func(k string) bool {
+		return excludeAnnotations.Has(k)
+	})
+}
+
 // makeSelector constructs the Selector we will apply to K8s resources.
 func makeSelector(revision *v1.Revision) *metav1.LabelSelector {
 	return &metav1.LabelSelector{
diff --git a/pkg/reconciler/revision/resources/pa.go b/pkg/reconciler/revision/resources/pa.go
index 76ebf5261..58fc98672 100644
--- a/pkg/reconciler/revision/resources/pa.go
+++ b/pkg/reconciler/revision/resources/pa.go
@@ -22,7 +22,6 @@ import (
 
 	"knative.dev/pkg/kmeta"
 	av1alpha1 "knative.dev/serving/pkg/apis/autoscaling/v1alpha1"
-	"knative.dev/serving/pkg/apis/serving"
 	v1 "knative.dev/serving/pkg/apis/serving/v1"
 	"knative.dev/serving/pkg/reconciler/revision/resources/names"
 )
@@ -31,13 +30,10 @@ import (
 func MakePA(rev *v1.Revision) *av1alpha1.PodAutoscaler {
 	return &av1alpha1.PodAutoscaler{
 		ObjectMeta: metav1.ObjectMeta{
-			Name:      names.PA(rev),
-			Namespace: rev.Namespace,
-			Labels:    makeLabels(rev),
-			Annotations: kmeta.FilterMap(rev.GetAnnotations(), func(k string) bool {
-				// Ignore last pinned annotation.
-				return k == serving.RevisionLastPinnedAnnotationKey
-			}),
+			Name:            names.PA(rev),
+			Namespace:       rev.Namespace,
+			Labels:          makeLabels(rev),
+			Annotations:     makeAnnotations(rev),
 			OwnerReferences: []metav1.OwnerReference{*kmeta.NewControllerRef(rev)},
 		},
 		Spec: av1alpha1.PodAutoscalerSpec{
diff --git a/pkg/reconciler/route/resources/service.go b/pkg/reconciler/route/resources/service.go
index c287f83d9..0c0cfdead 100644
--- a/pkg/reconciler/route/resources/service.go
+++ b/pkg/reconciler/route/resources/service.go
@@ -112,7 +112,8 @@ func makeK8sService(ctx context.Context, route *v1.Route, targetName string) (*c
 				// This service is owned by the Route.
 				*kmeta.NewControllerRef(route),
 			},
-			Labels: svcLabels,
+			Labels:      kmeta.UnionMaps(route.GetLabels(), svcLabels),
+			Annotations: route.GetAnnotations(),
 		},
 	}, nil
 }
diff --git a/vendor/knative.dev/pkg/network/transports.go b/vendor/knative.dev/pkg/network/transports.go
index c50276259..a692c5f5b 100644
--- a/vendor/knative.dev/pkg/network/transports.go
+++ b/vendor/knative.dev/pkg/network/transports.go
@@ -92,7 +92,7 @@ func dialBackOffHelper(ctx context.Context, network, address string, bo wait.Bac
 	return nil, fmt.Errorf("timed out dialing after %.2fs", elapsed.Seconds())
 }
 
-func newHTTPTransport(disableKeepAlives bool) http.RoundTripper {
+func newHTTPTransport(disableKeepAlives bool, maxIdle, maxIdlePerHost int) http.RoundTripper {
 	return &http.Transport{
 		// Those match net/http/transport.go
 		Proxy:                 http.ProxyFromEnvironment,
@@ -103,8 +103,8 @@ func newHTTPTransport(disableKeepAlives bool) http.RoundTripper {
 
 		// Those are bespoke.
 		DialContext:         DialWithBackOff,
-		MaxIdleConns:        1000,
-		MaxIdleConnsPerHost: 100,
+		MaxIdleConns:        maxIdle,
+		MaxIdleConnsPerHost: maxIdlePerHost,
 	}
 }
 
@@ -112,17 +112,17 @@ func newHTTPTransport(disableKeepAlives bool) http.RoundTripper {
 // since it will not cache connections.
 func NewProberTransport() http.RoundTripper {
 	return newAutoTransport(
-		newHTTPTransport(true /*disable keep-alives*/),
+		newHTTPTransport(true /*disable keep-alives*/, 0, 0 /*no caching*/),
 		NewH2CTransport())
 }
 
 // NewAutoTransport creates a RoundTripper that can use appropriate transport
 // based on the request's HTTP version.
-func NewAutoTransport() http.RoundTripper {
+func NewAutoTransport(maxIdle, maxIdlePerHost int) http.RoundTripper {
 	return newAutoTransport(
-		newHTTPTransport(false /*disable keep-alives*/),
+		newHTTPTransport(false /*disable keep-alives*/, maxIdle, maxIdlePerHost),
 		NewH2CTransport())
 }
 
 // AutoTransport uses h2c for HTTP2 requests and falls back to `http.DefaultTransport` for all others
-var AutoTransport = NewAutoTransport()
+var AutoTransport = NewAutoTransport(1000, 100)
diff --git a/vendor/modules.txt b/vendor/modules.txt
index 6df0ed18d..d30070a62 100644
--- a/vendor/modules.txt
+++ b/vendor/modules.txt
@@ -1004,7 +1004,7 @@ k8s.io/utils/buffer
 k8s.io/utils/integer
 k8s.io/utils/pointer
 k8s.io/utils/trace
-# knative.dev/caching v0.0.0-20200723145958-d031ce9de55d
+# knative.dev/caching v0.0.0-20200724160858-bf86c4f25164
 ## explicit
 knative.dev/caching/config
 knative.dev/caching/pkg/apis/caching
@@ -1025,7 +1025,7 @@ knative.dev/caching/pkg/client/injection/informers/caching/v1alpha1/image/fake
 knative.dev/caching/pkg/client/injection/informers/factory
 knative.dev/caching/pkg/client/injection/informers/factory/fake
 knative.dev/caching/pkg/client/listers/caching/v1alpha1
-# knative.dev/networking v0.0.0-20200723155758-cc457d7322d6
+# knative.dev/networking v0.0.0-20200724160858-a48770266a29
 ## explicit
 knative.dev/networking/pkg/apis/config
 knative.dev/networking/pkg/apis/networking
@@ -1052,7 +1052,7 @@ knative.dev/networking/pkg/client/injection/informers/networking/v1alpha1/server
 knative.dev/networking/pkg/client/injection/reconciler/networking/v1alpha1/serverlessservice
 knative.dev/networking/pkg/client/istio/listers/networking/v1alpha3
 knative.dev/networking/pkg/client/listers/networking/v1alpha1
-# knative.dev/pkg v0.0.0-20200723172857-6167cd9c1f2b
+# knative.dev/pkg v0.0.0-20200724211057-f21f66204a5c
 ## explicit
 knative.dev/pkg/apiextensions/storageversion
 knative.dev/pkg/apiextensions/storageversion/cmd/migrate
@@ -1176,7 +1176,7 @@ knative.dev/pkg/webhook/resourcesemantics/conversion
 knative.dev/pkg/webhook/resourcesemantics/defaulting
 knative.dev/pkg/webhook/resourcesemantics/validation
 knative.dev/pkg/websocket
-# knative.dev/test-infra v0.0.0-20200723141258-383af8175387
+# knative.dev/test-infra v0.0.0-20200724213858-d5ec9cdc6b33
 ## explicit
 knative.dev/test-infra/scripts
 # sigs.k8s.io/yaml v1.2.0


Test patch:
diff --git a/pkg/activator/handler/concurrency_reporter_test.go b/pkg/activator/handler/concurrency_reporter_test.go
index e78f47692..662183191 100644
--- a/pkg/activator/handler/concurrency_reporter_test.go
+++ b/pkg/activator/handler/concurrency_reporter_test.go
@@ -18,6 +18,9 @@ package handler
 
 import (
 	"context"
+	"fmt"
+	"net/http"
+	"net/http/httptest"
 	"sort"
 	"strconv"
 	"testing"
@@ -29,6 +32,7 @@ import (
 	"knative.dev/pkg/metrics/metricstest"
 	_ "knative.dev/pkg/metrics/testing"
 	rtesting "knative.dev/pkg/reconciler/testing"
+	"knative.dev/serving/pkg/activator/util"
 	v1 "knative.dev/serving/pkg/apis/serving/v1"
 	"knative.dev/serving/pkg/autoscaler/metrics"
 	asmetrics "knative.dev/serving/pkg/autoscaler/metrics"
@@ -309,23 +313,22 @@ func TestStats(t *testing.T) {
 
 	for _, tc := range tt {
 		t.Run(tc.name, func(t *testing.T) {
-			s, cr, ctx, cancel := newTestStats(t)
+			cr, _, cancel := newTestReporter(t)
 			defer cancel()
-			go func() {
-				cr.run(ctx.Done(), s.reportBiChan)
-				close(s.reportBiChan)
-			}()
 
 			// Apply request operations
 			for _, op := range tc.ops {
 				time := time.Time{}.Add(time.Duration(op.time) * time.Millisecond)
 				switch op.op {
 				case requestOpStart:
-					s.reqChan <- network.ReqEvent{Key: op.key, Type: network.ReqIn, Time: time}
+					cr.handleEvent(network.ReqEvent{Key: op.key, Type: network.ReqIn, Time: time})
 				case requestOpEnd:
-					s.reqChan <- network.ReqEvent{Key: op.key, Type: network.ReqOut, Time: time}
+					cr.handleEvent(network.ReqEvent{Key: op.key, Type: network.ReqOut, Time: time})
 				case requestOpTick:
-					s.reportBiChan <- time
+					stats := cr.report(time)
+					if len(stats) > 0 {
+						cr.statCh <- stats
+					}
 				}
 			}
 
@@ -333,7 +336,7 @@ func TestStats(t *testing.T) {
 			stats := make([]metrics.StatMessage, 0, len(tc.expectedStats))
 			for len(stats) < len(tc.expectedStats) {
 				select {
-				case x := <-s.statChan:
+				case x := <-cr.statCh:
 					stats = append(stats, x...)
 				case <-time.After(time.Second):
 					t.Fatal("Timed out waiting for the event")
@@ -342,7 +345,7 @@ func TestStats(t *testing.T) {
 
 			// Verify we're not getting extra events.
 			select {
-			case x := <-s.statChan:
+			case x := <-cr.statCh:
 				t.Fatal("Extra events received:", x)
 			case <-time.After(5 * time.Millisecond):
 				// Lookin' good.
@@ -366,22 +369,116 @@ func TestStats(t *testing.T) {
 	}
 }
 
+func TestConcurrencyReporterRun(t *testing.T) {
+	cr, ctx, cancel := newTestReporter(t)
+	defer cancel()
+
+	reportCh := make(chan time.Time)
+	go func() {
+		cr.run(ctx.Done(), reportCh)
+		close(reportCh)
+	}()
+
+	now := time.Time{}
+	cr.handleEvent(network.ReqEvent{Key: rev1, Type: network.ReqIn, Time: now})
+	cr.handleEvent(network.ReqEvent{Key: rev1, Type: network.ReqIn, Time: now})
+	cr.handleEvent(network.ReqEvent{Key: rev1, Type: network.ReqIn, Time: now})
+
+	reportCh <- now.Add(1)
+
+	want := []metrics.StatMessage{{
+		Key: rev1,
+		Stat: metrics.Stat{
+			AverageConcurrentRequests: 1,
+			RequestCount:              1,
+			PodName:                   activatorPodName,
+		},
+	}, {
+		Key: rev1,
+		Stat: metrics.Stat{
+			AverageConcurrentRequests: 2, // Discounted via the from 0 stat.
+			RequestCount:              2, // Discounted via the from 0 stat.
+			PodName:                   activatorPodName,
+		},
+	}}
+
+	reportCh <- time.Now()
+
+	got := make([]metrics.StatMessage, 0, len(want))
+	got = append(got, <-cr.statCh...) // Scale from 0.
+	got = append(got, <-cr.statCh...) // Actual report.
+	if !cmp.Equal(got, want) {
+		t.Errorf("Unexpected stats (-want +got): %s", cmp.Diff(want, got))
+	}
+}
+
+func TestConcurrencyReporterHandler(t *testing.T) {
+	cr, ctx, cancel := newTestReporter(t)
+	defer cancel()
+
+	reportCh := make(chan time.Time)
+	go func() {
+		cr.run(ctx.Done(), reportCh)
+		close(reportCh)
+	}()
+
+	baseHandler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {})
+	handler := cr.Handler(baseHandler)
+
+	resp := httptest.NewRecorder()
+	req := httptest.NewRequest(http.MethodPost, "http://example.com", nil)
+	rCtx := util.WithRevID(context.Background(), rev1)
+
+	// Send a few requests.
+	handler.ServeHTTP(resp, req.WithContext(rCtx))
+	handler.ServeHTTP(resp, req.WithContext(rCtx))
+	handler.ServeHTTP(resp, req.WithContext(rCtx))
+
+	want := []metrics.StatMessage{{
+		Key: rev1,
+		Stat: metrics.Stat{
+			AverageConcurrentRequests: 1,
+			RequestCount:              1,
+			PodName:                   activatorPodName,
+		},
+	}, {
+		Key: rev1,
+		Stat: metrics.Stat{
+			AverageConcurrentRequests: 0, // Discounted via the from 0 stat.
+			RequestCount:              2, // Discounted via the from 0 stat.
+			PodName:                   activatorPodName,
+		},
+	}}
+
+	reportCh <- time.Now()
+
+	got := make([]metrics.StatMessage, 0, len(want))
+	got = append(got, <-cr.statCh...) // Scale from 0.
+	got = append(got, <-cr.statCh...) // Actual report.
+	if !cmp.Equal(got, want) {
+		t.Errorf("Unexpected stats (-want +got): %s", cmp.Diff(want, got))
+	}
+}
+
 func TestMetricsReported(t *testing.T) {
 	reset()
-	s, cr, ctx, cancel := newTestStats(t)
+	cr, ctx, cancel := newTestReporter(t)
 	defer cancel()
+
+	reportCh := make(chan time.Time)
 	go func() {
-		cr.run(ctx.Done(), s.reportBiChan)
-		close(s.reportBiChan)
+		cr.run(ctx.Done(), reportCh)
+		close(reportCh)
 	}()
 
-	s.reqChan <- network.ReqEvent{Key: rev1, Type: network.ReqIn}
-	s.reqChan <- network.ReqEvent{Key: rev1, Type: network.ReqIn}
-	s.reqChan <- network.ReqEvent{Key: rev1, Type: network.ReqIn}
-	s.reqChan <- network.ReqEvent{Key: rev1, Type: network.ReqIn}
-	s.reportBiChan <- time.Time{}.Add(1 * time.Millisecond)
-	<-s.statChan // The scale from 0 quick-report
-	<-s.statChan // The actual report we want to see
+	cr.handleEvent(network.ReqEvent{Key: rev1, Type: network.ReqIn})
+	cr.handleEvent(network.ReqEvent{Key: rev1, Type: network.ReqIn})
+	cr.handleEvent(network.ReqEvent{Key: rev1, Type: network.ReqIn})
+	cr.handleEvent(network.ReqEvent{Key: rev1, Type: network.ReqIn})
+
+	reportCh <- time.Now()
+	<-cr.statCh
+	<-cr.statCh
 
 	wantTags := map[string]string{
 		metricskey.LabelRevisionName:      rev1.Name,
@@ -391,31 +488,23 @@ func TestMetricsReported(t *testing.T) {
 		metricskey.PodName:                "the-best-activator",
 		metricskey.ContainerName:          "activator",
 	}
-	metricstest.CheckLastValueData(t, "request_concurrency", wantTags, 4)
-}
+	metricstest.CheckLastValueData(t, "request_concurrency", wantTags, 3)
 
-// Test type to hold the bi-directional time channels
-type testStats struct {
-	reqChan      chan network.ReqEvent
-	statChan     chan []metrics.StatMessage
-	reportBiChan chan time.Time
+	reportCh <- time.Now()
+	<-cr.statCh
+
+	metricstest.CheckLastValueData(t, "request_concurrency", wantTags, 4)
 }
 
-func newTestStats(t *testing.T) (*testStats, *ConcurrencyReporter, context.Context, context.CancelFunc) {
-	reportBiChan := make(chan time.Time)
-	ts := &testStats{
-		reqChan: make(chan network.ReqEvent),
-		// Buffered channel permits avoiding sending the test commands on the separate go routine
-		// simplifying main test process.
-		statChan:     make(chan []metrics.StatMessage, 10),
-		reportBiChan: reportBiChan,
-	}
+func newTestReporter(t *testing.T) (*ConcurrencyReporter, context.Context, context.CancelFunc) {
 	ctx, cancel, _ := rtesting.SetupFakeContextWithCancel(t)
 	revisionInformer(ctx, revision(rev1.Namespace, rev1.Name),
 		revision(rev2.Namespace, rev2.Name), revision(rev3.Namespace, rev3.Name))
 
-	return ts, NewConcurrencyReporter(ctx, activatorPodName,
-		ts.reqChan, ts.statChan), ctx, cancel
+	// Buffered channel permits avoiding sending the test commands on the separate go routine
+	// simplifying main test process.
+	statCh := make(chan []metrics.StatMessage, 10)
+	return NewConcurrencyReporter(ctx, activatorPodName, statCh), ctx, cancel
 }
 
 func revisionInformer(ctx context.Context, revs ...*v1.Revision) {
@@ -433,9 +522,8 @@ func BenchmarkConcurrencyReporter(b *testing.B) {
 	defer cancel()
 
 	// Buffer equal to the activator.
-	reqCh := make(chan network.ReqEvent, 100)
 	statCh := make(chan []asmetrics.StatMessage)
-	cr := NewConcurrencyReporter(ctx, activatorPodName, reqCh, statCh)
+	cr := NewConcurrencyReporter(ctx, activatorPodName, statCh)
 
 	stopCh := make(chan struct{})
 	defer close(stopCh)
@@ -452,29 +540,25 @@ func BenchmarkConcurrencyReporter(b *testing.B) {
 		}
 	}()
 
+	fake := fakeservingclient.Get(ctx)
+	revisions := fakerevisioninformer.Get(ctx)
+
 	// Spread the load across 100 revisions.
 	keys := make([]types.NamespacedName, 0, 100)
 	for i := 0; i < cap(keys); i++ {
-		keys = append(keys, types.NamespacedName{
+		key := types.NamespacedName{
 			Namespace: testNamespace,
 			Name:      testRevName + strconv.Itoa(i),
-		})
-	}
-
-	requestChannel := func(key types.NamespacedName) {
-		reqCh <- network.ReqEvent{
-			Time: time.Now(),
-			Type: network.ReqIn,
-			Key:  key,
-		}
-		reqCh <- network.ReqEvent{
-			Time: time.Now(),
-			Type: network.ReqOut,
-			Key:  key,
 		}
+		keys = append(keys, key)
+
+		// Create revisions in the fake clients to trigger report logic.
+		rev := revision(key.Namespace, key.Name)
+		fake.ServingV1().Revisions(rev.Namespace).Create(rev)
+		revisions.Informer().GetIndexer().Add(rev)
 	}
 
-	requestDirect := func(key types.NamespacedName) {
+	request := func(key types.NamespacedName) {
 		cr.handleEvent(network.ReqEvent{
 			Time: time.Now(),
 			Type: network.ReqIn,
@@ -487,39 +571,71 @@ func BenchmarkConcurrencyReporter(b *testing.B) {
 		})
 	}
 
-	b.Run("sequential-direct", func(b *testing.B) {
+	b.Run("sequential", func(b *testing.B) {
 		for j := 0; j < b.N; j++ {
 			key := keys[j%len(keys)]
-			requestDirect(key)
+			request(key)
 		}
 	})
 
-	b.Run("sequential-channel", func(b *testing.B) {
-		for j := 0; j < b.N; j++ {
-			key := keys[j%len(keys)]
-			requestChannel(key)
-		}
-	})
-
-	b.Run("parallel-direct", func(b *testing.B) {
+	b.Run("parallel", func(b *testing.B) {
 		b.RunParallel(func(pb *testing.PB) {
 			var j int
 			for pb.Next() {
 				key := keys[j%len(keys)]
-				requestDirect(key)
+				request(key)
 				j++
 			}
 		})
 	})
+}
 
-	b.Run("parallel-channel", func(b *testing.B) {
-		b.RunParallel(func(pb *testing.PB) {
-			var j int
-			for pb.Next() {
-				key := keys[j%len(keys)]
-				requestChannel(key)
-				j++
+// BenchmarkConcurrencyReporterReport benchmarks the report function specifically
+// to get a feeling for how expensive it is as it'll lock the mutex and thus block
+// requests for the respective time too.
+func BenchmarkConcurrencyReporterReport(b *testing.B) {
+	for _, revs := range []int{1, 5, 10, 50, 100, 200} {
+		b.Run(fmt.Sprintf("revs-%d", revs), func(b *testing.B) {
+			ctx, cancel, _ := rtesting.SetupFakeContextWithCancel(b)
+			defer cancel()
+
+			// Buffer equal to the activator.
+			statCh := make(chan []asmetrics.StatMessage)
+			cr := NewConcurrencyReporter(ctx, activatorPodName, statCh)
+
+			// Just read and ignore all stat messages.
+			go func() {
+				for {
+					select {
+					case <-statCh:
+					}
+				}
+			}()
+
+			fake := fakeservingclient.Get(ctx)
+			revisions := fakerevisioninformer.Get(ctx)
+			for i := 0; i < revs; i++ {
+				key := types.NamespacedName{
+					Namespace: testNamespace,
+					Name:      testRevName + strconv.Itoa(i),
+				}
+
+				// Create revisions in the fake clients to trigger report logic.
+				rev := revision(key.Namespace, key.Name)
+				fake.ServingV1().Revisions(rev.Namespace).Create(rev)
+				revisions.Informer().GetIndexer().Add(rev)
+
+				// Send a dummy request for each revision to make sure it's reported.
+				cr.handleEvent(network.ReqEvent{
+					Time: time.Now(),
+					Type: network.ReqIn,
+					Key:  key,
+				})
+			}
+
+			for j := 0; j < b.N; j++ {
+				cr.report(time.Now())
 			}
 		})
-	})
+	}
 }
diff --git a/pkg/activator/handler/requestevent_handler_test.go b/pkg/activator/handler/requestevent_handler_test.go
deleted file mode 100644
index 59e431340..000000000
--- a/pkg/activator/handler/requestevent_handler_test.go
+++ /dev/null
@@ -1,71 +0,0 @@
-/*
-Copyright 2018 The Knative Authors
-
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
-    http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
-*/
-package handler
-
-import (
-	"bytes"
-	"context"
-	"net/http"
-	"net/http/httptest"
-	"testing"
-	"time"
-
-	"k8s.io/apimachinery/pkg/types"
-	"knative.dev/serving/pkg/activator/util"
-	"knative.dev/serving/pkg/network"
-)
-
-func TestRequestEventHandler(t *testing.T) {
-	namespace := "testspace"
-	revision := "testrevision"
-	timeOffset := 1 * time.Millisecond
-
-	baseHandler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
-		w.WriteHeader(http.StatusOK)
-		time.Sleep(timeOffset)
-	})
-
-	reqChan := make(chan network.ReqEvent, 2)
-	handler := NewRequestEventHandler(reqChan, baseHandler)
-
-	resp := httptest.NewRecorder()
-	req := httptest.NewRequest(http.MethodPost, "http://example.com", bytes.NewBufferString(""))
-	key := types.NamespacedName{Namespace: namespace, Name: revision}
-	ctx := util.WithRevID(context.Background(), key)
-	handler.ServeHTTP(resp, req.WithContext(ctx))
-
-	in := <-handler.reqChan
-	if in.Key != key {
-		t.Errorf("in.Key = %v, want %v", in.Key, key)
-	}
-	if in.Type != network.ReqIn {
-		t.Errorf("in.Type = %v, want %v", in.Type, network.ReqIn)
-	}
-	if in.Time.IsZero() {
-		t.Error("in.Time = Time{}, want something real")
-	}
-
-	out := <-handler.reqChan
-	if out.Key != key {
-		t.Errorf("out.Key = %v, want %v", in.Key, key)
-	}
-	if out.Type != network.ReqOut {
-		t.Errorf("in.Type = %v, want %v", in.Type, network.ReqIn)
-	}
-	if out.Time.Sub(in.Time) < timeOffset {
-		t.Errorf("out.Time.Sub(in.Time) = %v, want > %v", out.Time.Sub(in.Time), timeOffset)
-	}
-}
diff --git a/pkg/activator/net/throttler_test.go b/pkg/activator/net/throttler_test.go
index 189f98368..2ce90c41a 100644
--- a/pkg/activator/net/throttler_test.go
+++ b/pkg/activator/net/throttler_test.go
@@ -50,11 +50,9 @@ import (
 	"knative.dev/serving/pkg/queue"
 )
 
-const defaultMaxConcurrency = 1000
-
 var defaultParams = queue.BreakerParams{
 	QueueDepth:      1,
-	MaxConcurrency:  defaultMaxConcurrency,
+	MaxConcurrency:  breakerMaxConcurrency,
 	InitialCapacity: 0,
 }
 
@@ -65,12 +63,12 @@ type tryResult struct {
 
 func newTestThrottler(ctx context.Context) (*Throttler, func()) {
 	// Use different values for the test.
-	bp := breakerParams
-	breakerParams = defaultParams
+	bp := revisionBreakerParams
+	revisionBreakerParams = defaultParams
 
 	throttler := NewThrottler(ctx, "10.10.10.10")
 	return throttler, func() {
-		breakerParams = bp
+		revisionBreakerParams = bp
 	}
 }
 
@@ -82,6 +80,13 @@ func TestThrottlerUpdateCapacity(t *testing.T) {
 		containerConcurrency: 10,
 	}
 
+	// temporarily lower MaxConcurrency since the default value is math.MaxInt32
+	// and we want to test calling updateCapacity() below with the same value
+	// as MaxConcurrency. This will be very, very slow if MaxConcurrency is very large.
+	bmp := revisionBreakerParams.MaxConcurrency
+	revisionBreakerParams.MaxConcurrency = 1000
+	t.Cleanup(func() { revisionBreakerParams.MaxConcurrency = bmp })
+
 	rt.updateCapacity(1)
 	if got, want := rt.breaker.Capacity(), 10; got != want {
 		t.Errorf("Capacity = %d, want: %d", got, want)
@@ -90,8 +95,8 @@ func TestThrottlerUpdateCapacity(t *testing.T) {
 	if got, want := rt.breaker.Capacity(), 100; got != want {
 		t.Errorf("Capacity = %d, want: %d", got, want)
 	}
-	rt.updateCapacity(defaultMaxConcurrency) // So in theory should be 10x.
-	if got, want := rt.breaker.Capacity(), defaultMaxConcurrency; got != want {
+	rt.updateCapacity(revisionBreakerParams.MaxConcurrency) // So in theory should be 10x.
+	if got, want := rt.breaker.Capacity(), revisionBreakerParams.MaxConcurrency; got != want {
 		t.Errorf("Capacity = %d, want: %d", got, want)
 	}
 	rt.numActivators = 10
@@ -111,16 +116,16 @@ func TestThrottlerUpdateCapacity(t *testing.T) {
 
 	rt.containerConcurrency = 0
 	rt.updateCapacity(1)
-	if got, want := rt.breaker.Capacity(), defaultMaxConcurrency; got != want {
+	if got, want := rt.breaker.Capacity(), revisionBreakerParams.MaxConcurrency; got != want {
 		t.Errorf("Capacity = %d, want: %d", got, want)
 	}
 	rt.updateCapacity(10)
-	if got, want := rt.breaker.Capacity(), defaultMaxConcurrency; got != want {
+	if got, want := rt.breaker.Capacity(), revisionBreakerParams.MaxConcurrency; got != want {
 		t.Errorf("Capacity = %d, want: %d", got, want)
 	}
 	rt.numActivators = 200
 	rt.updateCapacity(1)
-	if got, want := rt.breaker.Capacity(), defaultMaxConcurrency; got != want {
+	if got, want := rt.breaker.Capacity(), revisionBreakerParams.MaxConcurrency; got != want {
 		t.Errorf("Capacity = %d, want: %d", got, want)
 	}
 	rt.updateCapacity(0)
@@ -172,7 +177,7 @@ func TestThrottlerUpdateCapacity(t *testing.T) {
 	rt.containerConcurrency = 0
 	rt.podTrackers = makeTrackers(3, 0)
 	rt.updateCapacity(1)
-	if got, want := rt.breaker.Capacity(), defaultMaxConcurrency; got != want {
+	if got, want := rt.breaker.Capacity(), revisionBreakerParams.MaxConcurrency; got != want {
 		t.Errorf("Capacity = %d, want: %d", got, want)
 	}
 	if got, want := len(rt.assignedTrackers), len(rt.podTrackers); got != want {
diff --git a/pkg/apis/serving/v1alpha1/revision_lifecycle_test.go b/pkg/apis/serving/v1alpha1/revision_lifecycle_test.go
index 657f28568..40b1d9892 100644
--- a/pkg/apis/serving/v1alpha1/revision_lifecycle_test.go
+++ b/pkg/apis/serving/v1alpha1/revision_lifecycle_test.go
@@ -537,38 +537,6 @@ func TestRevisionGetLastPinned(t *testing.T) {
 	}
 }
 
-func TestRevisionIsReachable(t *testing.T) {
-	tests := []struct {
-		name   string
-		labels map[string]string
-		want   bool
-	}{{
-		name:   "has route annotation",
-		labels: map[string]string{serving.RouteLabelKey: "the-route"},
-		want:   true,
-	}, {
-		name:   "empty route annotation",
-		labels: map[string]string{serving.RouteLabelKey: ""},
-		want:   false,
-	}, {
-		name:   "no route annotation",
-		labels: nil,
-		want:   false,
-	}}
-
-	for _, tt := range tests {
-		t.Run(tt.name, func(t *testing.T) {
-			rev := Revision{ObjectMeta: metav1.ObjectMeta{Labels: tt.labels}}
-
-			got := rev.IsReachable()
-
-			if got != tt.want {
-				t.Errorf("got: %t, want: %t", got, tt.want)
-			}
-		})
-	}
-}
-
 func TestPropagateDeploymentStatus(t *testing.T) {
 	rev := &RevisionStatus{}
 	rev.InitializeConditions()
diff --git a/pkg/reconciler/revision/resources/meta_test.go b/pkg/reconciler/revision/resources/meta_test.go
index 289d7f8f2..7d793fff3 100644
--- a/pkg/reconciler/revision/resources/meta_test.go
+++ b/pkg/reconciler/revision/resources/meta_test.go
@@ -102,3 +102,42 @@ func TestMakeLabels(t *testing.T) {
 		})
 	}
 }
+
+func TestMakeAnnotations(t *testing.T) {
+	tests := []struct {
+		name string
+		rev  *v1.Revision
+		want map[string]string
+	}{{
+		name: "no user annotations",
+		rev: &v1.Revision{
+			ObjectMeta: metav1.ObjectMeta{
+				Namespace: "foo",
+				Name:      "bar",
+			},
+		},
+		want: map[string]string{},
+	}, {
+		name: "exclude annotation",
+		rev: &v1.Revision{
+			ObjectMeta: metav1.ObjectMeta{
+				Namespace: "foo",
+				Name:      "bar",
+				Annotations: map[string]string{
+					serving.RoutingStateModifiedAnnotationKey: "exlcude me",
+					"keep": "keep me",
+				},
+			},
+		},
+		want: map[string]string{"keep": "keep me"},
+	}}
+
+	for _, test := range tests {
+		t.Run(test.name, func(t *testing.T) {
+			got := makeAnnotations(test.rev)
+			if diff := cmp.Diff(test.want, got); diff != "" {
+				t.Errorf("makeLabels (-want, +got) = %v", diff)
+			}
+		})
+	}
+}
diff --git a/pkg/reconciler/route/resources/service_test.go b/pkg/reconciler/route/resources/service_test.go
index 1adbb4f65..383edd4f7 100644
--- a/pkg/reconciler/route/resources/service_test.go
+++ b/pkg/reconciler/route/resources/service_test.go
@@ -252,6 +252,7 @@ func TestMakeK8sPlaceholderService(t *testing.T) {
 		name           string
 		expectedSpec   corev1.ServiceSpec
 		expectedLabels map[string]string
+		expectedAnnos  map[string]string
 		wantErr        bool
 		route          *v1.Route
 	}{{
@@ -266,6 +267,23 @@ func TestMakeK8sPlaceholderService(t *testing.T) {
 			serving.RouteLabelKey: "test-route",
 		},
 		wantErr: false,
+	}, {
+		name: "labels and annotations are propagated from the route",
+		route: Route("test-ns", "test-route",
+			WithRouteLabel(map[string]string{"route-label": "foo"}), WithRouteAnnotation(map[string]string{"route-anno": "bar"})),
+		expectedSpec: corev1.ServiceSpec{
+			Type:            corev1.ServiceTypeExternalName,
+			ExternalName:    "foo-test-route.test-ns.example.com",
+			SessionAffinity: corev1.ServiceAffinityNone,
+		},
+		expectedLabels: map[string]string{
+			serving.RouteLabelKey: "test-route",
+			"route-label":         "foo",
+		},
+		expectedAnnos: map[string]string{
+			"route-anno": "bar",
+		},
+		wantErr: false,
 	}, {
 		name:  "cluster local route",
 		route: Route("test-ns", "test-route", WithRouteLabel(map[string]string{serving.VisibilityLabelKey: serving.VisibilityClusterLocal})),
@@ -275,7 +293,8 @@ func TestMakeK8sPlaceholderService(t *testing.T) {
 			SessionAffinity: corev1.ServiceAffinityNone,
 		},
 		expectedLabels: map[string]string{
-			serving.RouteLabelKey: "test-route",
+			serving.RouteLabelKey:      "test-route",
+			serving.VisibilityLabelKey: serving.VisibilityClusterLocal,
 		},
 	}}
 	for _, tt := range tests {
@@ -301,6 +320,9 @@ func TestMakeK8sPlaceholderService(t *testing.T) {
 			if !cmp.Equal(tt.expectedLabels, got.ObjectMeta.Labels) {
 				t.Errorf("Unexpected Labels (-want +got): %s", cmp.Diff(tt.expectedLabels, got.ObjectMeta.Labels))
 			}
+			if !cmp.Equal(tt.expectedAnnos, got.ObjectMeta.Annotations) {
+				t.Errorf("Unexpected Annotations (-want +got): %s", cmp.Diff(tt.expectedAnnos, got.ObjectMeta.Annotations))
+			}
 			if !cmp.Equal(tt.expectedSpec, got.Spec) {
 				t.Errorf("Unexpected ServiceSpec (-want +got): %s", cmp.Diff(tt.expectedSpec, got.Spec))
 			}
diff --git a/third_party/contour-latest/contour.yaml b/third_party/contour-latest/contour.yaml
index dc3c0f31b..d7773ec62 100644
--- a/third_party/contour-latest/contour.yaml
+++ b/third_party/contour-latest/contour.yaml
@@ -1432,7 +1432,7 @@ spec:
         # if we change this on each version, you can no longer upgrade
         # just by applying the deployment YAML.
         # See #2423, #2395, #2150, and #2030 for earlier questions about this.
-        image: gcr.io/knative-nightly/github.com/projectcontour/contour/cmd/contour@sha256:c521d5a4e564f2b1749a0c1ff642cab5b1682bff06354dd12bd02a6789d5cee7
+        image: gcr.io/knative-nightly/github.com/projectcontour/contour/cmd/contour@sha256:5ab6172979119c55b43de192853b0dd9df7de56f7e939c6f2d0b831b17b3c48f
         imagePullPolicy: Always
         command:
         - contour
@@ -1672,7 +1672,7 @@ spec:
         - --contour-key-file=/certs/tls.key
         - --config-path=/config/contour.yaml
         command: ["contour"]
-        image: gcr.io/knative-nightly/github.com/projectcontour/contour/cmd/contour@sha256:c521d5a4e564f2b1749a0c1ff642cab5b1682bff06354dd12bd02a6789d5cee7
+        image: gcr.io/knative-nightly/github.com/projectcontour/contour/cmd/contour@sha256:5ab6172979119c55b43de192853b0dd9df7de56f7e939c6f2d0b831b17b3c48f
         imagePullPolicy: Always
         name: contour
         ports:
@@ -1764,7 +1764,7 @@ spec:
         args:
         - envoy
         - shutdown-manager
-        image: gcr.io/knative-nightly/github.com/projectcontour/contour/cmd/contour@sha256:c521d5a4e564f2b1749a0c1ff642cab5b1682bff06354dd12bd02a6789d5cee7
+        image: gcr.io/knative-nightly/github.com/projectcontour/contour/cmd/contour@sha256:5ab6172979119c55b43de192853b0dd9df7de56f7e939c6f2d0b831b17b3c48f
         imagePullPolicy: Always
         lifecycle:
           preStop:
@@ -1840,7 +1840,7 @@ spec:
         - --envoy-key-file=/certs/tls.key
         command:
         - contour
-        image: gcr.io/knative-nightly/github.com/projectcontour/contour/cmd/contour@sha256:c521d5a4e564f2b1749a0c1ff642cab5b1682bff06354dd12bd02a6789d5cee7
+        image: gcr.io/knative-nightly/github.com/projectcontour/contour/cmd/contour@sha256:5ab6172979119c55b43de192853b0dd9df7de56f7e939c6f2d0b831b17b3c48f
         imagePullPolicy: Always
         name: envoy-initconfig
         volumeMounts:
@@ -3305,7 +3305,7 @@ spec:
         # if we change this on each version, you can no longer upgrade
         # just by applying the deployment YAML.
         # See #2423, #2395, #2150, and #2030 for earlier questions about this.
-        image: gcr.io/knative-nightly/github.com/projectcontour/contour/cmd/contour@sha256:c521d5a4e564f2b1749a0c1ff642cab5b1682bff06354dd12bd02a6789d5cee7
+        image: gcr.io/knative-nightly/github.com/projectcontour/contour/cmd/contour@sha256:5ab6172979119c55b43de192853b0dd9df7de56f7e939c6f2d0b831b17b3c48f
         imagePullPolicy: Always
         command:
         - contour
@@ -3545,7 +3545,7 @@ spec:
         - --contour-key-file=/certs/tls.key
         - --config-path=/config/contour.yaml
         command: ["contour"]
-        image: gcr.io/knative-nightly/github.com/projectcontour/contour/cmd/contour@sha256:c521d5a4e564f2b1749a0c1ff642cab5b1682bff06354dd12bd02a6789d5cee7
+        image: gcr.io/knative-nightly/github.com/projectcontour/contour/cmd/contour@sha256:5ab6172979119c55b43de192853b0dd9df7de56f7e939c6f2d0b831b17b3c48f
         imagePullPolicy: Always
         name: contour
         ports:
@@ -3637,7 +3637,7 @@ spec:
         args:
         - envoy
         - shutdown-manager
-        image: gcr.io/knative-nightly/github.com/projectcontour/contour/cmd/contour@sha256:c521d5a4e564f2b1749a0c1ff642cab5b1682bff06354dd12bd02a6789d5cee7
+        image: gcr.io/knative-nightly/github.com/projectcontour/contour/cmd/contour@sha256:5ab6172979119c55b43de192853b0dd9df7de56f7e939c6f2d0b831b17b3c48f
         imagePullPolicy: Always
         lifecycle:
           preStop:
@@ -3713,7 +3713,7 @@ spec:
         - --envoy-key-file=/certs/tls.key
         command:
         - contour
-        image: gcr.io/knative-nightly/github.com/projectcontour/contour/cmd/contour@sha256:c521d5a4e564f2b1749a0c1ff642cab5b1682bff06354dd12bd02a6789d5cee7
+        image: gcr.io/knative-nightly/github.com/projectcontour/contour/cmd/contour@sha256:5ab6172979119c55b43de192853b0dd9df7de56f7e939c6f2d0b831b17b3c48f
         imagePullPolicy: Always
         name: envoy-initconfig
         volumeMounts:
diff --git a/third_party/contour-latest/net-contour.yaml b/third_party/contour-latest/net-contour.yaml
index 0faaf082d..5f0c2c7b8 100644
--- a/third_party/contour-latest/net-contour.yaml
+++ b/third_party/contour-latest/net-contour.yaml
@@ -35,7 +35,7 @@ metadata:
   namespace: knative-serving
   labels:
     networking.knative.dev/ingress-provider: contour
-    serving.knative.dev/release: "v20200722-25763217"
+    serving.knative.dev/release: "v20200724-a051c55c"
 data:
   _example: |
     ################################
@@ -94,7 +94,7 @@ spec:
       - name: controller
         # This is the Go import path for the binary that is containerized
         # and substituted here.
-        image: gcr.io/knative-nightly/knative.dev/net-contour/cmd/controller@sha256:522db3dea868014143ad78f85c8134edd0991176107295701f9c564d91637e52
+        image: gcr.io/knative-nightly/knative.dev/net-contour/cmd/controller@sha256:6735fea99a7966d7830caa8b7c73052ce82efde86786cd9c9d0535a3d6dbaad8
         resources:
           requests:
             cpu: 40m
diff --git a/vendor/knative.dev/pkg/reconciler/testing/table.go b/vendor/knative.dev/pkg/reconciler/testing/table.go
index a8bb76199..6059fefec 100644
--- a/vendor/knative.dev/pkg/reconciler/testing/table.go
+++ b/vendor/knative.dev/pkg/reconciler/testing/table.go
@@ -98,6 +98,8 @@ type TableRow struct {
 	// testing framework. Instead it is used in the test method. E.g. setting up the responses for a
 	// mock client can go in here.
 	OtherTestData map[string]interface{}
+
+	CmpOpts []cmp.Option
 }
 
 func objKey(o runtime.Object) string {
@@ -175,7 +177,7 @@ func (r *TableRow) Test(t *testing.T, factory Factory) {
 			t.Errorf("Unexpected action[%d]: %#v", i, got)
 		}
 
-		if diff := cmp.Diff(want, obj, ignoreLastTransitionTime, safeDeployDiff, cmpopts.EquateEmpty()); diff != "" {
+		if diff := cmp.Diff(want, obj, append(r.CmpOpts, ignoreLastTransitionTime, safeDeployDiff, cmpopts.EquateEmpty())...); diff != "" {
 			t.Errorf("Unexpected create (-want, +got): %s", diff)
 		}
 	}
@@ -196,7 +198,7 @@ func (r *TableRow) Test(t *testing.T, factory Factory) {
 				continue
 			}
 			t.Errorf("Missing update for %s (-want, +prevState): %s", key,
-				cmp.Diff(wo, oldObj, ignoreLastTransitionTime, safeDeployDiff, cmpopts.EquateEmpty()))
+				cmp.Diff(wo, oldObj, append(r.CmpOpts, ignoreLastTransitionTime, safeDeployDiff, cmpopts.EquateEmpty())...))
 			continue
 		}
 
@@ -209,7 +211,7 @@ func (r *TableRow) Test(t *testing.T, factory Factory) {
 		// Update the object state.
 		objPrevState[objKey(got)] = got
 
-		if diff := cmp.Diff(want.GetObject(), got, ignoreLastTransitionTime, safeDeployDiff, cmpopts.EquateEmpty()); diff != "" {
+		if diff := cmp.Diff(want.GetObject(), got, append(r.CmpOpts, ignoreLastTransitionTime, safeDeployDiff, cmpopts.EquateEmpty())...); diff != "" {
 			t.Errorf("Unexpected update (-want, +got): %s", diff)
 		}
 	}
@@ -231,7 +233,7 @@ func (r *TableRow) Test(t *testing.T, factory Factory) {
 				continue
 			}
 			t.Errorf("Missing status update for %s (-want, +prevState): %s", key,
-				cmp.Diff(wo, oldObj, ignoreLastTransitionTime, safeDeployDiff, cmpopts.EquateEmpty()))
+				cmp.Diff(wo, oldObj, append(r.CmpOpts, ignoreLastTransitionTime, safeDeployDiff, cmpopts.EquateEmpty())...))
 			continue
 		}
 
@@ -240,7 +242,7 @@ func (r *TableRow) Test(t *testing.T, factory Factory) {
 		// Update the object state.
 		objPrevState[objKey(got)] = got
 
-		if diff := cmp.Diff(want.GetObject(), got, ignoreLastTransitionTime, safeDeployDiff, cmpopts.EquateEmpty()); diff != "" {
+		if diff := cmp.Diff(want.GetObject(), got, append(r.CmpOpts, ignoreLastTransitionTime, safeDeployDiff, cmpopts.EquateEmpty())...); diff != "" {
 			t.Errorf("Unexpected status update (-want, +got): %s\nFull: %v", diff, got)
 		}
 	}
@@ -254,7 +256,7 @@ func (r *TableRow) Test(t *testing.T, factory Factory) {
 				continue
 			}
 			t.Errorf("Extra status update for %s (-extra, +prevState): %s", key,
-				cmp.Diff(wo, oldObj, ignoreLastTransitionTime, safeDeployDiff, cmpopts.EquateEmpty()))
+				cmp.Diff(wo, oldObj, append(r.CmpOpts, ignoreLastTransitionTime, safeDeployDiff, cmpopts.EquateEmpty())...))
 		}
 	}
 
diff --git a/vendor/knative.dev/test-infra/scripts/presubmit-tests.sh b/vendor/knative.dev/test-infra/scripts/presubmit-tests.sh
index bd0402c51..a76f890c0 100644
--- a/vendor/knative.dev/test-infra/scripts/presubmit-tests.sh
+++ b/vendor/knative.dev/test-infra/scripts/presubmit-tests.sh
@@ -28,7 +28,7 @@ readonly PRESUBMIT_TEST_FAIL_FAST=${PRESUBMIT_TEST_FAIL_FAST:-0}
 readonly NO_PRESUBMIT_FILES=(\.png \.gitignore \.gitattributes ^OWNERS ^OWNERS_ALIASES ^AUTHORS)
 
 # Flag if this is a presubmit run or not.
-(( IS_PROW )) && [[ -n "${PULL_PULL_SHA}" ]] && IS_PRESUBMIT=1 || IS_PRESUBMIT=0
+(( IS_PROW )) && [[ ${JOB_TYPE} == "presubmit" ]] && IS_PRESUBMIT=1 || IS_PRESUBMIT=0
 readonly IS_PRESUBMIT
 
 # List of changed files on presubmit, LF separated.
@@ -109,7 +109,8 @@ function run_build_tests() {
 # Parameters: $1 - report name.
 #             $2... - command (test) to run.
 function report_build_test() {
-  local report="$(mktemp)"
+  local report
+  report="$(mktemp)"
   local report_name="$1"
   shift
   local errors=""
@@ -163,7 +164,8 @@ function default_build_test_runner() {
   [[ -z "${go_pkg_dirs}" ]] && return ${failed}
   # Ensure all the code builds
   subheader "Checking that go code builds"
-  local report="$(mktemp)"
+  local report
+  report="$(mktemp)"
   local errors_go1=""
   local errors_go2=""
   if ! capture_output "${report}" go build -v ./... ; then
@@ -172,10 +174,12 @@ function default_build_test_runner() {
     errors_go1="$(grep -v '^\(github\.com\|knative\.dev\)/' "${report}" | sort | uniq)"
   fi
   # Get all build tags in go code (ignore /vendor, /hack and /third_party)
-  local tags="$(grep -r '// +build' . \
+  local tags
+  tags="$(grep -r '// +build' . \
     | grep -v '^./vendor/' | grep -v '^./hack/' | grep -v '^./third_party' \
     | cut -f3 -d' ' | sort | uniq | tr '\n' ' ')"
-  local tagged_pkgs="$(grep -r '// +build' . \
+  local tagged_pkgs
+  tagged_pkgs="$(grep -r '// +build' . \
     | grep -v '^./vendor/' | grep -v '^./hack/' | grep -v '^./third_party' \
     | grep ":// +build " | cut -f1 -d: | xargs dirname \
     | sort | uniq | tr '\n' ' ')"
@@ -190,7 +194,8 @@ function default_build_test_runner() {
     rm -f e2e.test
   done
 
-  local errors_go="$(echo -e "${errors_go1}\n${errors_go2}" | uniq)"
+  local errors_go
+  errors_go="$(echo -e "${errors_go1}\n${errors_go2}" | uniq)"
   create_junit_xml _build_tests Build_Go "${errors_go}"
   # Check that we don't have any forbidden licenses in our images.
   subheader "Checking for forbidden licenses"
@@ -266,17 +271,16 @@ function run_integration_tests() {
 
 # Default integration test runner that runs all `test/e2e-*tests.sh`.
 function default_integration_test_runner() {
-  # options is always empty.
-  # TODO: remove it or indeed allow passing options.
-  local options=""
   local failed=0
-  for e2e_test in $(find test/ -name e2e-*tests.sh); do
+  find test/ ! -name "$(printf "*\n*")" -name "e2e-*tests.sh" > tmp
+  while IFS= read -r e2e_test
+  do
     echo "Running integration test ${e2e_test}"
-    if ! ${e2e_test} ${options}; then
+    if ! ${e2e_test}; then
       failed=1
-      step_failed "${e2e_test} ${options}"
+      step_failed "${e2e_test}"
     fi
-  done
+  done < tmp
   return ${failed}
 }
 
@@ -324,7 +328,7 @@ function main() {
     # (https://github.com/kubernetes/test-infra/blob/09bd4c6709dc64308406443f8996f90cf3b40ed1/jenkins/bootstrap.py#L588)
     # TODO(chaodaiG): follow up on https://github.com/kubernetes/test-infra/blob/0fabd2ea816daa8c15d410c77a0c93c0550b283f/prow/initupload/run.go#L49
     echo ">> node name"
-    echo "$(curl -H "Metadata-Flavor: Google" 'http://169.254.169.254/computeMetadata/v1/instance/name' 2> /dev/null)"
+    curl -H "Metadata-Flavor: Google" 'http://169.254.169.254/computeMetadata/v1/instance/name' 2> /dev/null
     echo ">> pod name"
     echo "${HOSTNAME}"
   fi
@@ -336,6 +340,15 @@ function main() {
   while [[ $# -ne 0 ]]; do
     local parameter=$1
     case ${parameter} in
+      --help|-h)
+        echo "Usage: ./presubmit-tests.sh [options...]"
+        echo "  --build-tests: run build tests."
+        echo "  --unit-tests: run unit tests."
+        echo "  --integration-tests: run integration tests, basically all the e2e-*tests.sh."
+        echo "  --all-tests: run build tests, unit tests and integration tests in sequence."
+        echo "  --run-test: run custom tests. Can be used to run multiple tests that need different args."
+        echo "              For example, ./presubmit-tests.sh --run-test \"e2e-tests1.sh arg1\" \"e2e-tests2.sh arg2\"."
+        ;;
       --build-tests) RUN_BUILD_TESTS=1 ;;
       --unit-tests) RUN_UNIT_TESTS=1 ;;
       --integration-tests) RUN_INTEGRATION_TESTS=1 ;;