Repository: knative/serving
Pull request #9019 (2020-08-11 15:18:28)

Reimplement semaphore using packaged integer.
**Note:** This needs **thorough** review. It's the 1000th iteration of this and I'm still not 100% certain it's correct :joy: 

<!--
Request Prow to automatically lint any go code in this PR:

/lint
-->

<!-- Please include the 'why' behind your changes if no issue exists -->
## Proposed Changes

This is an implementation of a semaphore based on packed integers and a channel.

state is an int64 that has two int32s packed into it: capacity and inFlight. The former specifies how many request are allowed at any given time into the semaphore while the latter refers to the currently in-flight requests. Packing them both into one int64 allows us to optimize access semantics using atomic operations, which can't be guaranteed on 2 individual values.

The channel is merely used as a vehicle to be able to "wake up" individual goroutines if capacity becomes free. It's not consistently used in accordance to actual capacity but is rather a communication vehicle to ensure waiting routines are properly worken up.

### Benchmarks

```
benchmark                                 old ns/op     new ns/op     delta
BenchmarkBreakerReserve/sequential-16     60.2          23.3          -61.30%
BenchmarkBreakerReserve/parallel-16       243           80.6          -66.83%

benchmark                                    old ns/op     new ns/op     delta
BenchmarkBreakerMaybe/1-sequential-16        92.1          27.5          -70.14%
BenchmarkBreakerMaybe/1-parallel-16          310           224           -27.74%
BenchmarkBreakerMaybe/10-sequential-16       92.1          28.0          -69.60%
BenchmarkBreakerMaybe/10-parallel-16         843           83.1          -90.14%
BenchmarkBreakerMaybe/100-sequential-16      91.6          28.0          -69.43%
BenchmarkBreakerMaybe/100-parallel-16        183           74.0          -59.56%
BenchmarkBreakerMaybe/1000-sequential-16     90.3          27.9          -69.10%
BenchmarkBreakerMaybe/1000-parallel-16       220           74.1          -66.32%
```

**Release Note**

<!-- Enter your extended release note in the below block. If the PR requires
additional action from users switching to the new release, include the string
"action required". If no release note is required, write "NONE". -->

```release-note
NONE
```

/assign @vagababov @julz 


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

This pull-request has been approved by: *<a href="https://github.com/knative/serving/pull/9019#" title="Author self-approved">markusthoemmes</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/queue/OWNERS](https://github.com/knative/serving/blob/master/pkg/queue/OWNERS)~~ [markusthoemmes]

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":[]} -->
knative-metrics-robot: The following is the coverage report on the affected files.
Say `/test pull-knative-serving-go-coverage` to re-run this coverage report

File | Old Coverage | New Coverage | Delta
---- |:------------:|:------------:|:-----:
[pkg/queue/breaker.go](https://storage.cloud.google.com/knative-prow/pr-logs/pull/knative_serving/9019/pull-knative-serving-go-coverage/1293205276932968453/artifacts/line-cov.html#file0) | 98.7% | 98.8% | 0.1

knative-prow-robot: @markusthoemmes: The following test **failed**, say `/retest` to rerun all failed tests:

Test name | Commit | Details | Rerun command
--- | --- | --- | ---
pull-knative-serving-unit-tests | bda31a4ee8805e8ef9be867cfc85a4b93b3ad0cf | [link](https://prow.knative.dev/view/gs/knative-prow/pr-logs/pull/knative_serving/9019/pull-knative-serving-unit-tests/1293205276932968449) | `/test pull-knative-serving-unit-tests`

[Full PR test history](https://gubernator.knative.dev/pr/knative_serving/9019). [Your PR dashboard](https://gubernator.knative.dev/pr/markusthoemmes).

<details>

Instructions for interacting with me using PR comments are available [here](https://git.k8s.io/community/contributors/guide/pull-requests.md).  If you have questions or suggestions related to my behavior, please file an issue against the [kubernetes/test-infra](https://github.com/kubernetes/test-infra/issues/new?title=Prow%20issue:) repository. I understand the commands that are listed [here](https://go.k8s.io/bot-commands).
</details>
<!-- test report -->
knative-prow-robot: @markusthoemmes: The following tests **failed**, say `/retest` to rerun all failed tests:

Test name | Commit | Details | Rerun command
--- | --- | --- | ---
pull-knative-serving-unit-tests | bda31a4ee8805e8ef9be867cfc85a4b93b3ad0cf | [link](https://prow.knative.dev/view/gs/knative-prow/pr-logs/pull/knative_serving/9019/pull-knative-serving-unit-tests/1293205276932968449) | `/test pull-knative-serving-unit-tests`
pull-knative-serving-autotls-tests | bda31a4ee8805e8ef9be867cfc85a4b93b3ad0cf | [link](https://prow.knative.dev/view/gs/knative-prow/pr-logs/pull/knative_serving/9019/pull-knative-serving-autotls-tests/1293205276932968451) | `/test pull-knative-serving-autotls-tests`

[Full PR test history](https://gubernator.knative.dev/pr/knative_serving/9019). [Your PR dashboard](https://gubernator.knative.dev/pr/markusthoemmes).

<details>

Instructions for interacting with me using PR comments are available [here](https://git.k8s.io/community/contributors/guide/pull-requests.md).  If you have questions or suggestions related to my behavior, please file an issue against the [kubernetes/test-infra](https://github.com/kubernetes/test-infra/issues/new?title=Prow%20issue:) repository. I understand the commands that are listed [here](https://go.k8s.io/bot-commands).
</details>
<!-- test report -->
knative-test-reporter-robot: <!--[AUTOMATED-FLAKY-RETRYER]bda31a4ee8805e8ef9be867cfc85a4b93b3ad0cf[AUTOMATED-FLAKY-RETRYER]-->
The following jobs failed:

Test name | Triggers | Retries
--- | --- | ---
pull-knative-serving-autotls-tests |  | 0/3
pull-knative-serving-unit-tests |  | 0/3

Failed non-flaky tests preventing automatic retry of pull-knative-serving-autotls-tests:

```
test/e2e/autotls.TestTls
test/e2e/autotls.TestTls/SelfSignedPerNamespace
```
knative-metrics-robot: The following is the coverage report on the affected files.
Say `/test pull-knative-serving-go-coverage` to re-run this coverage report

File | Old Coverage | New Coverage | Delta
---- |:------------:|:------------:|:-----:
[pkg/queue/breaker.go](https://storage.cloud.google.com/knative-prow/pr-logs/pull/knative_serving/9019/pull-knative-serving-go-coverage/1293431265667584002/artifacts/line-cov.html#file0) | 98.7% | 98.8% | 0.1

knative-prow-robot: @markusthoemmes: The following test **failed**, say `/retest` to rerun all failed tests:

Test name | Commit | Details | Rerun command
--- | --- | --- | ---
pull-knative-serving-integration-tests | 9d39f75843a3d557883969efaf2443195091ee38 | [link](https://prow.knative.dev/view/gs/knative-prow/pr-logs/pull/knative_serving/9019/pull-knative-serving-integration-tests/1293431265667584001) | `/test pull-knative-serving-integration-tests`

[Full PR test history](https://gubernator.knative.dev/pr/knative_serving/9019). [Your PR dashboard](https://gubernator.knative.dev/pr/markusthoemmes).

<details>

Instructions for interacting with me using PR comments are available [here](https://git.k8s.io/community/contributors/guide/pull-requests.md).  If you have questions or suggestions related to my behavior, please file an issue against the [kubernetes/test-infra](https://github.com/kubernetes/test-infra/issues/new?title=Prow%20issue:) repository. I understand the commands that are listed [here](https://go.k8s.io/bot-commands).
</details>
<!-- test report -->
knative-test-reporter-robot: <!--[AUTOMATED-FLAKY-RETRYER]9d39f75843a3d557883969efaf2443195091ee38[AUTOMATED-FLAKY-RETRYER]-->
The following jobs failed:

Test name | Triggers | Retries
--- | --- | ---
pull-knative-serving-integration-tests |  | 0/3

Failed non-flaky tests preventing automatic retry of pull-knative-serving-integration-tests:

```
test/conformance/api/v1beta1.TestContainerExitingMsg
test/conformance/api/v1beta1.TestRevisionTimeout
```
markusthoemmes: /hold

Until after the release
markusthoemmes: /retest
slinkydeveloper: /assign
slinkydeveloper: @markusthoemmes I'm curious, can you share the numbers with memory usage too? `-mem` flag
markusthoemmes: @slinkydeveloper I redacted them cause they are the same. 0 allocations throughout.
slinkydeveloper: /lgtm <- for the perfomance tweaks, i didn't fully analyzed the concurrency :smile: 
knative-prow-robot: @markusthoemmes: The following test **failed**, say `/retest` to rerun all failed tests:

Test name | Commit | Details | Rerun command
--- | --- | --- | ---
pull-knative-serving-integration-tests | 9d39f75843a3d557883969efaf2443195091ee38 | [link](https://prow.knative.dev/view/gs/knative-prow/pr-logs/pull/knative_serving/9019/pull-knative-serving-integration-tests/1293576976241528832) | `/test pull-knative-serving-integration-tests`

[Full PR test history](https://gubernator.knative.dev/pr/knative_serving/9019). [Your PR dashboard](https://gubernator.knative.dev/pr/markusthoemmes).

<details>

Instructions for interacting with me using PR comments are available [here](https://git.k8s.io/community/contributors/guide/pull-requests.md).  If you have questions or suggestions related to my behavior, please file an issue against the [kubernetes/test-infra](https://github.com/kubernetes/test-infra/issues/new?title=Prow%20issue:) repository. I understand the commands that are listed [here](https://go.k8s.io/bot-commands).
</details>
<!-- test report -->
knative-test-reporter-robot: <!--[AUTOMATED-FLAKY-RETRYER]9d39f75843a3d557883969efaf2443195091ee38[AUTOMATED-FLAKY-RETRYER]-->
The following jobs failed:

Test name | Triggers | Retries
--- | --- | ---
pull-knative-serving-integration-tests | [2020-08-12 16:47:59.448 +0000 UTC](https://prow.knative.dev/view/gs/knative-prow/pr-logs/pull/knative_serving/9019/pull-knative-serving-integration-tests/1293576976241528832) | 1/3

Automatically retrying due to test flakiness...
/test pull-knative-serving-integration-tests
knative-prow-robot: @markusthoemmes: The following test **failed**, say `/retest` to rerun all failed tests:

Test name | Commit | Details | Rerun command
--- | --- | --- | ---
pull-knative-serving-integration-tests | 9d39f75843a3d557883969efaf2443195091ee38 | [link](https://prow.knative.dev/view/gs/knative-prow/pr-logs/pull/knative_serving/9019/pull-knative-serving-integration-tests/1293590187942612992) | `/test pull-knative-serving-integration-tests`

[Full PR test history](https://gubernator.knative.dev/pr/knative_serving/9019). [Your PR dashboard](https://gubernator.knative.dev/pr/markusthoemmes).

<details>

Instructions for interacting with me using PR comments are available [here](https://git.k8s.io/community/contributors/guide/pull-requests.md).  If you have questions or suggestions related to my behavior, please file an issue against the [kubernetes/test-infra](https://github.com/kubernetes/test-infra/issues/new?title=Prow%20issue:) repository. I understand the commands that are listed [here](https://go.k8s.io/bot-commands).
</details>
<!-- test report -->
knative-test-reporter-robot: <!--[AUTOMATED-FLAKY-RETRYER]9d39f75843a3d557883969efaf2443195091ee38[AUTOMATED-FLAKY-RETRYER]-->
The following jobs failed:

Test name | Triggers | Retries
--- | --- | ---
pull-knative-serving-integration-tests | [2020-08-12 16:47:59.448 +0000 UTC](https://prow.knative.dev/view/gs/knative-prow/pr-logs/pull/knative_serving/9019/pull-knative-serving-integration-tests/1293576976241528832)<br>[2020-08-12 17:39:59.289 +0000 UTC](https://prow.knative.dev/view/gs/knative-prow/pr-logs/pull/knative_serving/9019/pull-knative-serving-integration-tests/1293590187942612992) | 2/3

Automatically retrying due to test flakiness...
/test pull-knative-serving-integration-tests
knative-prow-robot: @markusthoemmes: The following test **failed**, say `/retest` to rerun all failed tests:

Test name | Commit | Details | Rerun command
--- | --- | --- | ---
pull-knative-serving-integration-tests | 9d39f75843a3d557883969efaf2443195091ee38 | [link](https://prow.knative.dev/view/gs/knative-prow/pr-logs/pull/knative_serving/9019/pull-knative-serving-integration-tests/1293603400260259840) | `/test pull-knative-serving-integration-tests`

[Full PR test history](https://gubernator.knative.dev/pr/knative_serving/9019). [Your PR dashboard](https://gubernator.knative.dev/pr/markusthoemmes).

<details>

Instructions for interacting with me using PR comments are available [here](https://git.k8s.io/community/contributors/guide/pull-requests.md).  If you have questions or suggestions related to my behavior, please file an issue against the [kubernetes/test-infra](https://github.com/kubernetes/test-infra/issues/new?title=Prow%20issue:) repository. I understand the commands that are listed [here](https://go.k8s.io/bot-commands).
</details>
<!-- test report -->
knative-test-reporter-robot: <!--[AUTOMATED-FLAKY-RETRYER]9d39f75843a3d557883969efaf2443195091ee38[AUTOMATED-FLAKY-RETRYER]-->
The following jobs failed:

Test name | Triggers | Retries
--- | --- | ---
pull-knative-serving-integration-tests | [2020-08-12 16:47:59.448 +0000 UTC](https://prow.knative.dev/view/gs/knative-prow/pr-logs/pull/knative_serving/9019/pull-knative-serving-integration-tests/1293576976241528832)<br>[2020-08-12 17:39:59.289 +0000 UTC](https://prow.knative.dev/view/gs/knative-prow/pr-logs/pull/knative_serving/9019/pull-knative-serving-integration-tests/1293590187942612992)<br>[2020-08-12 18:26:59.322 +0000 UTC](https://prow.knative.dev/view/gs/knative-prow/pr-logs/pull/knative_serving/9019/pull-knative-serving-integration-tests/1293603400260259840) | 3/3

Automatically retrying due to test flakiness...
/test pull-knative-serving-integration-tests
knative-metrics-robot: The following is the coverage report on the affected files.
Say `/test pull-knative-serving-go-coverage` to re-run this coverage report

File | Old Coverage | New Coverage | Delta
---- |:------------:|:------------:|:-----:
[pkg/queue/breaker.go](https://storage.cloud.google.com/knative-prow/pr-logs/pull/knative_serving/9019/pull-knative-serving-go-coverage/1300344677903372293/artifacts/line-cov.html#file0) | 98.7% | 98.8% | 0.1


Patch:
diff --git a/.github/workflows/knative-go-build.yaml b/.github/workflows/knative-go-build.yaml
new file mode 100644
index 000000000..81b9b0084
--- /dev/null
+++ b/.github/workflows/knative-go-build.yaml
@@ -0,0 +1,49 @@
+# Copyright 2020 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.
+
+name: Build
+
+on:
+  push:
+    branches: [ 'master', 'release-*' ]
+  pull_request:
+    branches: [ 'master', 'release-*' ]
+
+jobs:
+
+  build:
+    name: Build
+    strategy:
+      matrix:
+        go-version: [1.15.x]
+        platform: [ubuntu-latest]
+
+    runs-on: ${{ matrix.platform }}
+
+    steps:
+
+      - name: Set up Go ${{ matrix.go-version }}
+        uses: actions/setup-go@v2
+        with:
+          go-version: ${{ matrix.go-version }}
+        id: go
+
+      - name: Check out code
+        uses: actions/checkout@v2
+
+      - name: Vet
+        run: go vet ./...
+
+      - name: Build
+        run: go build -v ./...
diff --git a/.github/workflows/knative-stale.yaml b/.github/workflows/knative-stale.yaml
index 9eb667bff..3983efbe8 100644
--- a/.github/workflows/knative-stale.yaml
+++ b/.github/workflows/knative-stale.yaml
@@ -1,7 +1,7 @@
 name: 'Close stale'
 on:
   schedule:
-  - cron: '* * * * *' # Runs everyday
+  - cron: '* * * * *'
 
 jobs:
   stale:
@@ -14,7 +14,7 @@ jobs:
         stale-issue-message: |-
           This issue is stale because it has been open for 90 days with no
           activity. It will automatically close after 30 more days of
-          inactivity. Reopen the issue with `/reopen`.Mark the issue as
+          inactivity. Reopen the issue with `/reopen`. Mark the issue as
           fresh by adding the comment `/remove-lifecycle stale`.
         stale-issue-label: 'lifecycle/stale'
         exempt-issue-labels: 'lifecycle/frozen'
@@ -22,7 +22,7 @@ jobs:
         stale-pr-message: |-
           This Pull Request is stale because it has been open for 90 days with
           no activity. It will automatically close after 30 more days of
-          inactivity. Reopen with `/reopen`.Mark as fresh by adding the
+          inactivity. Reopen with `/reopen`. Mark as fresh by adding the
           comment `/remove-lifecycle stale`.
         stale-pr-label: 'lifecycle/stale'
         exempt-pr-labels: 'lifecycle/frozen'
diff --git a/.github/workflows/knative-style.yaml b/.github/workflows/knative-style.yaml
new file mode 100644
index 000000000..e6dca76c3
--- /dev/null
+++ b/.github/workflows/knative-style.yaml
@@ -0,0 +1,57 @@
+# Copyright 2020 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.
+
+name: Code Style
+
+on:
+  push:
+    branches: [ 'master', 'release-*' ]
+  pull_request:
+    branches: [ 'master', 'release-*' ]
+
+jobs:
+
+  lint:
+    name: Lint
+    runs-on: ubuntu-latest
+
+    steps:
+
+      - name: Set up Go 1.15.x
+        uses: actions/setup-go@v2
+        with:
+          go-version: 1.15.x
+        id: go
+
+      - name: Check out code
+        uses: actions/checkout@v2
+
+      # TODO: add prettier step
+      # TODO: add goimports step
+
+      - name: Go Format
+        shell: bash
+        run: |
+          gofmt -s -w $(find -path './vendor' -prune -o -path './third_party' -prune -o -type f -name '*.go' -print)
+
+      - name: Verify
+        shell: bash
+        run: |
+          if [[ $(git diff-index --name-only HEAD --) ]]; then
+              echo "Found diffs in:"
+              git diff-index --name-only HEAD --
+              echo "${{ github.repository }} is out of style. Please run go fmt."
+              exit 1
+          fi
+          echo "${{ github.repository }} is formatted correctly."
diff --git a/.github/workflows/knative-verify.yaml b/.github/workflows/knative-verify.yaml
new file mode 100644
index 000000000..6128969f3
--- /dev/null
+++ b/.github/workflows/knative-verify.yaml
@@ -0,0 +1,74 @@
+# Copyright 2020 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.
+
+name: Verify
+
+on:
+  push:
+    branches: [ 'master', 'release-*' ]
+  pull_request:
+    branches: [ 'master', 'release-*' ]
+
+jobs:
+
+  verify:
+    name: Verify Deps and Codegen
+    strategy:
+      matrix:
+        go-version: [1.15.x]
+        platform: [ubuntu-latest]
+
+    runs-on: ${{ matrix.platform }}
+
+    env:
+      GOPATH: ${{ github.workspace }}
+
+    steps:
+
+    - name: Set up Go ${{ matrix.go-version }}
+      uses: actions/setup-go@v2
+      with:
+        go-version: ${{ matrix.go-version }}
+      id: go
+
+    - name: Install Dependencies
+      run: |
+        go get github.com/google/ko/cmd/ko
+        go get github.com/google/go-licenses
+
+    - name: Check out code onto GOPATH
+      uses: actions/checkout@v2
+      with:
+        path: ./src/knative.dev/${{ github.event.repository.name }}
+
+    - name: Update Codegen
+      shell: bash
+      run: |
+        pushd ./src/knative.dev/${{ github.event.repository.name }}
+        ./hack/update-codegen.sh
+        popd
+
+    - name: Verify
+      shell: bash
+      run: |
+        pushd ./src/knative.dev/${{ github.event.repository.name }}
+        if [[ -z "$(git status --porcelain)" ]]; then
+            echo "${{ github.repository }} up to date."
+        else
+            repoDiff=$(git diff-index --name-only HEAD --)
+            echo "Found diffs in: $repoDiff"
+            echo "${{ github.repository }} is out of date. Please run hack/update-codegen.sh"
+            exit 1
+        fi
+        popd
diff --git a/cmd/queue/main.go b/cmd/queue/main.go
index 262b9ff08..11db447c1 100644
--- a/cmd/queue/main.go
+++ b/cmd/queue/main.go
@@ -36,6 +36,8 @@ import (
 
 	"k8s.io/apimachinery/pkg/types"
 
+	"path/filepath"
+
 	network "knative.dev/networking/pkg"
 	"knative.dev/networking/pkg/apis/networking"
 	pkglogging "knative.dev/pkg/logging"
@@ -55,7 +57,6 @@ import (
 	"knative.dev/serving/pkg/queue"
 	"knative.dev/serving/pkg/queue/health"
 	"knative.dev/serving/pkg/queue/readiness"
-	"path/filepath"
 )
 
 const (
diff --git a/go.mod b/go.mod
index 31c73da19..5dae207c7 100644
--- a/go.mod
+++ b/go.mod
@@ -17,7 +17,6 @@ require (
 	github.com/mattbaird/jsonpatch v0.0.0-20171005235357-81af80346b1a
 	github.com/prometheus/client_golang v1.6.0
 	github.com/prometheus/client_model v0.2.0
-	github.com/prometheus/common v0.9.1
 	github.com/tsenart/vegeta v12.7.1-0.20190725001342-b5f4fca92137+incompatible
 	go.opencensus.io v0.22.4
 	go.uber.org/atomic v1.6.0
@@ -32,10 +31,10 @@ require (
 	k8s.io/client-go v11.0.1-0.20190805182717-6502b5e7b1b5+incompatible
 	k8s.io/code-generator v0.18.6
 	k8s.io/kube-openapi v0.0.0-20200410145947-bcb3869e6f29
-	knative.dev/caching v0.0.0-20200827155307-479979167783
-	knative.dev/networking v0.0.0-20200828035206-7f8093fa206d
-	knative.dev/pkg v0.0.0-20200827231407-8d478d55c0e6
-	knative.dev/test-infra v0.0.0-20200828024406-bf34ea55d769
+	knative.dev/caching v0.0.0-20200831164016-8993b9e0e6f6
+	knative.dev/networking v0.0.0-20200831163108-0195c961813b
+	knative.dev/pkg v0.0.0-20200831210615-9c7506148742
+	knative.dev/test-infra v0.0.0-20200828211307-9d4372c9b1c7
 )
 
 replace (
diff --git a/go.sum b/go.sum
index 73549fa77..0858af050 100644
--- a/go.sum
+++ b/go.sum
@@ -1797,12 +1797,12 @@ k8s.io/utils v0.0.0-20200603063816-c1c6865ac451 h1:v8ud2Up6QK1lNOKFgiIVrZdMg7Mpm
 k8s.io/utils v0.0.0-20200603063816-c1c6865ac451/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA=
 knative.dev/caching v0.0.0-20190719140829-2032732871ff/go.mod h1:dHXFU6CGlLlbzaWc32g80cR92iuBSpsslDNBWI8C7eg=
 knative.dev/caching v0.0.0-20200116200605-67bca2c83dfa/go.mod h1:dHXFU6CGlLlbzaWc32g80cR92iuBSpsslDNBWI8C7eg=
-knative.dev/caching v0.0.0-20200827155307-479979167783 h1:9yUDT7LFwmwLqC78Y0BAoAg5hrLAfWuCNx8q4jeCLzA=
-knative.dev/caching v0.0.0-20200827155307-479979167783/go.mod h1:z4u/XnvKHH7HJpTbW7+j17SQtX9XtqnFytDcFLpUwtw=
+knative.dev/caching v0.0.0-20200831164016-8993b9e0e6f6 h1:tS93pEA8jCt8onqnXE3LoE1X6qTHdZVJ3QgF6UoE9jg=
+knative.dev/caching v0.0.0-20200831164016-8993b9e0e6f6/go.mod h1:5V2/0AagUm0RX4CsCDOY9FwLRnnF2c5IaaA+kqXbqAc=
 knative.dev/eventing-contrib v0.6.1-0.20190723221543-5ce18048c08b/go.mod h1:SnXZgSGgMSMLNFTwTnpaOH7hXDzTFtw0J8OmHflNx3g=
 knative.dev/eventing-contrib v0.11.2/go.mod h1:SnXZgSGgMSMLNFTwTnpaOH7hXDzTFtw0J8OmHflNx3g=
-knative.dev/networking v0.0.0-20200828035206-7f8093fa206d h1:+2hvbm2Q/HTOSBCqTfJg9Fc/SXE+5mhUvz2U1iMkcDU=
-knative.dev/networking v0.0.0-20200828035206-7f8093fa206d/go.mod h1:Ij4LmJ0j0y4o+I814gOFpwXhyG8RDJui57lguGi6dBc=
+knative.dev/networking v0.0.0-20200831163108-0195c961813b h1:RDQ+NOiTmSNOukmq57Y5CGAq6NJgamCXVf9FJsltJbc=
+knative.dev/networking v0.0.0-20200831163108-0195c961813b/go.mod h1:9ZMxo5uevxp5T+e7djbo+jH4KXK6Lja9rPS0u86+GHw=
 knative.dev/pkg v0.0.0-20191101194912-56c2594e4f11/go.mod h1:pgODObA1dTyhNoFxPZTTjNWfx6F0aKsKzn+vaT9XO/Q=
 knative.dev/pkg v0.0.0-20191111150521-6d806b998379/go.mod h1:pgODObA1dTyhNoFxPZTTjNWfx6F0aKsKzn+vaT9XO/Q=
 knative.dev/pkg v0.0.0-20200207155214-fef852970f43/go.mod h1:pgODObA1dTyhNoFxPZTTjNWfx6F0aKsKzn+vaT9XO/Q=
@@ -1811,10 +1811,10 @@ knative.dev/pkg v0.0.0-20200505191044-3da93ebb24c2/go.mod h1:Q6sL35DdGs8hIQZKdaC
 knative.dev/pkg v0.0.0-20200515002500-16d7b963416f/go.mod h1:tMOHGbxtRz8zYFGEGpV/bpoTEM1o89MwYFC4YJXl3GY=
 knative.dev/pkg v0.0.0-20200528142800-1c6815d7e4c9/go.mod h1:QgNZTxnwpB/oSpNcfnLVlw+WpEwwyKAvJlvR3hgeltA=
 knative.dev/pkg v0.0.0-20200711004937-22502028e31a/go.mod h1:AqAJV6rYi8IGikDjJ/9ZQd9qKdkXVlesVnVjwx62YB8=
-knative.dev/pkg v0.0.0-20200827020906-21dcafbfa751 h1:vWHtnpUHJnX6DUVS0eanSLjyxhBpPxfXB/cFUUxXiF8=
-knative.dev/pkg v0.0.0-20200827020906-21dcafbfa751/go.mod h1:vOVUMtkIWK+hq1+KwlLYBSYVjz5SNIP1vMj3/5SNGZw=
-knative.dev/pkg v0.0.0-20200827231407-8d478d55c0e6 h1:Wj8q6aj02ht5vimP6wLKfxG3I50v637wXxoyt0oLnTE=
-knative.dev/pkg v0.0.0-20200827231407-8d478d55c0e6/go.mod h1:vOVUMtkIWK+hq1+KwlLYBSYVjz5SNIP1vMj3/5SNGZw=
+knative.dev/pkg v0.0.0-20200831094608-e9da10bc8a18 h1:BJTbbN0/J0BY76tzHE6/oko4lw6GEeahMrZUfigySyc=
+knative.dev/pkg v0.0.0-20200831094608-e9da10bc8a18/go.mod h1:vOVUMtkIWK+hq1+KwlLYBSYVjz5SNIP1vMj3/5SNGZw=
+knative.dev/pkg v0.0.0-20200831210615-9c7506148742 h1:RZ/7XRzuh6h1SKv8ucQEUgEGIe3R9VjHe3eNw+LiBWc=
+knative.dev/pkg v0.0.0-20200831210615-9c7506148742/go.mod h1:vOVUMtkIWK+hq1+KwlLYBSYVjz5SNIP1vMj3/5SNGZw=
 knative.dev/test-infra v0.0.0-20200407185800-1b88cb3b45a5/go.mod h1:xcdUkMJrLlBswIZqL5zCuBFOC22WIPMQoVX1L35i0vQ=
 knative.dev/test-infra v0.0.0-20200505052144-5ea2f705bb55/go.mod h1:WqF1Azka+FxPZ20keR2zCNtiQA1MP9ZB4BH4HuI+SIU=
 knative.dev/test-infra v0.0.0-20200513011557-d03429a76034/go.mod h1:aMif0KXL4g19YCYwsy4Ocjjz5xgPlseYV+B95Oo4JGE=
@@ -1822,10 +1822,8 @@ knative.dev/test-infra v0.0.0-20200519015156-82551620b0a9/go.mod h1:A5b2OAXTOeHT
 knative.dev/test-infra v0.0.0-20200707183444-aed09e56ddc7/go.mod h1:RjYAhXnZqeHw9+B0zsbqSPlae0lCvjekO/nw5ZMpLCs=
 knative.dev/test-infra v0.0.0-20200825022047-cb4bb218c5e5 h1:iqbpe5rccMW2r1Mu1Prw4gJ9xi7yqahmtxzckTUHBeI=
 knative.dev/test-infra v0.0.0-20200825022047-cb4bb218c5e5/go.mod h1:Pmg2c7Z7q7BGFUV/GOpU5BlrD3ePJft4MPqx8AYBplc=
-knative.dev/test-infra v0.0.0-20200826192206-b4adbd18e3fe h1:fHJNwbFZ6sWRC0NqIDlgLQOebMNOyCIPCT1uAs+YJNs=
-knative.dev/test-infra v0.0.0-20200826192206-b4adbd18e3fe/go.mod h1:Pmg2c7Z7q7BGFUV/GOpU5BlrD3ePJft4MPqx8AYBplc=
-knative.dev/test-infra v0.0.0-20200828024406-bf34ea55d769 h1:4F657CFFjQ81XPe/hOSj2OoCA9G0GUVmgW/5FWAy6vM=
-knative.dev/test-infra v0.0.0-20200828024406-bf34ea55d769/go.mod h1:Pmg2c7Z7q7BGFUV/GOpU5BlrD3ePJft4MPqx8AYBplc=
+knative.dev/test-infra v0.0.0-20200828211307-9d4372c9b1c7 h1:yIzao6i9Hu51SCdpyqBEnhpt7G12FqFrxHK6ZnQnu8o=
+knative.dev/test-infra v0.0.0-20200828211307-9d4372c9b1c7/go.mod h1:Pmg2c7Z7q7BGFUV/GOpU5BlrD3ePJft4MPqx8AYBplc=
 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/net/throttler.go b/pkg/activator/net/throttler.go
index 405fec9a6..48e159490 100644
--- a/pkg/activator/net/throttler.go
+++ b/pkg/activator/net/throttler.go
@@ -278,9 +278,7 @@ func (rt *revisionThrottler) updateCapacity(backendCount int) {
 		assigned := rt.podTrackers
 		if rt.containerConcurrency != 0 {
 			rt.resetTrackers()
-			// TODO(vagababov): pull assign slice into RT.
-			assigned = assignSlice(rt.podTrackers,
-				ai, ac, rt.containerConcurrency)
+			assigned = assignSlice(rt.podTrackers, ai, ac, rt.containerConcurrency)
 		}
 		rt.logger.Debugf("Trackers %d/%d:  %v", ai, ac, rt.assignedTrackers)
 		// The actual write out of the assigned trackers has to be under lock.
diff --git a/pkg/apis/serving/v1alpha1/route_types.go b/pkg/apis/serving/v1alpha1/route_types.go
index d4e85ecb6..81d47c895 100644
--- a/pkg/apis/serving/v1alpha1/route_types.go
+++ b/pkg/apis/serving/v1alpha1/route_types.go
@@ -89,7 +89,7 @@ type RouteSpec struct {
 	// Tracking issue: https://github.com/knative/serving/issues/643
 	//
 	// +optional
-	DeprecatedGeneration int64 `json:"generation,omitempty"`
+	DeprecatedGeneration int64
 
 	// Traffic specifies how to distribute traffic over a collection of Knative Serving Revisions and Configurations.
 	// +optional
diff --git a/pkg/autoscaler/metrics/http_scrape_client.go b/pkg/autoscaler/metrics/http_scrape_client.go
index 4a19732dc..40a0436ff 100644
--- a/pkg/autoscaler/metrics/http_scrape_client.go
+++ b/pkg/autoscaler/metrics/http_scrape_client.go
@@ -24,11 +24,11 @@ import (
 	"net/http"
 	"sync"
 
-	dto "github.com/prometheus/client_model/go"
-	"github.com/prometheus/common/expfmt"
 	network "knative.dev/networking/pkg"
 )
 
+var errUnsupportedMetricType = errors.New("unsupported metric type")
+
 type httpScrapeClient struct {
 	httpClient *http.Client
 }
@@ -54,8 +54,7 @@ func (c *httpScrapeClient) Scrape(url string) (Stat, error) {
 	if err != nil {
 		return emptyStat, err
 	}
-	// Ask for protobuf by default. Note that during migration this will not trigger the proto format supported
-	// by Prometheus reporter as the latter uses `application/vnd.google.protobuf`.
+
 	req.Header.Add("Accept", network.ProtoAcceptContent)
 	resp, err := c.httpClient.Do(req)
 	if err != nil {
@@ -65,10 +64,10 @@ func (c *httpScrapeClient) Scrape(url string) (Stat, error) {
 	if resp.StatusCode < http.StatusOK || resp.StatusCode >= http.StatusMultipleChoices {
 		return emptyStat, fmt.Errorf("GET request for URL %q returned HTTP status %v", url, resp.StatusCode)
 	}
-	if resp.Header.Get("Content-Type") == network.ProtoAcceptContent {
-		return statFromProto(resp.Body)
+	if resp.Header.Get("Content-Type") != network.ProtoAcceptContent {
+		return emptyStat, errUnsupportedMetricType
 	}
-	return statFromPrometheus(resp.Body)
+	return statFromProto(resp.Body)
 }
 
 func statFromProto(body io.Reader) (Stat, error) {
@@ -86,67 +85,3 @@ func statFromProto(body io.Reader) (Stat, error) {
 	}
 	return stat, nil
 }
-
-func statFromPrometheus(body io.Reader) (Stat, error) {
-	var parser expfmt.TextParser
-	metricFamilies, err := parser.TextToMetricFamilies(body)
-	if err != nil {
-		return emptyStat, fmt.Errorf("reading text format failed: %w", err)
-	}
-
-	stat := emptyStat
-	for m, pv := range map[string]*float64{
-		"queue_average_concurrent_requests":         &stat.AverageConcurrentRequests,
-		"queue_average_proxied_concurrent_requests": &stat.AverageProxiedConcurrentRequests,
-		"queue_requests_per_second":                 &stat.RequestCount,
-		"queue_proxied_operations_per_second":       &stat.ProxiedRequestCount,
-	} {
-		pm := prometheusMetric(metricFamilies, m)
-		if pm == nil {
-			return emptyStat, fmt.Errorf("could not find value for %s in response", m)
-		}
-		*pv = *pm.Gauge.Value
-
-		if stat.PodName == "" {
-			stat.PodName = prometheusLabel(pm.Label, "destination_pod")
-			if stat.PodName == "" {
-				return emptyStat, errors.New("could not find pod name in metric labels")
-			}
-		}
-	}
-	// Transitional metrics, which older pods won't report.
-	for m, pv := range map[string]*float64{
-		"process_uptime": &stat.ProcessUptime, // Can be removed after 0.15 cuts.
-	} {
-		pm := prometheusMetric(metricFamilies, m)
-		// Ignore if not found.
-		if pm == nil {
-			continue
-		}
-		*pv = *pm.Gauge.Value
-	}
-	return stat, nil
-}
-
-// prometheusMetric returns the point of the first Metric of the MetricFamily
-// with the given key from the given map. If there is no such MetricFamily or it
-// has no Metrics, then returns nil.
-func prometheusMetric(metricFamilies map[string]*dto.MetricFamily, key string) *dto.Metric {
-	if metric, ok := metricFamilies[key]; ok && len(metric.Metric) > 0 {
-		return metric.Metric[0]
-	}
-
-	return nil
-}
-
-// prometheusLabels returns the value of the label with the given key from the
-// given slice of labels. Returns an empty string if the label cannot be found.
-func prometheusLabel(labels []*dto.LabelPair, key string) string {
-	for _, label := range labels {
-		if *label.Name == key {
-			return *label.Value
-		}
-	}
-
-	return ""
-}
diff --git a/pkg/queue/breaker.go b/pkg/queue/breaker.go
index 20c9a312f..1a4bcab8c 100644
--- a/pkg/queue/breaker.go
+++ b/pkg/queue/breaker.go
@@ -21,7 +21,6 @@ import (
 	"errors"
 	"fmt"
 	"math"
-	"sync"
 
 	"go.uber.org/atomic"
 )
@@ -177,131 +176,143 @@ func (b *Breaker) Capacity() int {
 	return b.sem.Capacity()
 }
 
-// newSemaphore creates a semaphore with the desired maximal and initial capacity.
-// Maximal capacity is the size of the buffered channel, it defines maximum number of tokens
-// in the rotation. Attempting to add more capacity then the max will result in error.
-// Initial capacity is the initial number of free tokens.
+// newSemaphore creates a semaphore with the desired initial capacity.
 func newSemaphore(maxCapacity, initialCapacity int) *semaphore {
 	queue := make(chan struct{}, maxCapacity)
 	sem := &semaphore{queue: queue}
-	if initialCapacity > 0 {
-		sem.updateCapacity(initialCapacity)
-	}
+	sem.updateCapacity(initialCapacity)
 	return sem
 }
 
-// semaphore is an implementation of a semaphore based on Go channels.
-// The presence of elements in the `queue` buffered channel correspond to available tokens.
-// Hence the max number of tokens to hand out equals to the size of the channel.
-// `capacity` defines the current number of tokens in the rotation.
+// semaphore is an implementation of a semaphore based on packed integers and a channel.
+// state is an uint64 that has two uint32s packed into it: capacity and inFlight. The
+// former specifies how many request are allowed at any given time into the semaphore
+// while the latter refers to the currently in-flight requests.
+// Packing them both into one uint64 allows us to optimize access semantics using atomic
+// operations, which can't be guaranteed on 2 individual values.
+// The channel is merely used as a vehicle to be able to "wake up" individual goroutines
+// if capacity becomes free. It's not consistently used in accordance to actual capacity
+// but is rather a communication vehicle to ensure waiting routines are properly woken
+// up.
 type semaphore struct {
-	queue    chan struct{}
-	reducers int
-	capacity int
-	mux      sync.RWMutex
+	state atomic.Uint64
+	queue chan struct{}
 }
 
-// tryAcquire receives the token from the semaphore if there's one
-// otherwise an error is returned.
+// tryAcquire receives a token from the semaphore if there is one otherwise returns false.
 func (s *semaphore) tryAcquire() bool {
-	select {
-	case <-s.queue:
-		return true
-	default:
-		return false
+	for {
+		old := s.state.Load()
+		capacity, in := unpack(old)
+		if in >= capacity {
+			return false
+		}
+		in++
+		if s.state.CAS(old, pack(capacity, in)) {
+			return true
+		}
 	}
 }
 
-// acquire receives the token from the semaphore, potentially blocking.
+// acquire acquires capacity from the semaphore.
 func (s *semaphore) acquire(ctx context.Context) error {
-	select {
-	case <-s.queue:
-		return nil
-	case <-ctx.Done():
-		return ctx.Err()
+	for {
+		old := s.state.Load()
+		capacity, in := unpack(old)
+
+		if in >= capacity {
+			select {
+			case <-ctx.Done():
+				return ctx.Err()
+			case <-s.queue:
+			}
+			// Force reload state.
+			continue
+		}
+
+		in++
+		if s.state.CAS(old, pack(capacity, in)) {
+			return nil
+		}
 	}
 }
 
-// release potentially puts the token back to the queue.
-// If the semaphore capacity was reduced in between and is not yet reflected,
-// we remove the tokens from the rotation instead of returning them back.
+// release releases capacity in the semaphore.
+// If the semaphore capacity was reduced in between and as a result inFlight is greater
+// than capacity, we don't wake up goroutines as they'd not get any capacity anyway.
 func (s *semaphore) release() error {
-	s.mux.Lock()
-	defer s.mux.Unlock()
+	for {
+		old := s.state.Load()
+		capacity, in := unpack(old)
 
-	if s.reducers > 0 {
-		s.capacity--
-		s.reducers--
-		return nil
-	}
+		if in == 0 {
+			return ErrRelease
+		}
 
-	// We want to make sure releasing a token is always non-blocking.
-	select {
-	case s.queue <- struct{}{}:
-		return nil
-	default:
-		// This only happens if release is called more often than acquire.
-		return ErrRelease
+		in--
+		if s.state.CAS(old, pack(capacity, in)) {
+			if in < capacity {
+				select {
+				case s.queue <- struct{}{}:
+				default:
+					// We generate more wakeups than we might need as we don't know
+					// how many goroutines are waiting here. It is therefore okay
+					// to drop the poke on the floor here as this case would mean we
+					// have enough wakeups to wake up as many goroutines as this semaphore
+					// can take, which is guaranteed to be enough.
+				}
+			}
+			return nil
+		}
 	}
 }
 
-// updateCapacity updates the capacity of the semaphore to the desired
-// size.
+// updateCapacity updates the capacity of the semaphore to the desired size.
 func (s *semaphore) updateCapacity(size int) error {
 	if size < 0 || size > cap(s.queue) {
 		return ErrUpdateCapacity
 	}
 
-	s.mux.Lock()
-	defer s.mux.Unlock()
-
-	if s.effectiveCapacity() == size {
-		return nil
-	}
+	s64 := uint64(size)
+	for {
+		old := s.state.Load()
+		capacity, in := unpack(old)
 
-	// Add capacity until we reach size, potentially consuming
-	// outstanding reducers first.
-	for s.effectiveCapacity() < size {
-		if s.reducers > 0 {
-			s.reducers--
-		} else {
-			select {
-			case s.queue <- struct{}{}:
-				s.capacity++
-			default:
-				// This indicates that we're operating close to
-				// MaxCapacity and returned more tokens than we
-				// acquired.
-				return ErrUpdateCapacity
-			}
+		if capacity == s64 {
+			// Nothing to do, exit early.
+			return nil
 		}
-	}
 
-	// Reduce capacity until we reach size, potentially adding
-	// new reducers if the queue channel is empty because of
-	// requests in-flight.
-	for s.effectiveCapacity() > size {
-		select {
-		case <-s.queue:
-			s.capacity--
-		default:
-			s.reducers++
+		if s.state.CAS(old, pack(s64, in)) {
+			if s64 > capacity {
+				for i := uint64(0); i < s64-capacity; i++ {
+					select {
+					case s.queue <- struct{}{}:
+					default:
+						// See comment in `release` for explanation of this case.
+					}
+				}
+			}
+			return nil
 		}
 	}
-
-	return nil
 }
 
-// effectiveCapacity is the capacity with reducers taken into account.
-// `mux` must be held to call it.
-func (s *semaphore) effectiveCapacity() int {
-	return s.capacity - s.reducers
+// Capacity is the capacity of the semaphore.
+func (s *semaphore) Capacity() int {
+	capacity, _ := unpack(s.state.Load())
+	return int(capacity)
 }
 
-// Capacity is the effective capacity after taking reducers into account.
-func (s *semaphore) Capacity() int {
-	s.mux.RLock()
-	defer s.mux.RUnlock()
+// unpack takes an uint64 and returns two uint32 (as uint64) comprised of the leftmost
+// and the rightmost bits respectively.
+func unpack(in uint64) (uint64, uint64) {
+	return in >> 32, in & 0xffffffff
+}
 
-	return s.effectiveCapacity()
+// pack takes two uint32 (as uint64 to avoid casting) and packs them into a single uint64
+// at the leftmost and the rightmost bits respectively.
+// It's up to the caller to ensure that left and right actually fit into 32 bit.
+func pack(left, right uint64) uint64 {
+	return left<<32 | right
 }
diff --git a/pkg/reconciler/autoscaling/kpa/kpa.go b/pkg/reconciler/autoscaling/kpa/kpa.go
index 2a777c429..badaa15b3 100644
--- a/pkg/reconciler/autoscaling/kpa/kpa.go
+++ b/pkg/reconciler/autoscaling/kpa/kpa.go
@@ -243,7 +243,7 @@ func computeActiveCondition(ctx context.Context, pa *pav1alpha1.PodAutoscaler, p
 	// In this case we'll be in the NoTraffic/inactive state.
 	// TODO(taragu): remove after 0.19
 	alreadyScaledDownSuccessfully := minReady > 0 && pa.Status.GetCondition(pav1alpha1.PodAutoscalerConditionActive).Reason == noTrafficReason
-	if pc.ready >= minReady || alreadyScaledDownSuccessfully {
+	if (pc.ready >= minReady || alreadyScaledDownSuccessfully) && pa.Status.ServiceName != "" {
 		pa.Status.MarkScaleTargetInitialized()
 	}
 
diff --git a/pkg/reconciler/route/resources/labels/labels.go b/pkg/reconciler/route/resources/labels/labels.go
index 3a77f6017..c6cbeee38 100644
--- a/pkg/reconciler/route/resources/labels/labels.go
+++ b/pkg/reconciler/route/resources/labels/labels.go
@@ -22,8 +22,8 @@ import (
 )
 
 // IsObjectLocalVisibility returns whether an ObjectMeta is of cluster-local visibility
-func IsObjectLocalVisibility(meta v1.ObjectMeta) bool {
-	return meta.Labels != nil && meta.Labels[serving.VisibilityLabelKey] != ""
+func IsObjectLocalVisibility(meta *v1.ObjectMeta) bool {
+	return meta.Labels[serving.VisibilityLabelKey] != ""
 }
 
 // SetVisibility sets the visibility on an ObjectMeta
diff --git a/pkg/reconciler/route/traffic/traffic.go b/pkg/reconciler/route/traffic/traffic.go
index c4389dcdc..9f695f6fa 100644
--- a/pkg/reconciler/route/traffic/traffic.go
+++ b/pkg/reconciler/route/traffic/traffic.go
@@ -116,7 +116,7 @@ func (t *Config) GetRevisionTrafficTargets(ctx context.Context, r *v1.Route) ([]
 
 			labels.SetVisibility(meta, t.Visibility[tt.Tag] == netv1alpha1.IngressVisibilityClusterLocal)
 
-			// http is currently the only supported scheme
+			// HTTP is currently the only supported scheme.
 			fullDomain, err := domains.DomainNameFromTemplate(ctx, *meta, hostname)
 			if err != nil {
 				return nil, err
diff --git a/pkg/reconciler/route/visibility/visibility.go b/pkg/reconciler/route/visibility/visibility.go
index d6f8892d1..08665b0bc 100644
--- a/pkg/reconciler/route/visibility/visibility.go
+++ b/pkg/reconciler/route/visibility/visibility.go
@@ -99,7 +99,7 @@ func (b *Resolver) GetVisibility(ctx context.Context, route *v1.Route) (map[stri
 		ttVisibility := netv1alpha1.IngressVisibilityExternalIP
 		// Is there a visibility setting on the placeholder Service?
 		if svc, ok := services[hostname]; ok {
-			if labels.IsObjectLocalVisibility(svc.ObjectMeta) {
+			if labels.IsObjectLocalVisibility(&svc.ObjectMeta) {
 				ttVisibility = netv1alpha1.IngressVisibilityClusterLocal
 			}
 		}
diff --git a/third_party/cert-manager-0.12.0/net-certmanager.yaml b/third_party/cert-manager-0.12.0/net-certmanager.yaml
index 9755119c0..8936c6eaf 100644
--- a/third_party/cert-manager-0.12.0/net-certmanager.yaml
+++ b/third_party/cert-manager-0.12.0/net-certmanager.yaml
@@ -18,7 +18,7 @@ metadata:
   # These are the permissions needed by the `cert-manager` `Certificate` implementation.
   name: knative-serving-certmanager
   labels:
-    serving.knative.dev/release: "v20200828-8a5392f"
+    serving.knative.dev/release: "v20200831-8a5392f"
     serving.knative.dev/controller: "true"
     networking.knative.dev/certificate-provider: cert-manager
 rules:
@@ -49,7 +49,7 @@ kind: ValidatingWebhookConfiguration
 metadata:
   name: config.webhook.net-certmanager.networking.internal.knative.dev
   labels:
-    serving.knative.dev/release: "v20200828-8a5392f"
+    serving.knative.dev/release: "v20200831-8a5392f"
 webhooks:
 - admissionReviewVersions:
   - v1beta1
@@ -86,7 +86,7 @@ metadata:
   name: net-certmanager-webhook-certs
   namespace: knative-serving
   labels:
-    serving.knative.dev/release: "v20200828-8a5392f"
+    serving.knative.dev/release: "v20200831-8a5392f"
 
 ---
 # Copyright 2020 The Knative Authors
@@ -109,7 +109,7 @@ metadata:
   name: config-certmanager
   namespace: knative-serving
   labels:
-    serving.knative.dev/release: "v20200828-8a5392f"
+    serving.knative.dev/release: "v20200831-8a5392f"
     networking.knative.dev/certificate-provider: cert-manager
 data:
   _example: |
@@ -156,7 +156,7 @@ metadata:
   name: networking-certmanager
   namespace: knative-serving
   labels:
-    serving.knative.dev/release: "v20200828-8a5392f"
+    serving.knative.dev/release: "v20200831-8a5392f"
     networking.knative.dev/certificate-provider: cert-manager
 spec:
   selector:
@@ -168,7 +168,7 @@ spec:
         cluster-autoscaler.kubernetes.io/safe-to-evict: "true"
       labels:
         app: networking-certmanager
-        serving.knative.dev/release: "v20200828-8a5392f"
+        serving.knative.dev/release: "v20200831-8a5392f"
     spec:
       serviceAccountName: controller
       containers:
@@ -208,7 +208,7 @@ kind: Service
 metadata:
   labels:
     app: networking-certmanager
-    serving.knative.dev/release: "v20200828-8a5392f"
+    serving.knative.dev/release: "v20200831-8a5392f"
     networking.knative.dev/certificate-provider: cert-manager
   name: networking-certmanager
   namespace: knative-serving
@@ -245,7 +245,7 @@ metadata:
   name: net-certmanager-webhook
   namespace: knative-serving
   labels:
-    serving.knative.dev/release: "v20200828-8a5392f"
+    serving.knative.dev/release: "v20200831-8a5392f"
 spec:
   selector:
     matchLabels:
@@ -258,7 +258,7 @@ spec:
       labels:
         app: net-certmanager-webhook
         role: net-certmanager-webhook
-        serving.knative.dev/release: "v20200828-8a5392f"
+        serving.knative.dev/release: "v20200831-8a5392f"
     spec:
       serviceAccountName: controller
       containers:
@@ -319,7 +319,7 @@ metadata:
   namespace: knative-serving
   labels:
     role: net-certmanager-webhook
-    serving.knative.dev/release: "v20200828-8a5392f"
+    serving.knative.dev/release: "v20200831-8a5392f"
 spec:
   ports:
   - # Define metrics and profiling for them to be accessible within service meshes.
diff --git a/third_party/net-istio.yaml b/third_party/net-istio.yaml
index c2ac04a2c..3c7d2bf63 100644
--- a/third_party/net-istio.yaml
+++ b/third_party/net-istio.yaml
@@ -18,7 +18,7 @@ metadata:
   # These are the permissions needed by the Istio Ingress implementation.
   name: knative-serving-istio
   labels:
-    serving.knative.dev/release: "v20200828-2555ce9"
+    serving.knative.dev/release: "v20200831-cfa0ad5"
     serving.knative.dev/controller: "true"
     networking.knative.dev/ingress-provider: istio
 rules:
@@ -48,7 +48,7 @@ metadata:
   name: knative-ingress-gateway
   namespace: knative-serving
   labels:
-    serving.knative.dev/release: "v20200828-2555ce9"
+    serving.knative.dev/release: "v20200831-cfa0ad5"
     networking.knative.dev/ingress-provider: istio
 spec:
   selector:
@@ -85,7 +85,7 @@ metadata:
   name: cluster-local-gateway
   namespace: knative-serving
   labels:
-    serving.knative.dev/release: "v20200828-2555ce9"
+    serving.knative.dev/release: "v20200831-cfa0ad5"
     networking.knative.dev/ingress-provider: istio
 spec:
   selector:
@@ -104,7 +104,7 @@ metadata:
   name: knative-local-gateway
   namespace: knative-serving
   labels:
-    serving.knative.dev/release: "v20200828-2555ce9"
+    serving.knative.dev/release: "v20200831-cfa0ad5"
     networking.knative.dev/ingress-provider: istio
 spec:
   selector:
@@ -123,7 +123,7 @@ metadata:
   name: knative-local-gateway
   namespace: knative-serving
   labels:
-    serving.knative.dev/release: "v20200828-2555ce9"
+    serving.knative.dev/release: "v20200831-cfa0ad5"
     networking.knative.dev/ingress-provider: istio
 spec:
   type: ClusterIP
@@ -181,7 +181,7 @@ kind: MutatingWebhookConfiguration
 metadata:
   name: webhook.istio.networking.internal.knative.dev
   labels:
-    serving.knative.dev/release: "v20200828-2555ce9"
+    serving.knative.dev/release: "v20200831-cfa0ad5"
     networking.knative.dev/ingress-provider: istio
 webhooks:
 - admissionReviewVersions:
@@ -217,7 +217,7 @@ kind: ValidatingWebhookConfiguration
 metadata:
   name: config.webhook.istio.networking.internal.knative.dev
   labels:
-    serving.knative.dev/release: "v20200828-2555ce9"
+    serving.knative.dev/release: "v20200831-cfa0ad5"
     networking.knative.dev/ingress-provider: istio
 webhooks:
 - admissionReviewVersions:
@@ -255,7 +255,7 @@ metadata:
   name: istio-webhook-certs
   namespace: knative-serving
   labels:
-    serving.knative.dev/release: "v20200828-2555ce9"
+    serving.knative.dev/release: "v20200831-cfa0ad5"
     networking.knative.dev/ingress-provider: istio
 
 ---
@@ -279,7 +279,7 @@ metadata:
   name: config-istio
   namespace: knative-serving
   labels:
-    serving.knative.dev/release: "v20200828-2555ce9"
+    serving.knative.dev/release: "v20200831-cfa0ad5"
     networking.knative.dev/ingress-provider: istio
 data:
   _example: |
@@ -349,7 +349,7 @@ metadata:
   name: networking-istio
   namespace: knative-serving
   labels:
-    serving.knative.dev/release: "v20200828-2555ce9"
+    serving.knative.dev/release: "v20200831-cfa0ad5"
     networking.knative.dev/ingress-provider: istio
 spec:
   selector:
@@ -365,14 +365,14 @@ spec:
         sidecar.istio.io/inject: "false"
       labels:
         app: networking-istio
-        serving.knative.dev/release: "v20200828-2555ce9"
+        serving.knative.dev/release: "v20200831-cfa0ad5"
     spec:
       serviceAccountName: controller
       containers:
       - name: networking-istio
         # This is the Go import path for the binary that is containerized
         # and substituted here.
-        image: gcr.io/knative-nightly/knative.dev/net-istio/cmd/controller@sha256:6a9925f4a62ae29ffde4fe02491d27b26cec4210d4797868b072aa18c7c30f2d
+        image: gcr.io/knative-nightly/knative.dev/net-istio/cmd/controller@sha256:0b5d610527b7e5df00d02f1e2d8d8451fce055e787412bb6d566573486f777d6
         resources:
           requests:
             cpu: 30m
@@ -424,7 +424,7 @@ metadata:
   name: istio-webhook
   namespace: knative-serving
   labels:
-    serving.knative.dev/release: "v20200828-2555ce9"
+    serving.knative.dev/release: "v20200831-cfa0ad5"
     networking.knative.dev/ingress-provider: istio
 spec:
   selector:
@@ -438,14 +438,14 @@ spec:
       labels:
         app: istio-webhook
         role: istio-webhook
-        serving.knative.dev/release: "v20200828-2555ce9"
+        serving.knative.dev/release: "v20200831-cfa0ad5"
     spec:
       serviceAccountName: controller
       containers:
       - name: webhook
         # This is the Go import path for the binary that is containerized
         # and substituted here.
-        image: gcr.io/knative-nightly/knative.dev/net-istio/cmd/webhook@sha256:cf3b49d137051d6a45ce24b861519ae3506d2ac96b03bd508708a470ec2db9f0
+        image: gcr.io/knative-nightly/knative.dev/net-istio/cmd/webhook@sha256:73f91373ef04b6059617fcfec956265444d700e70383ceaed5024c8881203ac5
         resources:
           requests:
             cpu: 20m
@@ -499,7 +499,7 @@ metadata:
   namespace: knative-serving
   labels:
     role: istio-webhook
-    serving.knative.dev/release: "v20200828-2555ce9"
+    serving.knative.dev/release: "v20200831-cfa0ad5"
     networking.knative.dev/ingress-provider: istio
 spec:
   ports:
diff --git a/vendor/knative.dev/networking/pkg/apis/networking/register.go b/vendor/knative.dev/networking/pkg/apis/networking/register.go
index 9f2bdf789..1aacae7fb 100644
--- a/vendor/knative.dev/networking/pkg/apis/networking/register.go
+++ b/vendor/knative.dev/networking/pkg/apis/networking/register.go
@@ -35,6 +35,9 @@ const (
 	// Istio-based Ingress will reconcile into a VirtualService).
 	IngressClassAnnotationKey = "networking.knative.dev/ingress.class"
 
+	// DisableAutoTLSAnnotationKey is the label key attached to a namespace to indicate that
+	// AutoTLS should not be enabled for it.
+	DisableAutoTLSAnnotationKey = "networking.knative.dev/disableAutoTLS"
 	// IngressLabelKey is the label key attached to underlying network programming
 	// resources to indicate which Ingress triggered their creation.
 	IngressLabelKey = GroupName + "/ingress"
@@ -81,10 +84,6 @@ const (
 	// a wildcard certificate should be not created for it.
 	DisableWildcardCertLabelKey = "networking.knative.dev/disableWildcardCert"
 
-	// DisableAutoTLSLabelKey is the label key attached to a namespace to indicate that
-	// AutoTLS should not be enabled for it.
-	DisableAutoTLSLabelKey = "networking.knative.dev/disableAutoTLS"
-
 	// WildcardCertDomainLabelKey is the label key attached to a certificate to indicate the
 	// domain for which it was issued.
 	WildcardCertDomainLabelKey = "networking.knative.dev/wildcardDomain"
diff --git a/vendor/knative.dev/pkg/injection/sharedmain/main.go b/vendor/knative.dev/pkg/injection/sharedmain/main.go
index 526320fcd..5dacff41d 100644
--- a/vendor/knative.dev/pkg/injection/sharedmain/main.go
+++ b/vendor/knative.dev/pkg/injection/sharedmain/main.go
@@ -126,6 +126,39 @@ func GetLeaderElectionConfig(ctx context.Context) (*leaderelection.Config, error
 	return leaderelection.NewConfigFromConfigMap(leaderElectionConfigMap)
 }
 
+// EnableInjectionOrDie enables Knative Injection and starts the informers.
+// Both Context and Config are optional.
+func EnableInjectionOrDie(ctx context.Context, cfg *rest.Config) context.Context {
+	if ctx == nil {
+		ctx = signals.NewContext()
+	}
+	if cfg == nil {
+		cfg = ParseAndGetConfigOrDie()
+	}
+
+	// Respect user provided settings, but if omitted customize the default behavior.
+	if cfg.QPS == 0 {
+		cfg.QPS = rest.DefaultQPS
+	}
+	if cfg.Burst == 0 {
+		cfg.Burst = rest.DefaultBurst
+	}
+	ctx = injection.WithConfig(ctx, cfg)
+
+	ctx, informers := injection.Default.SetupInformers(ctx, cfg)
+
+	// Start the injection clients and informers.
+	logging.FromContext(ctx).Info("Starting informers...")
+	go func(ctx context.Context) {
+		if err := controller.StartInformers(ctx.Done(), informers...); err != nil {
+			logging.FromContext(ctx).Fatalw("Failed to start informers", zap.Error(err))
+		}
+		<-ctx.Done()
+	}(ctx)
+
+	return ctx
+}
+
 // Main runs the generic main flow with a new context.
 // If any of the contructed controllers are AdmissionControllers or Conversion webhooks,
 // then a webhook is started to serve them.
@@ -190,9 +223,8 @@ func MainWithConfig(ctx context.Context, component string, cfg *rest.Config, cto
 	if cfg.Burst == 0 {
 		cfg.Burst = len(ctors) * rest.DefaultBurst
 	}
-	ctx = injection.WithConfig(ctx, cfg)
 
-	ctx, informers := injection.Default.SetupInformers(ctx, cfg)
+	ctx = EnableInjectionOrDie(ctx, cfg)
 
 	logger, atomicLevel := SetupLoggerOrDie(ctx, component)
 	defer flush(logger)
@@ -246,10 +278,7 @@ func MainWithConfig(ctx context.Context, component string, cfg *rest.Config, cto
 		})
 	}
 
-	logger.Info("Starting informers...")
-	if err := controller.StartInformers(ctx.Done(), informers...); err != nil {
-		logger.Fatalw("Failed to start informers", zap.Error(err))
-	}
+	// Wait for webhook informers to sync.
 	if wh != nil {
 		wh.InformersHaveSynced()
 	}
diff --git a/vendor/knative.dev/pkg/leaderelection/chaosduck/main.go b/vendor/knative.dev/pkg/leaderelection/chaosduck/main.go
index 72e3be3d2..eeb20383f 100644
--- a/vendor/knative.dev/pkg/leaderelection/chaosduck/main.go
+++ b/vendor/knative.dev/pkg/leaderelection/chaosduck/main.go
@@ -33,11 +33,8 @@ import (
 	"k8s.io/apimachinery/pkg/util/wait"
 	"k8s.io/client-go/kubernetes"
 	kubeclient "knative.dev/pkg/client/injection/kube/client"
-	"knative.dev/pkg/controller"
-	"knative.dev/pkg/injection"
 	"knative.dev/pkg/injection/sharedmain"
 	"knative.dev/pkg/kflag"
-	"knative.dev/pkg/signals"
 	"knative.dev/pkg/system"
 )
 
@@ -118,13 +115,8 @@ func quack(ctx context.Context, kc kubernetes.Interface, component string, leade
 }
 
 func main() {
-	ctx := signals.NewContext()
+	ctx := sharedmain.EnableInjectionOrDie(nil, nil)
 
-	// We don't expect informers to be set up, but we do expect the client to get attached to ctx.
-	ctx, informers := injection.Default.SetupInformers(ctx, sharedmain.ParseAndGetConfigOrDie())
-	if err := controller.StartInformers(ctx.Done(), informers...); err != nil {
-		log.Fatalf("Failed to start informers %v", err)
-	}
 	kc := kubeclient.Get(ctx)
 
 	// Until we are shutdown, build up an index of components and kill
diff --git a/vendor/knative.dev/pkg/leaderelection/config.go b/vendor/knative.dev/pkg/leaderelection/config.go
index b9e44a7a0..8f56e53d8 100644
--- a/vendor/knative.dev/pkg/leaderelection/config.go
+++ b/vendor/knative.dev/pkg/leaderelection/config.go
@@ -118,7 +118,7 @@ type ComponentConfig struct {
 	// Autoscaler need to know the Lease names to filter out Leases which are not
 	// used for Autoscaler. Instead of exposing the names from leadelection package,
 	// we let Autoscaler to pass them in.
-	LeaseName func(i uint32) string
+	LeaseName func(i uint32) string `json:"-"`
 	// Identity is the unique string identifying a resource lock holder across
 	// all participants in an election. If not present, a new unique string will
 	// be generated to be used as identity for each BuildElector call.
diff --git a/vendor/modules.txt b/vendor/modules.txt
index c98e73870..7b27f8c8d 100644
--- a/vendor/modules.txt
+++ b/vendor/modules.txt
@@ -279,7 +279,6 @@ github.com/prometheus/client_golang/prometheus/promhttp
 ## explicit
 github.com/prometheus/client_model/go
 # github.com/prometheus/common v0.9.1
-## explicit
 github.com/prometheus/common/expfmt
 github.com/prometheus/common/internal/bitbucket.org/ww/goautoneg
 github.com/prometheus/common/log
@@ -1041,7 +1040,7 @@ k8s.io/utils/buffer
 k8s.io/utils/integer
 k8s.io/utils/pointer
 k8s.io/utils/trace
-# knative.dev/caching v0.0.0-20200827155307-479979167783
+# knative.dev/caching v0.0.0-20200831164016-8993b9e0e6f6
 ## explicit
 knative.dev/caching/config
 knative.dev/caching/pkg/apis/caching
@@ -1062,7 +1061,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-20200828035206-7f8093fa206d
+# knative.dev/networking v0.0.0-20200831163108-0195c961813b
 ## explicit
 knative.dev/networking/pkg
 knative.dev/networking/pkg/apis/networking
@@ -1089,7 +1088,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/listers/networking/v1alpha1
 knative.dev/networking/pkg/ingress
-# knative.dev/pkg v0.0.0-20200827231407-8d478d55c0e6
+# knative.dev/pkg v0.0.0-20200831210615-9c7506148742
 ## explicit
 knative.dev/pkg/apiextensions/storageversion
 knative.dev/pkg/apiextensions/storageversion/cmd/migrate
@@ -1215,7 +1214,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-20200828024406-bf34ea55d769
+# knative.dev/test-infra v0.0.0-20200828211307-9d4372c9b1c7
 ## explicit
 knative.dev/test-infra/scripts
 # sigs.k8s.io/yaml v1.2.0


Test patch:
diff --git a/.github/workflows/knative-go-test.yaml b/.github/workflows/knative-go-test.yaml
new file mode 100644
index 000000000..53985abd7
--- /dev/null
+++ b/.github/workflows/knative-go-test.yaml
@@ -0,0 +1,46 @@
+# Copyright 2020 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.
+
+name: Test
+
+on:
+  push:
+    branches: [ 'master', 'release-*' ]
+  pull_request:
+    branches: [ 'master', 'release-*' ]
+
+jobs:
+
+  test:
+    name: Test
+    strategy:
+      matrix:
+        go-version: [1.15.x]
+        platform: [ubuntu-latest]
+
+    runs-on: ${{ matrix.platform }}
+
+    steps:
+
+      - name: Set up Go ${{ matrix.go-version }}
+        uses: actions/setup-go@v2
+        with:
+          go-version: ${{ matrix.go-version }}
+        id: go
+
+      - name: Check out code
+        uses: actions/checkout@v2
+
+      - name: Test
+        run: go test -race ./...
diff --git a/pkg/autoscaler/metrics/http_scrape_client_test.go b/pkg/autoscaler/metrics/http_scrape_client_test.go
index bccd95be9..63c945615 100644
--- a/pkg/autoscaler/metrics/http_scrape_client_test.go
+++ b/pkg/autoscaler/metrics/http_scrape_client_test.go
@@ -40,28 +40,6 @@ const (
 
 var (
 	testURL = "http://test-revision-zhudex.test-namespace:9090/metrics"
-	// TODO: Use Prometheus lib to generate the following text instead of using text format directly.
-	testAverageConcurrencyContext = fmt.Sprintf(`# HELP queue_average_concurrent_requests Number of requests currently being handled by this pod
-# TYPE queue_average_concurrent_requests gauge
-queue_average_concurrent_requests{destination_namespace="test-namespace",destination_revision="test-revision",destination_pod="test-revision-1234"} %f
-`, queueAverageConcurrentRequests)
-	testQPSContext = fmt.Sprintf(`# HELP queue_requests_per_second Number of requests received since last Stat
-# TYPE queue_requests_per_second gauge
-queue_requests_per_second{destination_namespace="test-namespace",destination_revision="test-revision",destination_pod="test-revision-1234"} %d
-`, queueRequestsPerSecond)
-	testAverageProxiedConcurrenyContext = fmt.Sprintf(`# HELP queue_average_proxied_concurrent_requests Number of proxied requests currently being handled by this pod
-# TYPE queue_average_proxied_concurrent_requests gauge
-queue_average_proxied_concurrent_requests{destination_namespace="test-namespace",destination_revision="test-revision",destination_pod="test-revision-1234"} %f
-`, queueAverageProxiedConcurrentRequests)
-	testProxiedQPSContext = fmt.Sprintf(`# HELP queue_proxied_operations_per_second Number of proxied requests received since last Stat
-# TYPE queue_proxied_operations_per_second gauge
-queue_proxied_operations_per_second{destination_namespace="test-namespace",destination_revision="test-revision",destination_pod="test-revision-1234"} %d
-`, queueProxiedOperationsPerSecond)
-	testUptimeContext = fmt.Sprintf(`# HELP process_uptime The number of seconds that the process has been up
-# TYPE process_uptime gauge
-process_uptime{destination_configuration="s1",destination_namespace="default",destination_pod="s1-tdgpn-deployment-86f6459cf8-mc9mw",destination_revision="s1-tdgpn"} %f
-`, processUptime)
-	testFullContext = testAverageConcurrencyContext + testQPSContext + testAverageProxiedConcurrenyContext + testProxiedQPSContext + testUptimeContext
 
 	stat = Stat{
 		PodName:                          podName,
@@ -99,82 +77,17 @@ func TestNewHTTPScrapeClientErrorCases(t *testing.T) {
 }
 
 func TestHTTPScrapeClientScrapeHappyCaseWithOptionals(t *testing.T) {
-	for _, hClient := range []*http.Client{
-		newTestHTTPClient(makeResponse(http.StatusOK, testFullContext), nil),
-		newTestHTTPClient(makeProtoResponse(http.StatusOK, stat, network.ProtoAcceptContent), nil),
-	} {
-		sClient, err := newHTTPScrapeClient(hClient)
-		if err != nil {
-			t.Fatalf("newHTTPScrapeClient = %v, want no error", err)
-		}
-		got, err := sClient.Scrape(testURL)
-		if err != nil {
-			t.Fatalf("Scrape = %v, want no error", err)
-		}
-		if !cmp.Equal(got, stat) {
-			t.Errorf("Scraped stat mismatch; diff(-want,+got):\n%s", cmp.Diff(stat, got))
-		}
+	hClient := newTestHTTPClient(makeProtoResponse(http.StatusOK, stat, network.ProtoAcceptContent), nil)
+	sClient, err := newHTTPScrapeClient(hClient)
+	if err != nil {
+		t.Fatalf("newHTTPScrapeClient = %v, want no error", err)
 	}
-}
-
-func TestHTTPScrapeClientScrapeErrorCases(t *testing.T) {
-	testCases := []struct {
-		name            string
-		responseCode    int
-		responseErr     error
-		responseContext string
-		expectedErr     string
-	}{{
-		name:         "Non 200 return code",
-		responseCode: http.StatusForbidden,
-		expectedErr:  fmt.Sprintf(`GET request for URL %q returned HTTP status 403`, testURL),
-	}, {
-		name:         "Error got when sending request",
-		responseCode: http.StatusOK,
-		responseErr:  errors.New("upstream closed"),
-		expectedErr:  "upstream closed",
-	}, {
-		name:            "Bad response context format",
-		responseCode:    http.StatusOK,
-		responseContext: "bad context",
-		expectedErr:     "reading text format failed: text format parsing error in line 1: unexpected end of input stream",
-	}, {
-		name:            "Missing average concurrency",
-		responseCode:    http.StatusOK,
-		responseContext: testQPSContext + testAverageProxiedConcurrenyContext + testProxiedQPSContext,
-		expectedErr:     "could not find value for queue_average_concurrent_requests in response",
-	}, {
-		name:            "Missing QPS",
-		responseCode:    http.StatusOK,
-		responseContext: testAverageConcurrencyContext + testAverageProxiedConcurrenyContext + testProxiedQPSContext,
-		expectedErr:     "could not find value for queue_requests_per_second in response",
-	}, {
-		name:            "Missing average proxied concurrency",
-		responseCode:    http.StatusOK,
-		responseContext: testAverageConcurrencyContext + testQPSContext + testProxiedQPSContext,
-		expectedErr:     "could not find value for queue_average_proxied_concurrent_requests in response",
-	}, {
-		name:            "Missing proxied QPS",
-		responseCode:    http.StatusOK,
-		responseContext: testAverageConcurrencyContext + testQPSContext + testAverageProxiedConcurrenyContext,
-		expectedErr:     "could not find value for queue_proxied_operations_per_second in response",
-	}}
-
-	for _, test := range testCases {
-		t.Run(test.name, func(t *testing.T) {
-			hClient := newTestHTTPClient(makeResponse(test.responseCode, test.responseContext), test.responseErr)
-			sClient, err := newHTTPScrapeClient(hClient)
-			if err != nil {
-				t.Errorf("newHTTPScrapeClient=%v, want no error", err)
-			}
-			if _, err := sClient.Scrape(testURL); err != nil {
-				if !strings.Contains(err.Error(), test.expectedErr) {
-					t.Errorf("Got error message: %q, want to contain: %q", err.Error(), test.expectedErr)
-				}
-			} else {
-				t.Error("Expected error from newServiceScraperWithClient, got nil")
-			}
-		})
+	got, err := sClient.Scrape(testURL)
+	if err != nil {
+		t.Fatalf("Scrape = %v, want no error", err)
+	}
+	if !cmp.Equal(got, stat) {
+		t.Errorf("Scraped stat mismatch; diff(-want,+got):\n%s", cmp.Diff(stat, got))
 	}
 }
 
@@ -182,6 +95,7 @@ func TestHTTPScrapeClientScrapeProtoErrorCases(t *testing.T) {
 	testCases := []struct {
 		name         string
 		responseCode int
+		responseType string
 		responseErr  error
 		stat         Stat
 		expectedErr  string
@@ -194,11 +108,16 @@ func TestHTTPScrapeClientScrapeProtoErrorCases(t *testing.T) {
 		responseCode: http.StatusOK,
 		responseErr:  errors.New("upstream closed"),
 		expectedErr:  "upstream closed",
+	}, {
+		name:         "Wrong Content-Type",
+		responseCode: http.StatusOK,
+		responseType: "text/html",
+		expectedErr:  errUnsupportedMetricType.Error(),
 	}}
 
 	for _, test := range testCases {
 		t.Run(test.name, func(t *testing.T) {
-			hClient := newTestHTTPClient(makeProtoResponse(test.responseCode, test.stat, network.ProtoAcceptContent), test.responseErr)
+			hClient := newTestHTTPClient(makeProtoResponse(test.responseCode, test.stat, test.responseType), test.responseErr)
 			sClient, err := newHTTPScrapeClient(hClient)
 			if err != nil {
 				t.Fatalf("newHTTPScrapeClient=%v, want no error", err)
@@ -214,13 +133,6 @@ func TestHTTPScrapeClientScrapeProtoErrorCases(t *testing.T) {
 	}
 }
 
-func makeResponse(statusCode int, context string) *http.Response {
-	return &http.Response{
-		StatusCode: statusCode,
-		Body:       ioutil.NopCloser(bytes.NewBufferString(context)),
-	}
-}
-
 func makeProtoResponse(statusCode int, stat Stat, contentType string) *http.Response {
 	buffer, _ := stat.Marshal()
 	res := &http.Response{
@@ -262,21 +174,19 @@ func BenchmarkUnmarshallingProtoData(b *testing.B) {
 	benchmarks := []struct {
 		name    string
 		podName string
-	}{
-		{
-			name:    "BenchmarkStatWithEmptyPodName",
-			podName: "",
-		}, {
-			name:    "BenchmarkStatWithSmallPodName",
-			podName: "a-lzrjc-deployment-85d4b7d859-gspjs",
-		}, {
-			name:    "BenchmarkStatWithMediumPodName",
-			podName: "a-lzrjc-deployment-85d4b7d859-gspjs" + strings.Repeat("p", 50),
-		}, {
-			name:    "BenchmarkStatWithLargePodName",
-			podName: strings.Repeat("p", 253),
-		},
-	}
+	}{{
+		name:    "BenchmarkStatWithEmptyPodName",
+		podName: "",
+	}, {
+		name:    "BenchmarkStatWithSmallPodName",
+		podName: "a-lzrjc-deployment-85d4b7d859-gspjs",
+	}, {
+		name:    "BenchmarkStatWithMediumPodName",
+		podName: "a-lzrjc-deployment-85d4b7d859-gspjs" + strings.Repeat("p", 50),
+	}, {
+		name:    "BenchmarkStatWithLargePodName",
+		podName: strings.Repeat("p", 253),
+	}}
 	for _, bm := range benchmarks {
 		stat.PodName = bm.podName
 		bodyBytes, err := stat.Marshal()
diff --git a/pkg/queue/breaker_test.go b/pkg/queue/breaker_test.go
index 1a73e8a2d..394d3853a 100644
--- a/pkg/queue/breaker_test.go
+++ b/pkg/queue/breaker_test.go
@@ -94,7 +94,7 @@ func TestBreakerOverloadMixed(t *testing.T) {
 	// Bring breaker to capacity.
 	reqs.request()
 	// This happens in go-routine, so spin.
-	for len(b.sem.queue) > 0 {
+	for _, in := unpack(b.sem.state.Load()); in != 1; _, in = unpack(b.sem.state.Load()) {
 		time.Sleep(time.Millisecond * 2)
 	}
 	_, rr := b.Reserve(context.Background())
@@ -263,7 +263,7 @@ func TestSemaphoreAcquireHasCapacity(t *testing.T) {
 
 	sem := newSemaphore(1, 0)
 	tryAcquire(sem, gotChan)
-	sem.release() // Allows 1 acquire
+	sem.updateCapacity(1) // Allows 1 acquire
 
 	for i := 0; i < want; i++ {
 		select {
@@ -293,30 +293,6 @@ func TestSemaphoreRelease(t *testing.T) {
 	}
 }
 
-func TestSemaphoreReleasesSeveralReducers(t *testing.T) {
-	const wantAfterFirstrelease = 1
-	const wantAfterSecondrelease = 0
-	sem := newSemaphore(2, 2)
-	sem.acquire(context.Background())
-	sem.acquire(context.Background())
-	sem.updateCapacity(0)
-	sem.release()
-	if got := sem.Capacity(); got != wantAfterSecondrelease {
-		t.Errorf("Capacity = %d, want: %d", got, wantAfterSecondrelease)
-	}
-	if sem.reducers != wantAfterFirstrelease {
-		t.Errorf("sem.reducers = %d, want: %d", sem.reducers, wantAfterFirstrelease)
-	}
-
-	sem.release()
-	if got := sem.Capacity(); got != wantAfterSecondrelease {
-		t.Errorf("Capacity = %d, want: %d", got, wantAfterSecondrelease)
-	}
-	if sem.reducers != wantAfterSecondrelease {
-		t.Errorf("sem.reducers = %d, want: %d", sem.reducers, wantAfterSecondrelease)
-	}
-}
-
 func TestSemaphoreUpdateCapacity(t *testing.T) {
 	const initialCapacity = 1
 	sem := newSemaphore(3, initialCapacity)
@@ -330,40 +306,6 @@ func TestSemaphoreUpdateCapacity(t *testing.T) {
 	}
 }
 
-// Test the case when we add more capacity then the number of waiting reducers
-func TestSemaphoreUpdateCapacityLessThenReducers(t *testing.T) {
-	const initialCapacity = 2
-	sem := newSemaphore(2, initialCapacity)
-	sem.acquire(context.Background())
-	sem.acquire(context.Background())
-	sem.updateCapacity(initialCapacity - 2)
-	if got, want := sem.reducers, 2; got != want {
-		t.Errorf("sem.reducers = %d, want: %d", got, want)
-	}
-	sem.release()
-	sem.release()
-	sem.release()
-	if got, want := sem.reducers, 0; got != want {
-		t.Errorf("sem.reducers = %d, want: %d", got, want)
-	}
-}
-
-func TestSemaphoreUpdateCapacityConsumingReducers(t *testing.T) {
-	const initialCapacity = 2
-	sem := newSemaphore(2, initialCapacity)
-	sem.acquire(context.Background())
-	sem.acquire(context.Background())
-	sem.updateCapacity(initialCapacity - 2)
-	if got, want := sem.reducers, 2; got != want {
-		t.Errorf("sem.reducers = %d, want: %d", got, want)
-	}
-
-	sem.updateCapacity(initialCapacity)
-	if got, want := sem.reducers, 0; got != want {
-		t.Errorf("sem.reducers = %d, want: %d", got, want)
-	}
-}
-
 func TestSemaphoreUpdateCapacityOverflow(t *testing.T) {
 	sem := newSemaphore(2, 0)
 	if err := sem.updateCapacity(3); err != ErrUpdateCapacity {
@@ -379,14 +321,6 @@ func TestSemaphoreUpdateCapacityOutOfBound(t *testing.T) {
 	}
 }
 
-func TestSemaphoreUpdateCapacityBrokenState(t *testing.T) {
-	sem := newSemaphore(1, 0)
-	sem.release() // This Release is not paired with an acquire
-	if err := sem.updateCapacity(1); err != ErrUpdateCapacity {
-		t.Errorf("updateCapacity = %v, want: %v", err, ErrUpdateCapacity)
-	}
-}
-
 func TestSemaphoreUpdateCapacityDoNothing(t *testing.T) {
 	sem := newSemaphore(1, 1)
 	if err := sem.updateCapacity(1); err != nil {
@@ -394,6 +328,17 @@ func TestSemaphoreUpdateCapacityDoNothing(t *testing.T) {
 	}
 }
 
+func TestPackUnpack(t *testing.T) {
+	wantL := uint64(256)
+	wantR := uint64(513)
+
+	gotL, gotR := unpack(pack(wantL, wantR))
+
+	if gotL != wantL || gotR != wantR {
+		t.Fatalf("Got %d, %d want %d, %d", gotL, gotR, wantL, wantR)
+	}
+}
+
 func tryAcquire(sem *semaphore, gotChan chan struct{}) {
 	go func() {
 		// blocking until someone puts the token into the semaphore
diff --git a/pkg/reconciler/autoscaling/kpa/kpa_test.go b/pkg/reconciler/autoscaling/kpa/kpa_test.go
index d3bb678d3..24419ec38 100644
--- a/pkg/reconciler/autoscaling/kpa/kpa_test.go
+++ b/pkg/reconciler/autoscaling/kpa/kpa_test.go
@@ -1048,7 +1048,7 @@ func TestReconcile(t *testing.T) {
 			kpa(testNamespace, testRevision, withScales(0, -1), WithReachabilityReachable,
 				WithPAMetricsService(privateSvc), WithPASKSNotReady(noPrivateServiceName)),
 			// SKS won't be ready bc no ready endpoints, but private service name will be populated.
-			sks(testNamespace, testRevision, WithDeployRef(deployName), WithPrivateService),
+			sks(testNamespace, testRevision, WithDeployRef(deployName), WithPrivateService, WithPubService),
 			metric(testNamespace, testRevision),
 			deploy(testNamespace, testRevision, func(d *appsv1.Deployment) {
 				d.Spec.Replicas = ptr.Int32(0)
@@ -1056,6 +1056,30 @@ func TestReconcile(t *testing.T) {
 		},
 		WantStatusUpdates: []clientgotesting.UpdateActionImpl{{
 			Object: kpa(testNamespace, testRevision, markScaleTargetInitialized,
+				WithNoTraffic(noTrafficReason, "The target is not receiving traffic."),
+				withScales(0, -1), WithReachabilityReachable,
+				WithPAMetricsService(privateSvc), WithObservedGeneration(1),
+				WithPASKSNotReady(""), WithPAStatusService(testRevision),
+			),
+		}},
+	}, {
+		Name: "initial scale zero: sks ServiceName empty",
+		Key:  key,
+		Ctx: context.WithValue(context.WithValue(context.Background(), asConfigKey{}, initialScaleZeroASConfig()), deciderKey{},
+			decider(testNamespace, testRevision, -1, /* desiredScale */
+				0 /* ebc */, scaling.MinActivators)),
+		Objects: []runtime.Object{
+			kpa(testNamespace, testRevision, withScales(0, -1), WithReachabilityReachable,
+				WithPAMetricsService(privateSvc), WithPASKSNotReady(noPrivateServiceName)),
+			// SKS won't be ready bc no ready endpoints, but private service name will be populated.
+			sks(testNamespace, testRevision, WithDeployRef(deployName), WithPrivateService),
+			metric(testNamespace, testRevision),
+			deploy(testNamespace, testRevision, func(d *appsv1.Deployment) {
+				d.Spec.Replicas = ptr.Int32(0)
+			}),
+		},
+		WantStatusUpdates: []clientgotesting.UpdateActionImpl{{
+			Object: kpa(testNamespace, testRevision,
 				WithNoTraffic(noTrafficReason, "The target is not receiving traffic."),
 				withScales(0, -1), WithReachabilityReachable,
 				WithPAMetricsService(privateSvc), WithObservedGeneration(1),
diff --git a/pkg/reconciler/route/resources/labels/labels_test.go b/pkg/reconciler/route/resources/labels/labels_test.go
index 24e16c502..a5f8a68bd 100644
--- a/pkg/reconciler/route/resources/labels/labels_test.go
+++ b/pkg/reconciler/route/resources/labels/labels_test.go
@@ -24,47 +24,83 @@ import (
 	"knative.dev/serving/pkg/apis/serving"
 )
 
-func TestDeleteLabel(t *testing.T) {
+func TestIsObjectLocalVisibility(t *testing.T) {
 	tests := []struct {
-		name     string
-		meta     *v1.ObjectMeta
-		key      string
-		expected v1.ObjectMeta
-	}{
-		{
-			name:     "No labels in object meta",
-			meta:     &v1.ObjectMeta{},
-			key:      "key",
-			expected: v1.ObjectMeta{},
+		name string
+		meta *v1.ObjectMeta
+		want bool
+	}{{
+		name: "nil",
+		meta: &v1.ObjectMeta{},
+	}, {
+		name: "empty labels",
+		meta: &v1.ObjectMeta{
+			Labels: map[string]string{},
+		},
+	}, {
+		name: "no matching labels",
+		meta: &v1.ObjectMeta{
+			Labels: map[string]string{"frankie-goes": "to-hollywood"},
 		},
-		{
-			name: "No matching key",
-			meta: &v1.ObjectMeta{
-				Labels: map[string]string{"some label": "some value"},
-			},
-			key: "unknown",
-			expected: v1.ObjectMeta{
-				Labels: map[string]string{"some label": "some value"},
-			},
+	}, {
+		name: "false",
+		meta: &v1.ObjectMeta{
+			Labels: map[string]string{serving.VisibilityLabelKey: ""},
 		},
-		{
-			name: "Has matching key",
-			meta: &v1.ObjectMeta{
-				Labels: map[string]string{"some label": "some value"},
-			},
-			key: "some label",
-			expected: v1.ObjectMeta{
-				Labels: map[string]string{},
-			},
+	}, {
+		name: "true",
+		meta: &v1.ObjectMeta{
+			Labels: map[string]string{serving.VisibilityLabelKey: "set"},
 		},
+		want: true,
+	}}
+
+	for _, test := range tests {
+		t.Run(test.name, func(t *testing.T) {
+			if got, want := IsObjectLocalVisibility(test.meta), test.want; got != want {
+				t.Errorf("IsObjectLocalVisibility = %v, want: %v", got, want)
+			}
+		})
 	}
+}
+
+func TestDeleteLabel(t *testing.T) {
+	tests := []struct {
+		name string
+		meta *v1.ObjectMeta
+		key  string
+		want v1.ObjectMeta
+	}{{
+		name: "No labels in object meta",
+		meta: &v1.ObjectMeta{},
+		key:  "key",
+		want: v1.ObjectMeta{},
+	}, {
+		name: "No matching key",
+		meta: &v1.ObjectMeta{
+			Labels: map[string]string{"some label": "some value"},
+		},
+		key: "unknown",
+		want: v1.ObjectMeta{
+			Labels: map[string]string{"some label": "some value"},
+		},
+	}, {
+		name: "Has matching key",
+		meta: &v1.ObjectMeta{
+			Labels: map[string]string{"some label": "some value"},
+		},
+		key: "some label",
+		want: v1.ObjectMeta{
+			Labels: map[string]string{},
+		},
+	}}
 	for _, tt := range tests {
 		t.Run(tt.name, func(t *testing.T) {
 			DeleteLabel(tt.meta, tt.key)
 
-			if !cmp.Equal(tt.expected, *tt.meta) {
+			if !cmp.Equal(tt.want, *tt.meta) {
 				t.Errorf("DeleteLabel (-want, +got) = %v",
-					cmp.Diff(tt.expected, *tt.meta))
+					cmp.Diff(tt.want, *tt.meta))
 			}
 		})
 	}
@@ -76,48 +112,43 @@ func TestSetLabel(t *testing.T) {
 		meta  *v1.ObjectMeta
 		key   string
 		value string
-
-		expected v1.ObjectMeta
-	}{
-		{
-			name:  "No labels in object meta",
-			meta:  &v1.ObjectMeta{},
-			key:   "key",
-			value: "value",
-			expected: v1.ObjectMeta{
-				Labels: map[string]string{"key": "value"},
-			},
+		want  v1.ObjectMeta
+	}{{
+		name:  "No labels in object meta",
+		meta:  &v1.ObjectMeta{},
+		key:   "key",
+		value: "value",
+		want: v1.ObjectMeta{
+			Labels: map[string]string{"key": "value"},
 		},
-		{
-			name: "Empty labels",
-			meta: &v1.ObjectMeta{
-				Labels: map[string]string{},
-			},
-			key:   "key",
-			value: "value",
-			expected: v1.ObjectMeta{
-				Labels: map[string]string{"key": "value"},
-			},
+	}, {
+		name: "Empty labels",
+		meta: &v1.ObjectMeta{
+			Labels: map[string]string{},
 		},
-		{
-			name: "Conflicting labels",
-			meta: &v1.ObjectMeta{
-				Labels: map[string]string{"key": "old value"},
-			},
-			key:   "key",
-			value: "new value",
-			expected: v1.ObjectMeta{
-				Labels: map[string]string{"key": "new value"},
-			},
+		key:   "key",
+		value: "value",
+		want: v1.ObjectMeta{
+			Labels: map[string]string{"key": "value"},
 		},
-	}
+	}, {
+		name: "Conflicting labels",
+		meta: &v1.ObjectMeta{
+			Labels: map[string]string{"key": "old value"},
+		},
+		key:   "key",
+		value: "new value",
+		want: v1.ObjectMeta{
+			Labels: map[string]string{"key": "new value"},
+		},
+	}}
 	for _, tt := range tests {
 		t.Run(tt.name, func(t *testing.T) {
 			SetLabel(tt.meta, tt.key, tt.value)
 
-			if !cmp.Equal(tt.expected, *tt.meta) {
+			if !cmp.Equal(tt.want, *tt.meta) {
 				t.Errorf("DeleteLabel (-want, +got) = %v",
-					cmp.Diff(tt.expected, *tt.meta))
+					cmp.Diff(tt.want, *tt.meta))
 			}
 		})
 	}
@@ -128,17 +159,16 @@ func TestSetVisibility(t *testing.T) {
 		name           string
 		meta           *v1.ObjectMeta
 		isClusterLocal bool
-		expected       v1.ObjectMeta
+		want           v1.ObjectMeta
 	}{{
 		name:           "Set cluster local true",
 		meta:           &v1.ObjectMeta{},
 		isClusterLocal: true,
-		expected:       v1.ObjectMeta{Labels: map[string]string{serving.VisibilityLabelKey: serving.VisibilityClusterLocal}},
+		want:           v1.ObjectMeta{Labels: map[string]string{serving.VisibilityLabelKey: serving.VisibilityClusterLocal}},
 	}, {
-		name:           "Set cluster local false",
-		meta:           &v1.ObjectMeta{Labels: map[string]string{serving.VisibilityLabelKey: serving.VisibilityClusterLocal}},
-		isClusterLocal: false,
-		expected:       v1.ObjectMeta{Labels: map[string]string{}},
+		name: "Set cluster local false",
+		meta: &v1.ObjectMeta{Labels: map[string]string{serving.VisibilityLabelKey: serving.VisibilityClusterLocal}},
+		want: v1.ObjectMeta{Labels: map[string]string{}},
 	}}
 	for _, tt := range tests {
 		t.Run(tt.name, func(t *testing.T) {
diff --git a/test/e2e-common.sh b/test/e2e-common.sh
index 1426b02ff..f9d766343 100644
--- a/test/e2e-common.sh
+++ b/test/e2e-common.sh
@@ -165,7 +165,7 @@ function build_knative_from_source() {
   eval "${ENV_OUTPUT}"
 }
 
-# Installs Knative Serving in the current cluster, and waits for it to be ready.
+# Installs Knative Serving in the current cluster.
 # If no parameters are passed, installs the current source-based build, unless custom
 # YAML files were passed using the --custom-yamls flag.
 # Parameters: $1 - Knative Serving version "HEAD" or "latest-release". Default is "HEAD".
@@ -186,7 +186,7 @@ function install_knative_serving() {
   done
 }
 
-# Installs Knative Serving in the current cluster, and waits for it to be ready.
+# Installs Knative Serving in the current cluster.
 # If no parameters are passed, installs the current source-based build.
 # Parameters: $1 - Knative Serving version "HEAD" or "latest-release".
 #             $2 - Knative Monitoring YAML file (optional)
diff --git a/test/e2e/autoscale.go b/test/e2e/autoscale.go
index 3564f4ec1..ddb73b12c 100644
--- a/test/e2e/autoscale.go
+++ b/test/e2e/autoscale.go
@@ -287,7 +287,7 @@ func numberOfReadyPods(ctx *testContext) (float64, error) {
 	return float64(resources.ReadyAddressCount(eps)), nil
 }
 
-func checkPodScale(ctx *testContext, targetPods, minPods, maxPods float64, duration time.Duration) error {
+func checkPodScale(ctx *testContext, targetPods, minPods, maxPods float64, duration time.Duration, quick bool) error {
 	// Short-circuit traffic generation once we exit from the check logic.
 	done := time.After(duration)
 	ticker := time.NewTicker(2 * time.Second)
@@ -311,8 +311,14 @@ func checkPodScale(ctx *testContext, targetPods, minPods, maxPods float64, durat
 			// A quick test succeeds when the number of pods scales up to `targetPods`
 			// (and, for sanity check, no more than `maxPods`).
 			if got >= targetPods && got <= maxPods {
-				ctx.t.Logf("Got %v replicas, reached target of %v, exiting early", got, targetPods)
-				return nil
+				if quick {
+					// A quick test succeeds when the number of pods scales up to `targetPods`
+					// (and, for sanity check, no more than `maxPods`).
+					if got >= targetPods && got <= maxPods {
+						ctx.t.Logf("Quick Mode: got %v >= %v", got, targetPods)
+						return nil
+					}
+				}
 			}
 			if minPods < targetPods-1 {
 				// Increase `minPods`, but leave room to reduce flakiness.
@@ -363,7 +369,7 @@ func assertAutoscaleUpToNumPods(ctx *testContext, curPods, targetPods float64, d
 
 	grp.Go(func() error {
 		defer close(stopChan)
-		return checkPodScale(ctx, targetPods, minPods, maxPods, duration)
+		return checkPodScale(ctx, targetPods, minPods, maxPods, duration, quick)
 	})
 
 	if err := grp.Wait(); err != nil {
diff --git a/test/e2e/grpc_test.go b/test/e2e/grpc_test.go
index 6d0baf6cb..6f52305b6 100644
--- a/test/e2e/grpc_test.go
+++ b/test/e2e/grpc_test.go
@@ -259,7 +259,7 @@ func assertGRPCAutoscaleUpToNumPods(ctx *testContext, curPods, targetPods float6
 
 	grp.Go(func() error {
 		defer close(stopChan)
-		return checkPodScale(ctx, targetPods, minPods, maxPods, duration)
+		return checkPodScale(ctx, targetPods, minPods, maxPods, duration, true /* quick */)
 	})
 
 	if err := grp.Wait(); err != nil {
diff --git a/test/e2e/initial_scale_test.go b/test/e2e/initial_scale_test.go
index 08f52afd5..63fbb5212 100644
--- a/test/e2e/initial_scale_test.go
+++ b/test/e2e/initial_scale_test.go
@@ -36,6 +36,7 @@ import (
 // the revision level. This test runs after the cluster wide flag allow-zero-initial-scale
 // is set to true.
 func TestInitScaleZero(t *testing.T) {
+	t.Skip("Enable once #9245 is fixed")
 	t.Parallel()
 
 	clients := Setup(t)
diff --git a/third_party/contour-latest/net-contour.yaml b/third_party/contour-latest/net-contour.yaml
index 90d36564e..a45ea95e0 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: "v20200828-96e90669"
+    serving.knative.dev/release: "v20200831-3e31139c"
 data:
   _example: |
     ################################
@@ -100,7 +100,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:00a4d097bb2bbd271fe109a3e63eda66792f45bfb1ef4a5daf039f25a9a2ae75
+        image: gcr.io/knative-nightly/knative.dev/net-contour/cmd/controller@sha256:b9e61aa34de4e6790714c5f8aeabc0dc2771f67a51e9d77552631e3cec8500c2
         resources:
           requests:
             cpu: 40m
diff --git a/vendor/knative.dev/pkg/metrics/metricstest/metricstest.go b/vendor/knative.dev/pkg/metrics/metricstest/metricstest.go
index ca0c82ec2..f63e34a52 100644
--- a/vendor/knative.dev/pkg/metrics/metricstest/metricstest.go
+++ b/vendor/knative.dev/pkg/metrics/metricstest/metricstest.go
@@ -91,10 +91,10 @@ func CheckDistributionData(t test.T, name string, wantTags map[string]string, ex
 			t.Error("reporter count wrong", "metric", name, "got", s.Count, "want", expectedCount)
 		}
 		if s.Min != expectedMin {
-			t.Error("reporter count wrong", "metric", name, "got", s.Min, "want", expectedMin)
+			t.Error("reporter min wrong", "metric", name, "got", s.Min, "want", expectedMin)
 		}
 		if s.Max != expectedMax {
-			t.Error("reporter count wrong", "metric", name, "got", s.Max, "want", expectedMax)
+			t.Error("reporter max wrong", "metric", name, "got", s.Max, "want", expectedMax)
 		}
 	}
 }
diff --git a/vendor/knative.dev/test-infra/scripts/e2e-tests.sh b/vendor/knative.dev/test-infra/scripts/e2e-tests.sh
index e644a5c33..7ec4bd7f3 100644
--- a/vendor/knative.dev/test-infra/scripts/e2e-tests.sh
+++ b/vendor/knative.dev/test-infra/scripts/e2e-tests.sh
@@ -51,11 +51,8 @@ function setup_test_cluster() {
   set -o errexit
   set -o pipefail
 
-  header "Test cluster setup"
-  kubectl get nodes
-
   header "Setting up test cluster"
-
+  kubectl get nodes
   # Set the actual project the test cluster resides in
   # It will be a project assigned by Boskos if test is running on Prow,
   # otherwise will be ${E2E_GCP_PROJECT_ID} set up by user.
@@ -63,21 +60,17 @@ function setup_test_cluster() {
   export E2E_PROJECT_ID
   readonly E2E_PROJECT_ID
 
-  local k8s_user
-  k8s_user=$(gcloud config get-value core/account)
   local k8s_cluster
   k8s_cluster=$(kubectl config current-context)
 
   is_protected_cluster "${k8s_cluster}" && \
     abort "kubeconfig context set to ${k8s_cluster}, which is forbidden"
 
-  # If cluster admin role isn't set, this is a brand new cluster
-  # Setup the admin role and also KO_DOCKER_REPO if it is a GKE cluster
-  if [[ -z "$(kubectl get clusterrolebinding cluster-admin-binding 2> /dev/null)" && "${k8s_cluster}" =~ ^gke_.* ]]; then
-    acquire_cluster_admin_role "${k8s_user}" "${E2E_CLUSTER_NAME}" "${E2E_GKE_CLUSTER_REGION}" "${E2E_GKE_CLUSTER_ZONE}"
-    # Incorporate an element of randomness to ensure that each run properly publishes images.
-    export KO_DOCKER_REPO=gcr.io/${E2E_PROJECT_ID}/${REPO_NAME}-e2e-img/${RANDOM}
-  fi
+  # Acquire cluster admin role for the current user.
+  acquire_cluster_admin_role "${k8s_cluster}"
+
+  # Setup KO_DOCKER_REPO if it is a GKE cluster. Incorporate an element of randomness to ensure that each run properly publishes images.
+  [[ "${k8s_cluster}" =~ ^gke_.* ]] && export KO_DOCKER_REPO=gcr.io/${E2E_PROJECT_ID}/${REPO_NAME}-e2e-img/${RANDOM}
 
   # Safety checks
   is_protected_gcr "${KO_DOCKER_REPO}" && \
@@ -92,7 +85,7 @@ function setup_test_cluster() {
   export KO_DATA_PATH="${REPO_ROOT_DIR}/.git"
 
   # Do not run teardowns if we explicitly want to skip them.
-  (( ! SKIP_TEARDOWNS )) && trap teardown_test_resources EXIT
+  (( ! SKIP_TEARDOWNS )) && add_trap teardown_test_resources EXIT
 
   # Handle failures ourselves, so we can dump useful info.
   set +o errexit
@@ -130,6 +123,7 @@ function fail_test() {
 SKIP_TEARDOWNS=0
 SKIP_ISTIO_ADDON=0
 E2E_SCRIPT=""
+CLOUD_PROVIDER="gke"
 
 # Parse flags and initialize the test cluster.
 function initialize() {
@@ -166,6 +160,7 @@ function initialize() {
         [[ $# -ge 2 ]] || abort "missing parameter after $1"
         shift
         case ${parameter} in
+          --cloud-provider) CLOUD_PROVIDER="$1" ;;
           --kubetest2-flag) extra_kubetest2_flags+=("$1") ;;
           --cluster-creation-flag) extra_cluster_creation_flags+=("$1") ;;
           *) abort "unknown option ${parameter}" ;;
@@ -186,7 +181,7 @@ function initialize() {
   readonly SKIP_TEARDOWNS
 
   if (( ! run_tests )); then
-    create_gke_test_cluster extra_kubetest2_flags extra_cluster_creation_flags e2e_script_command
+    create_test_cluster "${CLOUD_PROVIDER}" extra_kubetest2_flags extra_cluster_creation_flags e2e_script_command
   else
     setup_test_cluster
   fi
diff --git a/vendor/knative.dev/test-infra/scripts/infra-library.sh b/vendor/knative.dev/test-infra/scripts/infra-library.sh
index bb702d3ca..ebdae3f29 100644
--- a/vendor/knative.dev/test-infra/scripts/infra-library.sh
+++ b/vendor/knative.dev/test-infra/scripts/infra-library.sh
@@ -106,6 +106,7 @@ function dump_cluster_state() {
 }
 
 # On a Prow job, save some metadata about the test for Testgrid.
+# Parameters: $1 - cluster provider name
 function save_metadata() {
   (( ! IS_PROW )) && return
   local geo_key="Region"
@@ -116,6 +117,7 @@ function save_metadata() {
   fi
   local cluster_version
   cluster_version="$(kubectl version --short=true)"
+  run_kntest metadata set --key="E2E:Provider" --value="$1"
   run_kntest metadata set --key="E2E:${geo_key}" --value="${geo_value}"
   run_kntest metadata set --key="E2E:Machine" --value="${E2E_GKE_CLUSTER_MACHINE}"
   run_kntest metadata set --key="E2E:Version" --value="${cluster_version}"
@@ -123,30 +125,74 @@ function save_metadata() {
   run_kntest metadata set --key="E2E:MaxNodes" --value="${E2E_MAX_CLUSTER_NODES}"
 }
 
-# Create a GKE test cluster with kubetest2 and call the current script again.
-# Parameters: $1 - extra kubetest2 flags
-#             $2 - extra cluster creation flags
-#             $3 - test command to run by the kubetest2 tester
-function create_gke_test_cluster() {
+# Sets the current user as cluster admin for the given cluster.
+# Parameters: $1 - cluster context name
+function acquire_cluster_admin_role() {
+  if [[ -z "$(kubectl get clusterrolebinding cluster-admin-binding 2> /dev/null)" ]]; then
+    if [[ "$1" =~ ^gke_.* ]]; then
+      kubectl create clusterrolebinding cluster-admin-binding \
+        --clusterrole=cluster-admin --user="$(gcloud config get-value core/account)"
+    else
+      kubectl create clusterrolebinding cluster-admin-binding-"${USER}" \
+        --clusterrole=cluster-admin --user="${USER}"
+    fi
+  fi
+}
+
+# Create a test cluster and run the tests if provided.
+# Parameters: $1 - cluster provider name, e.g. gke
+#             $2 - extra kubetest2 flags
+#             $3 - extra cluster creation flags
+#             $4 - test command to run by the kubetest2 tester
+function create_test_cluster() {
   # Fail fast during setup.
   set -o errexit
   set -o pipefail
-  local -n _extra_kubetest2_flags=$1
-  local -n _extra_cluster_creation_flags=$2
-  local -n _tester_command=$3
 
   if function_exists cluster_setup; then
     cluster_setup || fail_test "cluster setup failed"
   fi
 
+  case "$1" in
+    gke) create_gke_test_cluster "$2" "$3" "$4" ;;
+    kind) create_kind_test_cluster "$2" "$3" "$4" ;;
+    *) echo "unsupported provider: $1"; exit 1 ;;
+  esac
+
+  local result="$?"
+  # Ignore any errors below, this is a best-effort cleanup and shouldn't affect the test result.
+  set +o errexit
+  set +o pipefail
+  function_exists cluster_teardown && cluster_teardown
+  echo "Artifacts were written to ${ARTIFACTS}"
+  echo "Test result code is ${result}"
+  exit "${result}"
+}
+
+# Create a KIND test cluster with kubetest2 and run the test command.
+# Parameters: $1 - extra kubetest2 flags
+#             $2 - extra cluster creation flags
+#             $3 - test command to run by the kubetest2 tester
+function create_kind_test_cluster() {
+  # TODO(chizhg): implement with kubetest2
+  return 0
+}
+
+# Create a GKE test cluster with kubetest2 and run the test command.
+# Parameters: $1 - extra kubetest2 flags
+#             $2 - extra cluster creation flags
+#             $3 - test command to run by the kubetest2 tester after the cluster is created (optional)
+function create_gke_test_cluster() {
+  local -n _extra_kubetest2_flags=$1
+  local -n _extra_cluster_creation_flags=$2
+
   echo "Cluster will have a minimum of ${E2E_MIN_CLUSTER_NODES} and a maximum of ${E2E_MAX_CLUSTER_NODES} nodes."
   local _kubetest2_flags=(
     "gke"
     "--create-command=${E2E_GKE_COMMAND_GROUP} container clusters create --quiet --enable-autoscaling
       --min-nodes=${E2E_MIN_CLUSTER_NODES} --max-nodes=${E2E_MAX_CLUSTER_NODES}
       --cluster-version=${E2E_CLUSTER_VERSION}
-      --scopes=${E2E_GKE_SCOPES} --enable-basic-auth --no-issue-client-certificate
-      --no-enable-ip-alias --no-enable-autoupgrade
+      --scopes=${E2E_GKE_SCOPES} --no-enable-autoupgrade
       ${_extra_cluster_creation_flags[@]}"
     "--environment=${E2E_GKE_ENVIRONMENT}"
     "--cluster-name=${E2E_CLUSTER_NAME}"
@@ -163,7 +209,7 @@ function create_gke_test_cluster() {
     _kubetest2_flags+=("--project=${gcloud_project}")
     echo "gcloud project is ${gcloud_project}"
   else
-    echo "Using boskos for the test cluster"
+    echo "Using boskos for provisioning the GCP project to create the test cluster"
   fi
 
   if (( IS_BOSKOS )); then
@@ -176,24 +222,19 @@ function create_gke_test_cluster() {
   fi
 
   # Create cluster and run the tests
-  create_gke_test_cluster_with_retries _kubetest2_flags _tester_command
-  local result="$?"
-  # Ignore any errors below, this is a best-effort cleanup and shouldn't affect the test result.
-  set +o errexit
-  set +o pipefail
-  function_exists cluster_teardown && cluster_teardown
-  echo "Artifacts were written to ${ARTIFACTS}"
-  echo "Test result code is ${result}"
-  exit "${result}"
+  create_gke_test_cluster_with_retries _kubetest2_flags "$3"
 }
 
 # TODO(chizhg): move this to kubetest2 gke deployer.
 # Retry backup regions/zones if cluster creations failed due to stockout.
 # Parameters: $1 - kubetest2 flags other than geo flag
-#             $2 - test command to run by the kubetest2 tester
+#             $2 - test command to run by the kubetest2 tester after the cluster is created (optional)
 function create_gke_test_cluster_with_retries() {
   local -n kubetest2_flags=$1
-  local -n tester_command=$2
+  local -n command=$2
+  if (( ${#command[@]} )); then
+    tester_command=("--test=exec" "--" "${command[@]}")
+  fi
   local cluster_creation_log=/tmp/${REPO_NAME}-cluster_creation-log
   # zone_not_provided is a placeholder for e2e_cluster_zone to make for loop below work
   local zone_not_provided="zone_not_provided"
@@ -220,19 +261,16 @@ function create_gke_test_cluster_with_retries() {
 
       header "Creating test cluster ${E2E_CLUSTER_VERSION} in ${cluster_creation_zone}"
       if run_go_tool k8s-sigs.io/kubetest2 \
-        kubetest2 "${kubetest2_flags[@]}" --region="${cluster_creation_zone}" \
-          --test=exec -- "${tester_command[@]}" 2>&1 \
+        kubetest2 "${kubetest2_flags[@]}" --region="${cluster_creation_zone}" "${tester_command[@]}" 2>&1 \
          | tee "${cluster_creation_log}"; then
         # Save some metadata about cluster creation for using in prow and testgrid
-        save_metadata
+        save_metadata "gke"
         return 0
       fi
-      # Retry if cluster creation failed because of:
-      # - stockout (https://github.com/knative/test-infra/issues/592)
-      # - latest GKE not available in this region/zone yet (https://github.com/knative/test-infra/issues/694)
+      # Retry if cluster creation failed because of stockout (https://github.com/knative/test-infra/issues/592)
+      # or Nodes failing to start correctly.
+      # shellcheck disable=SC2143
       [[ -z "$(grep -Fo 'does not have enough resources available to fulfill' "${cluster_creation_log}")" \
-          && -z "$(grep -Fo 'ResponseError: code=400, message=No valid versions with the prefix' "${cluster_creation_log}")" \
-          && -z "$(grep -Po 'ResponseError: code=400, message=Master version "[0-9a-z\-\.]+" is unsupported' "${cluster_creation_log}")"  \
           && -z "$(grep -Po 'only \d+ nodes out of \d+ have registered; this is likely due to Nodes failing to start correctly' "${cluster_creation_log}")" ]] \
           && return 1
     done
diff --git a/vendor/knative.dev/test-infra/scripts/library.sh b/vendor/knative.dev/test-infra/scripts/library.sh
index 82cc1bdf5..26603fc1e 100644
--- a/vendor/knative.dev/test-infra/scripts/library.sh
+++ b/vendor/knative.dev/test-infra/scripts/library.sh
@@ -327,44 +327,6 @@ function dump_app_logs() {
   done
 }
 
-# Sets the given user as cluster admin.
-# Parameters: $1 - user
-#             $2 - cluster name
-#             $3 - cluster region
-#             $4 - cluster zone, optional
-function acquire_cluster_admin_role() {
-  echo "Acquiring cluster-admin role for user '$1'"
-  local geoflag="--region=$3"
-  [[ -n $4 ]] && geoflag="--zone=$3-$4"
-  # Get the password of the admin and use it, as the service account (or the user)
-  # might not have the necessary permission.
-  local password=$(gcloud --format="value(masterAuth.password)" \
-      container clusters describe $2 ${geoflag})
-  if [[ -n "${password}" ]]; then
-    # Cluster created with basic authentication
-    kubectl config set-credentials cluster-admin \
-        --username=admin --password=${password}
-  else
-    local cert=$(mktemp)
-    local key=$(mktemp)
-    echo "Certificate in ${cert}, key in ${key}"
-    gcloud --format="value(masterAuth.clientCertificate)" \
-      container clusters describe $2 ${geoflag} | base64 --decode > ${cert}
-    gcloud --format="value(masterAuth.clientKey)" \
-      container clusters describe $2 ${geoflag} | base64 --decode > ${key}
-    kubectl config set-credentials cluster-admin \
-      --client-certificate=${cert} --client-key=${key}
-  fi
-  kubectl config set-context $(kubectl config current-context) \
-      --user=cluster-admin
-  kubectl create clusterrolebinding cluster-admin-binding \
-      --clusterrole=cluster-admin \
-      --user=$1
-  # Reset back to the default account
-  gcloud container clusters get-credentials \
-      $2 ${geoflag} --project $(gcloud config get-value project)
-}
-
 # Run a command through tee and capture its output.
 # Parameters: $1 - file where the output will be stored.
 #             $2... - command to run.