Repository: knative/serving
Pull request #8859 (2020-07-30 21:44:09)

Pod random shuffle for scraping, pt I
In this first part of pod random shuffling, we are introducing pod filters and transformers
that permit apply various filtering and final transformation to the pod list.
The current get pod IPs by age is simply re-implemented using this new construct (covering all the testing).

In part II, we will be introducing a possibility to cutoff young pods, if possible, and then random shuffling
the rest, rather than sorting them to increase diversity of pods scraped in the metric collector.

/assign @julz @yanweiguo  @markusthoemmes 

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

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

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

Patch:
diff --git a/pkg/activator/handler/metrics.go b/pkg/activator/handler/metrics.go
index 812c25ed7..a1ae2c3b9 100644
--- a/pkg/activator/handler/metrics.go
+++ b/pkg/activator/handler/metrics.go
@@ -22,6 +22,7 @@ import (
 
 	"go.opencensus.io/stats"
 	"go.opencensus.io/stats/view"
+	"go.opencensus.io/tag"
 )
 
 var (
@@ -56,21 +57,19 @@ func register() {
 			Description: "Concurrent requests that are routed to Activator",
 			Measure:     requestConcurrencyM,
 			Aggregation: view.LastValue(),
-			TagKeys:     append(metrics.CommonRevisionKeys, metrics.PodTagKey, metrics.ContainerTagKey),
+			TagKeys:     []tag.Key{metrics.PodTagKey, metrics.ContainerTagKey},
 		},
 		&view.View{
 			Description: "The number of requests that are routed to Activator",
 			Measure:     requestCountM,
 			Aggregation: view.Count(),
-			TagKeys: append(metrics.CommonRevisionKeys, metrics.PodTagKey, metrics.ContainerTagKey,
-				metrics.ResponseCodeKey, metrics.ResponseCodeClassKey),
+			TagKeys:     []tag.Key{metrics.PodTagKey, metrics.ContainerTagKey, metrics.ResponseCodeKey, metrics.ResponseCodeClassKey},
 		},
 		&view.View{
 			Description: "The response time in millisecond",
 			Measure:     responseTimeInMsecM,
 			Aggregation: defaultLatencyDistribution,
-			TagKeys: append(metrics.CommonRevisionKeys, metrics.PodTagKey, metrics.ContainerTagKey,
-				metrics.ResponseCodeKey, metrics.ResponseCodeClassKey),
+			TagKeys:     []tag.Key{metrics.PodTagKey, metrics.ContainerTagKey, metrics.ResponseCodeKey, metrics.ResponseCodeClassKey},
 		},
 	); err != nil {
 		panic(err)
diff --git a/pkg/autoscaler/metrics/stats_scraper.go b/pkg/autoscaler/metrics/stats_scraper.go
index 9bcfee711..84c95622d 100644
--- a/pkg/autoscaler/metrics/stats_scraper.go
+++ b/pkg/autoscaler/metrics/stats_scraper.go
@@ -81,7 +81,6 @@ func init() {
 			Description: "The time to scrape metrics in milliseconds",
 			Measure:     scrapeTimeM,
 			Aggregation: view.Distribution(pkgmetrics.Buckets125(1, 100000)...),
-			TagKeys:     metrics.CommonRevisionKeys,
 		},
 	); err != nil {
 		panic(err)
diff --git a/pkg/autoscaler/scaling/metrics.go b/pkg/autoscaler/scaling/metrics.go
index f918cf710..e9d0e9732 100644
--- a/pkg/autoscaler/scaling/metrics.go
+++ b/pkg/autoscaler/scaling/metrics.go
@@ -18,7 +18,6 @@ package scaling
 
 import (
 	pkgmetrics "knative.dev/pkg/metrics"
-	"knative.dev/serving/pkg/metrics"
 
 	"go.opencensus.io/stats"
 	"go.opencensus.io/stats/view"
@@ -76,55 +75,46 @@ func register() {
 			Description: "Number of pods autoscaler wants to allocate",
 			Measure:     desiredPodCountM,
 			Aggregation: view.LastValue(),
-			TagKeys:     metrics.CommonRevisionKeys,
 		},
 		&view.View{
 			Description: "Average of requests count over the stable window",
 			Measure:     stableRequestConcurrencyM,
 			Aggregation: view.LastValue(),
-			TagKeys:     metrics.CommonRevisionKeys,
 		},
 		&view.View{
 			Description: "Current excess burst capacity over average request count over the stable window",
 			Measure:     excessBurstCapacityM,
 			Aggregation: view.LastValue(),
-			TagKeys:     metrics.CommonRevisionKeys,
 		},
 		&view.View{
 			Description: "Average of requests count over the panic window",
 			Measure:     panicRequestConcurrencyM,
 			Aggregation: view.LastValue(),
-			TagKeys:     metrics.CommonRevisionKeys,
 		},
 		&view.View{
 			Description: "The desired number of concurrent requests for each pod",
 			Measure:     targetRequestConcurrencyM,
 			Aggregation: view.LastValue(),
-			TagKeys:     metrics.CommonRevisionKeys,
 		},
 		&view.View{
 			Description: "1 if autoscaler is in panic mode, 0 otherwise",
 			Measure:     panicM,
 			Aggregation: view.LastValue(),
-			TagKeys:     metrics.CommonRevisionKeys,
 		},
 		&view.View{
 			Description: "Average requests-per-second over the stable window",
 			Measure:     stableRPSM,
 			Aggregation: view.LastValue(),
-			TagKeys:     metrics.CommonRevisionKeys,
 		},
 		&view.View{
 			Description: "Average requests-per-second over the panic window",
 			Measure:     panicRPSM,
 			Aggregation: view.LastValue(),
-			TagKeys:     metrics.CommonRevisionKeys,
 		},
 		&view.View{
 			Description: "The desired requests-per-second for each pod",
 			Measure:     targetRPSM,
 			Aggregation: view.LastValue(),
-			TagKeys:     metrics.CommonRevisionKeys,
 		},
 	); err != nil {
 		panic(err)
diff --git a/pkg/metrics/key.go b/pkg/metrics/key.go
index a4d17cb59..f5987bad8 100644
--- a/pkg/metrics/key.go
+++ b/pkg/metrics/key.go
@@ -36,6 +36,4 @@ var (
 	ResponseCodeKey      = tag.MustNewKey(metricskey.LabelResponseCode)
 	ResponseCodeClassKey = tag.MustNewKey(metricskey.LabelResponseCodeClass)
 	RouteTagKey          = tag.MustNewKey("tag")
-
-	CommonRevisionKeys = []tag.Key{NamespaceTagKey, ServiceTagKey, ConfigTagKey, RevisionTagKey}
 )
diff --git a/pkg/metrics/tags.go b/pkg/metrics/tags.go
index f798522e3..4777e35d0 100644
--- a/pkg/metrics/tags.go
+++ b/pkg/metrics/tags.go
@@ -24,6 +24,7 @@ import (
 	"k8s.io/apimachinery/pkg/types"
 	"knative.dev/pkg/metrics/metricskey"
 
+	"go.opencensus.io/resource"
 	"go.opencensus.io/tag"
 )
 
@@ -55,10 +56,7 @@ func RevisionContext(ns, svc, cfg, rev string) (context.Context, error) {
 	key := types.NamespacedName{Namespace: ns, Name: rev}
 	ctx, ok := contextCache.Get(key)
 	if !ok {
-		rctx, err := AugmentWithRevision(context.Background(), ns, svc, cfg, rev)
-		if err != nil {
-			return rctx, err
-		}
+		rctx := AugmentWithRevision(context.Background(), ns, svc, cfg, rev)
 		contextCache.Add(key, rctx)
 		ctx = rctx
 	}
@@ -106,24 +104,25 @@ func PodRevisionContext(pod, container, ns, svc, cfg, rev string) (context.Conte
 		if err != nil {
 			return rctx, err
 		}
-		rctx, err = AugmentWithRevision(rctx, ns, svc, cfg, rev)
-		if err != nil {
-			return rctx, err
-		}
+		rctx = AugmentWithRevision(rctx, ns, svc, cfg, rev)
 		contextCache.Add(key, rctx)
 		ctx = rctx
 	}
 	return ctx.(context.Context), nil
 }
 
-// AugmentWithRevision augments the given context with revision specific tags.
-func AugmentWithRevision(baseCtx context.Context, ns, svc, cfg, rev string) (context.Context, error) {
-	return tag.New(
-		baseCtx,
-		tag.Upsert(NamespaceTagKey, ns),
-		tag.Upsert(ServiceTagKey, valueOrUnknown(svc)),
-		tag.Upsert(ConfigTagKey, cfg),
-		tag.Upsert(RevisionTagKey, rev))
+// AugmentWithRevision augments the given context with a knative_revision resource.
+func AugmentWithRevision(baseCtx context.Context, ns, svc, cfg, rev string) context.Context {
+	r := resource.Resource{
+		Type: metricskey.ResourceTypeKnativeRevision,
+		Labels: map[string]string{
+			metricskey.LabelNamespaceName:     ns,
+			metricskey.LabelServiceName:       valueOrUnknown(svc),
+			metricskey.LabelConfigurationName: cfg,
+			metricskey.LabelRevisionName:      rev,
+		},
+	}
+	return metricskey.WithResource(baseCtx, r)
 }
 
 // AugmentWithResponse augments the given context with response-code specific tags.
diff --git a/pkg/queue/request_metric.go b/pkg/queue/request_metric.go
index 94c8cd80f..a3b8006ff 100644
--- a/pkg/queue/request_metric.go
+++ b/pkg/queue/request_metric.go
@@ -23,6 +23,7 @@ import (
 
 	"go.opencensus.io/stats"
 	"go.opencensus.io/stats/view"
+	"go.opencensus.io/tag"
 
 	pkgmetrics "knative.dev/pkg/metrics"
 	pkghttp "knative.dev/serving/pkg/http"
@@ -80,8 +81,7 @@ type appRequestMetricsHandler struct {
 // NewRequestMetricsHandler creates an http.Handler that emits request metrics.
 func NewRequestMetricsHandler(next http.Handler,
 	ns, service, config, rev, pod string) (http.Handler, error) {
-	keys := append(metrics.CommonRevisionKeys, metrics.PodTagKey,
-		metrics.ContainerTagKey, metrics.ResponseCodeKey, metrics.ResponseCodeClassKey, metrics.RouteTagKey)
+	keys := []tag.Key{metrics.PodTagKey, metrics.ContainerTagKey, metrics.ResponseCodeKey, metrics.ResponseCodeClassKey, metrics.RouteTagKey}
 	if err := pkgmetrics.RegisterResourceView(
 		&view.View{
 			Description: "The number of requests that are routed to queue-proxy",
@@ -143,8 +143,7 @@ func (h *requestMetricsHandler) ServeHTTP(w http.ResponseWriter, r *http.Request
 // NewAppRequestMetricsHandler creates an http.Handler that emits request metrics.
 func NewAppRequestMetricsHandler(next http.Handler, b *Breaker,
 	ns, service, config, rev, pod string) (http.Handler, error) {
-	keys := append(metrics.CommonRevisionKeys, metrics.PodTagKey,
-		metrics.ContainerTagKey, metrics.ResponseCodeKey, metrics.ResponseCodeClassKey)
+	keys := []tag.Key{metrics.PodTagKey, metrics.ContainerTagKey, metrics.ResponseCodeKey, metrics.ResponseCodeClassKey}
 	if err := pkgmetrics.RegisterResourceView(&view.View{
 		Description: "The number of requests that are routed to user-container",
 		Measure:     appRequestCountM,
diff --git a/pkg/reconciler/autoscaling/kpa/metrics.go b/pkg/reconciler/autoscaling/kpa/metrics.go
index b84087d1b..d513a4790 100644
--- a/pkg/reconciler/autoscaling/kpa/metrics.go
+++ b/pkg/reconciler/autoscaling/kpa/metrics.go
@@ -18,7 +18,6 @@ package kpa
 
 import (
 	pkgmetrics "knative.dev/pkg/metrics"
-	"knative.dev/serving/pkg/metrics"
 
 	"go.opencensus.io/stats"
 	"go.opencensus.io/stats/view"
@@ -60,31 +59,26 @@ func register() {
 			Description: "Number of pods autoscaler requested from Kubernetes",
 			Measure:     requestedPodCountM,
 			Aggregation: view.LastValue(),
-			TagKeys:     metrics.CommonRevisionKeys,
 		},
 		&view.View{
 			Description: "Number of pods that are allocated currently",
 			Measure:     actualPodCountM,
 			Aggregation: view.LastValue(),
-			TagKeys:     metrics.CommonRevisionKeys,
 		},
 		&view.View{
 			Description: "Number of pods that are not ready currently",
 			Measure:     notReadyPodCountM,
 			Aggregation: view.LastValue(),
-			TagKeys:     metrics.CommonRevisionKeys,
 		},
 		&view.View{
 			Description: "Number of pods that are pending currently",
 			Measure:     pendingPodCountM,
 			Aggregation: view.LastValue(),
-			TagKeys:     metrics.CommonRevisionKeys,
 		},
 		&view.View{
 			Description: "Number of pods that are terminating currently",
 			Measure:     terminatingPodCountM,
 			Aggregation: view.LastValue(),
-			TagKeys:     metrics.CommonRevisionKeys,
 		},
 	); err != nil {
 		panic(err)
diff --git a/pkg/reconciler/labeler/controller.go b/pkg/reconciler/labeler/controller.go
index ee7e19c15..e1d6afff7 100644
--- a/pkg/reconciler/labeler/controller.go
+++ b/pkg/reconciler/labeler/controller.go
@@ -29,6 +29,8 @@ import (
 	routeinformer "knative.dev/serving/pkg/client/injection/informers/serving/v1/route"
 	routereconciler "knative.dev/serving/pkg/client/injection/reconciler/serving/v1/route"
 	servingreconciler "knative.dev/serving/pkg/reconciler"
+	labelerv1 "knative.dev/serving/pkg/reconciler/labeler/v1"
+	labelerv2 "knative.dev/serving/pkg/reconciler/labeler/v2"
 
 	"knative.dev/pkg/configmap"
 	"knative.dev/pkg/controller"
@@ -58,12 +60,7 @@ func newControllerWithClock(
 	configInformer := configurationinformer.Get(ctx)
 	revisionInformer := revisioninformer.Get(ctx)
 
-	c := &Reconciler{
-		client:              servingclient.Get(ctx),
-		configurationLister: configInformer.Lister(),
-		revisionLister:      revisionInformer.Lister(),
-		clock:               clock,
-	}
+	c := &Reconciler{}
 	impl := routereconciler.NewImpl(ctx, c, func(*controller.Impl) controller.Options {
 		return controller.Options{
 			// The labeler shouldn't mutate the route's status.
@@ -74,26 +71,34 @@ func newControllerWithClock(
 	logger.Info("Setting up event handlers")
 	routeInformer.Informer().AddEventHandler(controller.HandleAll(impl.Enqueue))
 
-	c.tracker = tracker.New(impl.EnqueueKey, controller.GetTrackerLease(ctx))
+	tracker := tracker.New(impl.EnqueueKey, controller.GetTrackerLease(ctx))
 
 	// Make sure trackers are deleted once the observers are removed.
 	routeInformer.Informer().AddEventHandler(cache.ResourceEventHandlerFuncs{
-		DeleteFunc: c.tracker.OnDeletedObserver,
+		DeleteFunc: tracker.OnDeletedObserver,
 	})
 
 	configInformer.Informer().AddEventHandler(controller.HandleAll(
 		controller.EnsureTypeMeta(
-			c.tracker.OnChanged,
+			tracker.OnChanged,
 			v1.SchemeGroupVersion.WithKind("Configuration"),
 		),
 	))
 
 	revisionInformer.Informer().AddEventHandler(controller.HandleAll(
 		controller.EnsureTypeMeta(
-			c.tracker.OnChanged,
+			tracker.OnChanged,
 			v1.SchemeGroupVersion.WithKind("Revision"),
 		),
 	))
 
+	client := servingclient.Get(ctx)
+
+	c.caccV1 = labelerv1.NewConfigurationAccessor(client, tracker, configInformer.Lister())
+	c.caccV2 = labelerv2.NewConfigurationAccessor(client, tracker, configInformer.Lister(), configInformer.Informer().GetIndexer(), clock)
+
+	c.raccV1 = labelerv1.NewRevisionAccessor(client, tracker, revisionInformer.Lister())
+	c.raccV2 = labelerv2.NewRevisionAccessor(client, tracker, revisionInformer.Lister(), revisionInformer.Informer().GetIndexer(), clock)
+
 	return impl
 }
diff --git a/pkg/reconciler/labeler/labeler.go b/pkg/reconciler/labeler/labeler.go
index 454f642a0..307d8c61a 100644
--- a/pkg/reconciler/labeler/labeler.go
+++ b/pkg/reconciler/labeler/labeler.go
@@ -19,62 +19,77 @@ package labeler
 import (
 	"context"
 
-	"k8s.io/apimachinery/pkg/util/clock"
 	pkgreconciler "knative.dev/pkg/reconciler"
-	"knative.dev/pkg/tracker"
 	cfgmap "knative.dev/serving/pkg/apis/config"
 	v1 "knative.dev/serving/pkg/apis/serving/v1"
-	clientset "knative.dev/serving/pkg/client/clientset/versioned"
 	routereconciler "knative.dev/serving/pkg/client/injection/reconciler/serving/v1/route"
-	listers "knative.dev/serving/pkg/client/listers/serving/v1"
 	labelerv1 "knative.dev/serving/pkg/reconciler/labeler/v1"
 	labelerv2 "knative.dev/serving/pkg/reconciler/labeler/v2"
 )
 
 // Reconciler implements controller.Reconciler for Route resources.
 type Reconciler struct {
-	client clientset.Interface
+	caccV1 *labelerv1.Configuration
+	raccV1 *labelerv1.Revision
 
-	// tracker to track revisions and configurations
-	tracker tracker.Interface
-
-	// Listers index properties about resources
-	configurationLister listers.ConfigurationLister
-	revisionLister      listers.RevisionLister
-	clock               clock.Clock
+	caccV2 *labelerv2.Configuration
+	raccV2 *labelerv2.Revision
 }
 
 // Check that our Reconciler implements routereconciler.Interface
 var _ routereconciler.Interface = (*Reconciler)(nil)
 var _ routereconciler.Finalizer = (*Reconciler)(nil)
 
-func (c *Reconciler) FinalizeKind(ctx context.Context, r *v1.Route) pkgreconciler.Event {
-	switch cfgmap.FromContextOrDefaults(ctx).Features.ResponsiveRevisionGC {
+// FinalizeKind removes all Route reference metadata from its traffic targets.
+// This does not modify or observe spec for the Route itself.
+func (rec *Reconciler) FinalizeKind(ctx context.Context, r *v1.Route) pkgreconciler.Event {
+	newGC := cfgmap.FromContextOrDefaults(ctx).Features.ResponsiveRevisionGC
 
-	case cfgmap.Disabled: // v1 logic
-		cacc := labelerv1.NewConfigurationAccessor(c.client, c.tracker, c.configurationLister)
-		racc := labelerv1.NewRevisionAccessor(c.client, c.tracker, c.revisionLister)
-		return labelerv1.ClearLabels(r.Namespace, r.Name, cacc, racc)
+	// v1 logic
+	if newGC == cfgmap.Disabled || newGC == cfgmap.Allowed {
+		if err := labelerv1.ClearLabels(r.Namespace, r.Name, rec.caccV1, rec.raccV1); err != nil {
+			return err
+		}
+	}
 
-	default: // v2 logic
-		cacc := labelerv2.NewConfigurationAccessor(c.client, c.tracker, c.configurationLister, c.clock)
-		racc := labelerv2.NewRevisionAccessor(c.client, c.tracker, c.revisionLister, c.clock)
-		return labelerv2.ClearLabels(r.Namespace, r.Name, cacc, racc)
+	// v2 logic
+	if newGC == cfgmap.Allowed || newGC == cfgmap.Enabled {
+		if err := labelerv2.ClearRoutingMeta(r.Namespace, r.Name, rec.caccV2, rec.raccV2); err != nil {
+			return err
+		}
 	}
+
+	return nil
 }
 
-func (c *Reconciler) ReconcileKind(ctx context.Context, r *v1.Route) pkgreconciler.Event {
+// ReconcileKind syncs the Route reference metadata to its traffic targets.
+// This does not modify or observe spec for the Route itself.
+func (rec *Reconciler) ReconcileKind(ctx context.Context, r *v1.Route) pkgreconciler.Event {
 	switch cfgmap.FromContextOrDefaults(ctx).Features.ResponsiveRevisionGC {
-
-	case cfgmap.Disabled: // v1 logic
-		cacc := labelerv1.NewConfigurationAccessor(c.client, c.tracker, c.configurationLister)
-		racc := labelerv1.NewRevisionAccessor(c.client, c.tracker, c.revisionLister)
-		return labelerv1.SyncLabels(r, cacc, racc)
-
-	default: // v2 logic
-		cacc := labelerv2.NewConfigurationAccessor(c.client, c.tracker, c.configurationLister, c.clock)
-		racc := labelerv2.NewRevisionAccessor(c.client, c.tracker, c.revisionLister, c.clock)
-		return labelerv2.SyncLabels(r, cacc, racc)
-
+	case cfgmap.Disabled:
+		if err := labelerv1.SyncLabels(r, rec.caccV1, rec.raccV1); err != nil {
+			return err
+		}
+		// Clear the new label for downgrade
+		if err := labelerv2.ClearRoutingMeta(r.Namespace, r.Name, rec.caccV2, rec.raccV2); err != nil {
+			return err
+		}
+	case cfgmap.Allowed: // Both labelers on, down/upgrade don't lose data.
+		if err := labelerv1.SyncLabels(r, rec.caccV1, rec.raccV1); err != nil {
+			return err
+		}
+		if err := labelerv2.SyncRoutingMeta(r, rec.caccV2, rec.raccV2); err != nil {
+			return err
+		}
+	case cfgmap.Enabled:
+		if err := labelerv2.SyncRoutingMeta(r, rec.caccV2, rec.raccV2); err != nil {
+			return err
+		}
+		// Clear the old label for upgrade
+		if err := labelerv1.ClearLabels(r.Namespace, r.Name, rec.caccV1, rec.raccV1); err != nil {
+			return err
+		}
 	}
+
+	return nil
 }
diff --git a/pkg/reconciler/labeler/v2/accessors.go b/pkg/reconciler/labeler/v2/accessors.go
index 8e87b7b2a..ae9ceb5cb 100644
--- a/pkg/reconciler/labeler/v2/accessors.go
+++ b/pkg/reconciler/labeler/v2/accessors.go
@@ -17,11 +17,13 @@ limitations under the License.
 package v2
 
 import (
-	"fmt"
+	"strings"
 
 	"k8s.io/apimachinery/pkg/labels"
 	"k8s.io/apimachinery/pkg/types"
 	"k8s.io/apimachinery/pkg/util/clock"
+	"k8s.io/client-go/tools/cache"
+	"k8s.io/kube-openapi/pkg/util/sets"
 	"knative.dev/pkg/kmeta"
 
 	"knative.dev/pkg/tracker"
@@ -34,17 +36,18 @@ import (
 // Accessor defines an abstraction for manipulating labeled entity
 // (Configuration, Revision) with shared logic.
 type Accessor interface {
-	list(ns, name string) ([]kmeta.Accessor, error)
+	list(ns, routeName string, state v1.RoutingState) ([]kmeta.Accessor, error)
 	patch(ns, name string, pt types.PatchType, p []byte) error
-	makeMetadataPatch(ns, name, routeName string) (map[string]interface{}, error)
+	makeMetadataPatch(ns, name, routeName string, remove bool) (map[string]interface{}, error)
 }
 
 // Revision is an implementation of Accessor for Revisions.
 type Revision struct {
-	client         clientset.Interface
-	tracker        tracker.Interface
-	revisionLister listers.RevisionLister
-	clock          clock.Clock
+	client  clientset.Interface
+	tracker tracker.Interface
+	lister  listers.RevisionLister
+	indexer cache.Indexer
+	clock   clock.Clock
 }
 
 // Revision implements Accessor
@@ -55,26 +58,24 @@ func NewRevisionAccessor(
 	client clientset.Interface,
 	tracker tracker.Interface,
 	lister listers.RevisionLister,
+	indexer cache.Indexer,
 	clock clock.Clock) *Revision {
 	return &Revision{
-		client:         client,
-		tracker:        tracker,
-		revisionLister: lister,
-		clock:          clock,
+		client:  client,
+		tracker: tracker,
+		lister:  lister,
+		indexer: indexer,
+		clock:   clock,
 	}
 }
 
 // makeMetadataPatch makes a metadata map to be patched or nil if no changes are needed.
 func makeMetadataPatch(
-	acc kmeta.Accessor, routeName string, addRoutingState bool, clock clock.Clock) (map[string]interface{}, error) {
+	acc kmeta.Accessor, routeName string, addRoutingState, remove bool, clock clock.Clock) (map[string]interface{}, error) {
 	labels := map[string]interface{}{}
 	annotations := map[string]interface{}{}
 
-	if err := addRouteLabel(acc, routeName, labels); err != nil {
-		return nil, err
-	}
-
-	if addRoutingState {
+	if stateChanged := updateRouteAnnotation(acc, routeName, annotations, remove); stateChanged && addRoutingState {
 		markRoutingState(acc, routeName != "", clock, labels, annotations)
 	}
 
@@ -106,40 +107,52 @@ func markRoutingState(
 	}
 }
 
-// addRouteLabel appends the route label to the list of labels if needed
-// or removes the label if routeName is nil.
-func addRouteLabel(acc kmeta.Accessor, routeName string, diffLabels map[string]interface{}) error {
-	if routeName == "" { // remove the label
-		if acc.GetLabels()[serving.RouteLabelKey] != "" {
-			diffLabels[serving.RouteLabelKey] = nil
+// updateRouteAnnotation appends the route annotation to the list of labels if needed
+// or removes the annotation if routeName is nil.
+// Returns true if the entire annotation is newly added or removed, which signifies a state change.
+func updateRouteAnnotation(acc kmeta.Accessor, routeName string, diffAnn map[string]interface{}, remove bool) bool {
+	valSet := getListAnnValue(acc.GetAnnotations(), serving.RoutesAnnotationKey)
+	has := valSet.Has(routeName)
+	switch {
+	case has && remove:
+		if len(valSet) == 1 {
+			diffAnn[serving.RoutesAnnotationKey] = nil
+			return true
 		}
-	} else { // add the label
-		if oldLabel := acc.GetLabels()[serving.RouteLabelKey]; oldLabel == "" {
-			diffLabels[serving.RouteLabelKey] = routeName
-		} else if oldLabel != routeName {
-			// TODO(whaught): this restricts us to only one route -> revision
-			// We can move this to a comma separated list annotation and use the new routingState label.
-			return fmt.Errorf("resource already has route label %q, and cannot be referenced by %q", oldLabel, routeName)
+		valSet.Delete(routeName)
+		diffAnn[serving.RoutesAnnotationKey] = strings.Join(valSet.UnsortedList(), ",")
+		return false
+
+	case !has && !remove:
+		if len(valSet) == 0 {
+			diffAnn[serving.RoutesAnnotationKey] = routeName
+			return true
 		}
+		valSet.Insert(routeName)
+		diffAnn[serving.RoutesAnnotationKey] = strings.Join(valSet.UnsortedList(), ",")
+		return false
 	}
 
-	return nil
+	return false
 }
 
 // list implements Accessor
-func (r *Revision) list(ns, name string) ([]kmeta.Accessor, error) {
-	rl, err := r.revisionLister.Revisions(ns).List(labels.SelectorFromSet(labels.Set{
-		serving.RouteLabelKey: name,
-	}))
-	if err != nil {
-		return nil, err
+func (r *Revision) list(ns, routeName string, state v1.RoutingState) ([]kmeta.Accessor, error) {
+	kl := make([]kmeta.Accessor, 0, 1)
+	filter := func(m interface{}) {
+		r := m.(*v1.Revision)
+		if getListAnnValue(r.Annotations, serving.RoutesAnnotationKey).Has(routeName) {
+			kl = append(kl, r)
+		}
 	}
-	// Need a copy to change types in Go
-	kl := make([]kmeta.Accessor, len(rl))
-	for i, r := range rl {
-		kl[i] = r
+	selector := labels.SelectorFromSet(labels.Set{
+		serving.RoutingStateLabelKey: string(state),
+	})
+
+	if err := cache.ListAllByNamespace(r.indexer, ns, selector, filter); err != nil {
+		return nil, err
 	}
-	return kl, err
+	return kl, nil
 }
 
 // patch implements Accessor
@@ -148,20 +161,21 @@ func (r *Revision) patch(ns, name string, pt types.PatchType, p []byte) error {
 	return err
 }
 
-func (r *Revision) makeMetadataPatch(ns, name, routeName string) (map[string]interface{}, error) {
-	rev, err := r.revisionLister.Revisions(ns).Get(name)
+func (r *Revision) makeMetadataPatch(ns, name, routeName string, remove bool) (map[string]interface{}, error) {
+	rev, err := r.lister.Revisions(ns).Get(name)
 	if err != nil {
 		return nil, err
 	}
-	return makeMetadataPatch(rev, routeName, true /*addRoutingState*/, r.clock)
+	return makeMetadataPatch(rev, routeName, true /*addRoutingState*/, remove, r.clock)
 }
 
 // Configuration is an implementation of Accessor for Configurations.
 type Configuration struct {
-	client              clientset.Interface
-	tracker             tracker.Interface
-	configurationLister listers.ConfigurationLister
-	clock               clock.Clock
+	client  clientset.Interface
+	tracker tracker.Interface
+	lister  listers.ConfigurationLister
+	indexer cache.Indexer
+	clock   clock.Clock
 }
 
 // Configuration implements Accessor
@@ -172,29 +186,41 @@ func NewConfigurationAccessor(
 	client clientset.Interface,
 	tracker tracker.Interface,
 	lister listers.ConfigurationLister,
+	indexer cache.Indexer,
 	clock clock.Clock) *Configuration {
 	return &Configuration{
-		client:              client,
-		tracker:             tracker,
-		configurationLister: lister,
-		clock:               clock,
+		client:  client,
+		tracker: tracker,
+		lister:  lister,
+		indexer: indexer,
+		clock:   clock,
 	}
 }
 
 // list implements Accessor
-func (c *Configuration) list(ns, name string) ([]kmeta.Accessor, error) {
-	cl, err := c.configurationLister.Configurations(ns).List(labels.SelectorFromSet(labels.Set{
-		serving.RouteLabelKey: name,
-	}))
-	if err != nil {
+func (c *Configuration) list(ns, routeName string, state v1.RoutingState) ([]kmeta.Accessor, error) {
+	kl := make([]kmeta.Accessor, 0, 1)
+	filter := func(m interface{}) {
+		c := m.(*v1.Configuration)
+		if getListAnnValue(c.Annotations, serving.RoutesAnnotationKey).Has(routeName) {
+			kl = append(kl, c)
+		}
+	}
+
+	if err := cache.ListAllByNamespace(c.indexer, ns, labels.Everything(), filter); err != nil {
 		return nil, err
 	}
-	// Need a copy to change types in Go
-	kl := make([]kmeta.Accessor, len(cl))
-	for i, c := range cl {
-		kl[i] = c
+	return kl, nil
+}
+
+// getListAnnValue finds a given value in a comma-separated annotation.
+// returns the entire annotation value and true if found.
+func getListAnnValue(annotations map[string]string, key string) sets.String {
+	l := annotations[key]
+	if l == "" {
+		return sets.String{}
 	}
-	return kl, err
+	return sets.NewString(strings.Split(l, ",")...)
 }
 
 // patch implements Accessor
@@ -203,10 +229,10 @@ func (c *Configuration) patch(ns, name string, pt types.PatchType, p []byte) err
 	return err
 }
 
-func (c *Configuration) makeMetadataPatch(ns, name, routeName string) (map[string]interface{}, error) {
-	config, err := c.configurationLister.Configurations(ns).Get(name)
+func (c *Configuration) makeMetadataPatch(ns, name, routeName string, remove bool) (map[string]interface{}, error) {
+	config, err := c.lister.Configurations(ns).Get(name)
 	if err != nil {
 		return nil, err
 	}
-	return makeMetadataPatch(config, routeName, false /*addRoutingState*/, c.clock)
+	return makeMetadataPatch(config, routeName, false /*addRoutingState*/, remove, c.clock)
 }
diff --git a/pkg/reconciler/labeler/v2/labels.go b/pkg/reconciler/labeler/v2/metasync.go
similarity index 63%
rename from pkg/reconciler/labeler/v2/labels.go
rename to pkg/reconciler/labeler/v2/metasync.go
index 72853eaa6..630082f8d 100644
--- a/pkg/reconciler/labeler/v2/labels.go
+++ b/pkg/reconciler/labeler/v2/metasync.go
@@ -28,9 +28,9 @@ import (
 	v1 "knative.dev/serving/pkg/apis/serving/v1"
 )
 
-// SyncLabels makes sure that the revisions and configurations referenced from
-// a Route are labeled with route labels.
-func SyncLabels(r *v1.Route, cacc *Configuration, racc *Revision) error {
+// SyncRoutingMeta makes sure that the revisions and configurations referenced from
+// a Route are labeled with the routingState label and routes annotation.
+func SyncRoutingMeta(r *v1.Route, cacc *Configuration, racc *Revision) error {
 	revisions := sets.NewString()
 	configs := sets.NewString()
 
@@ -45,7 +45,7 @@ func SyncLabels(r *v1.Route, cacc *Configuration, racc *Revision) error {
 				return err
 			}
 
-			rev, err := racc.revisionLister.Revisions(r.Namespace).Get(revName)
+			rev, err := racc.lister.Revisions(r.Namespace).Get(revName)
 			if err != nil {
 				// The revision might not exist (yet). The informers will notify if it gets created.
 				continue
@@ -64,7 +64,7 @@ func SyncLabels(r *v1.Route, cacc *Configuration, racc *Revision) error {
 				return err
 			}
 
-			config, err := cacc.configurationLister.Configurations(r.Namespace).Get(configName)
+			config, err := cacc.lister.Configurations(r.Namespace).Get(configName)
 			if err != nil {
 				// The config might not exist (yet). The informers will notify if it gets created.
 				continue
@@ -81,46 +81,46 @@ func SyncLabels(r *v1.Route, cacc *Configuration, racc *Revision) error {
 	}
 
 	// Use a revision accessor to manipulate the revisions.
-	if err := deleteLabelForNotListed(r.Namespace, r.Name, racc, revisions); err != nil {
+	if err := clearMetaForNotListed(r.Namespace, r.Name, racc, revisions); err != nil {
 		return err
 	}
-	if err := setLabelForListed(r, racc, revisions); err != nil {
+	if err := setMetaForListed(r, racc, revisions); err != nil {
 		return err
 	}
 
 	// Use a config access to manipulate the configs.
-	if err := deleteLabelForNotListed(r.Namespace, r.Name, cacc, configs); err != nil {
+	if err := clearMetaForNotListed(r.Namespace, r.Name, cacc, configs); err != nil {
 		return err
 	}
-	return setLabelForListed(r, cacc, configs)
+	return setMetaForListed(r, cacc, configs)
 }
 
-// ClearLabels removes any labels for a named route from given accessors.
-func ClearLabels(ns, name string, accs ...Accessor) error {
+// ClearRoutingMeta removes any labels for a named route from given accessors.
+func ClearRoutingMeta(ns, name string, accs ...Accessor) error {
 	for _, acc := range accs {
-		if err := deleteLabelForNotListed(ns, name, acc, nil /*none listed*/); err != nil {
+		if err := clearMetaForNotListed(ns, name, acc, nil /*none listed*/); err != nil {
 			return err
 		}
 	}
 	return nil
 }
 
-// setLabelForListed uses the accessor to attach the label for this route to every element
+// setMetaForListed uses the accessor to attach the label for this route to every element
 // listed within "names" in the same namespace.
-func setLabelForListed(route *v1.Route, acc Accessor, names sets.String) error {
+func setMetaForListed(route *v1.Route, acc Accessor, names sets.String) error {
 	for name := range names {
-		if err := setRouteLabel(acc, route.Namespace, name, route.Name); err != nil {
-			return fmt.Errorf("failed to add route label to Namespace=%s Name=%q: %w", route.Namespace, name, err)
+		if err := setRoutingMeta(acc, route.Namespace, name, route.Name, false); err != nil {
+			return fmt.Errorf("failed to add route annotation to Namespace=%s Name=%q: %w", route.Namespace, name, err)
 		}
 	}
 	return nil
 }
 
-// deleteLabelForNotListed uses the accessor to delete the label from any listable entity that is
-// not named within our list.  Unlike setLabelForListed, this function takes ns/name instead of a
+// clearMetaForNotListed uses the accessor to delete the label from any listable entity that is
+// not named within our list.  Unlike setMetaForListed, this function takes ns/name instead of a
 // Route so that it can clean things up when a Route ceases to exist.
-func deleteLabelForNotListed(ns, routeName string, acc Accessor, names sets.String) error {
-	oldList, err := acc.list(ns, routeName)
+func clearMetaForNotListed(ns, routeName string, acc Accessor, names sets.String) error {
+	oldList, err := acc.list(ns, routeName, v1.RoutingStateActive)
 	if err != nil {
 		return err
 	}
@@ -132,8 +132,8 @@ func deleteLabelForNotListed(ns, routeName string, acc Accessor, names sets.Stri
 			continue
 		}
 
-		if err := setRouteLabel(acc, ns, name, ""); err != nil {
-			return fmt.Errorf("failed to remove route label to %s %q: %w",
+		if err := setRoutingMeta(acc, ns, name, routeName, true); err != nil {
+			return fmt.Errorf("failed to remove route annotation to %s %q: %w",
 				elt.GroupVersionKind(), name, err)
 		}
 	}
@@ -141,11 +141,12 @@ func deleteLabelForNotListed(ns, routeName string, acc Accessor, names sets.Stri
 	return nil
 }
 
-// setRouteLabel toggles the route label on the specified element through the provided accessor.
-// a nil route name will cause the route label to be deleted, and a non-nil route will cause
+// setRoutingMeta toggles the routing state label, routes list and timestamp annotation on the specified
+// element through the provided accessor.
+// A nil route name will cause the route to be de-referenced, and a non-nil route will cause
 // that route name to be attached to the element.
-func setRouteLabel(acc Accessor, ns, name, routeName string) error {
-	if mergePatch, err := acc.makeMetadataPatch(ns, name, routeName); err != nil {
+func setRoutingMeta(acc Accessor, ns, name, routeName string, remove bool) error {
+	if mergePatch, err := acc.makeMetadataPatch(ns, name, routeName, remove); err != nil {
 		return err
 	} else if mergePatch != nil {
 		patch, err := json.Marshal(mergePatch)
diff --git a/pkg/resources/pods.go b/pkg/resources/pods.go
index 7311ac18c..49c06c6f9 100644
--- a/pkg/resources/pods.go
+++ b/pkg/resources/pods.go
@@ -99,33 +99,72 @@ func (pa PodAccessor) NotReadyCount() (int, error) {
 	return nr, err
 }
 
-// PodIPsByAge returns the list of running pod (terminating
-// and non-running are excluded) IP addresses, sorted descending by pod age.
-func (pa PodAccessor) PodIPsByAge() ([]string, error) {
+// PodFilter provides a way to filter pods for a revision.
+// Returning true, means that pod should be kept.
+type PodFilter func(p *corev1.Pod) bool
+
+// PodTransformer provides a way to do something with the pod
+// that has been selected by all the filters.
+// For example pod transformer may extract a field and store it in
+// internal state.
+type PodTransformer func(p *corev1.Pod)
+
+// ProcessPods filters all the pods using provided pod filters and then if the pod
+// is selected, applies the transformer to it.
+func (pa PodAccessor) ProcessPods(pt PodTransformer, pfs ...PodFilter) error {
 	pods, err := pa.podsLister.List(pa.selector)
 	if err != nil {
-		return nil, err
+		return err
 	}
-	// Keep only running ones.
-	write := 0
 	for _, p := range pods {
-		if p.Status.Phase == corev1.PodRunning && p.DeletionTimestamp == nil {
-			pods[write] = p
-			write++
+		if applyFilters(p, pfs...) {
+			pt(p)
 		}
 	}
-	pods = pods[:write]
+	return nil
+}
+
+func applyFilters(p *corev1.Pod, pfs ...PodFilter) bool {
+	for _, pf := range pfs {
+		if !pf(p) {
+			return false
+		}
+	}
+	return true
+}
+
+func podRunning(p *corev1.Pod) bool {
+	return p.Status.Phase == corev1.PodRunning && p.DeletionTimestamp == nil
+}
+
+type podIPByAgeSorter struct {
+	pods []*corev1.Pod
+}
 
-	if len(pods) > 1 {
+func (s *podIPByAgeSorter) process(p *corev1.Pod) {
+	s.pods = append(s.pods, p)
+}
+
+func (s *podIPByAgeSorter) get() []string {
+	if len(s.pods) > 1 {
 		// This results in a few reflection calls, which we can easily avoid.
-		sort.SliceStable(pods, func(i, j int) bool {
-			return pods[i].Status.StartTime.Before(pods[j].Status.StartTime)
+		sort.SliceStable(s.pods, func(i, j int) bool {
+			return s.pods[i].Status.StartTime.Before(s.pods[j].Status.StartTime)
 		})
 	}
-	ret := make([]string, 0, len(pods))
-	for _, p := range pods {
+	ret := make([]string, 0, len(s.pods))
+	for _, p := range s.pods {
 		ret = append(ret, p.Status.PodIP)
 	}
+	return ret
+}
 
-	return ret, nil
+// PodIPsByAge returns the list of running pods (terminating
+// and non-running are excluded) IP addresses, sorted descending by pod age.
+func (pa PodAccessor) PodIPsByAge() ([]string, error) {
+	ps := podIPByAgeSorter{}
+	if err := pa.ProcessPods(ps.process, podRunning); err != nil {
+		return nil, err
+	}
+	return ps.get(), nil
 }


Test patch:
diff --git a/cmd/autoscaler/main_test.go b/cmd/autoscaler/main_test.go
index 8204ab5b8..66ae9abe0 100644
--- a/cmd/autoscaler/main_test.go
+++ b/cmd/autoscaler/main_test.go
@@ -60,22 +60,6 @@ func TestUniscalerFactoryFailures(t *testing.T) {
 			serving.RevisionLabelKey: "bamba",
 		},
 		want: fmt.Sprintf("label %q not found or empty in Decider", serving.ConfigurationLabelKey),
-	}, {
-		name: "values not ascii",
-		labels: map[string]string{
-			serving.ServiceLabelKey:       "la",
-			serving.ConfigurationLabelKey: "verité",
-			serving.RevisionLabelKey:      "bamba",
-		},
-		want: "invalid value: only ASCII characters accepted",
-	}, {
-		name: "too long of a value",
-		labels: map[string]string{
-			serving.ServiceLabelKey:       "cat is ",
-			serving.RevisionLabelKey:      "bamba",
-			serving.ConfigurationLabelKey: "l" + strings.Repeat("o", 253) + "ng",
-		},
-		want: "max length must be 255 characters",
 	}}
 
 	uniScalerFactory := testUniScalerFactory()
diff --git a/pkg/activator/handler/concurrency_reporter_test.go b/pkg/activator/handler/concurrency_reporter_test.go
index b5b92045e..f02818eb8 100644
--- a/pkg/activator/handler/concurrency_reporter_test.go
+++ b/pkg/activator/handler/concurrency_reporter_test.go
@@ -27,6 +27,7 @@ import (
 	"time"
 
 	"github.com/google/go-cmp/cmp"
+	"go.opencensus.io/resource"
 	"k8s.io/apimachinery/pkg/types"
 	"knative.dev/pkg/metrics/metricskey"
 	"knative.dev/pkg/metrics/metricstest"
@@ -480,20 +481,26 @@ func TestMetricsReported(t *testing.T) {
 	<-cr.statCh
 	<-cr.statCh
 
+	wantResource := &resource.Resource{
+		Type: "knative_revision",
+		Labels: map[string]string{
+			metricskey.LabelRevisionName:      rev1.Name,
+			metricskey.LabelNamespaceName:     rev1.Namespace,
+			metricskey.LabelServiceName:       "service-" + rev1.Name,
+			metricskey.LabelConfigurationName: "config-" + rev1.Name,
+		},
+	}
 	wantTags := map[string]string{
-		metricskey.LabelRevisionName:      rev1.Name,
-		metricskey.LabelNamespaceName:     rev1.Namespace,
-		metricskey.LabelServiceName:       "service-" + rev1.Name,
-		metricskey.LabelConfigurationName: "config-" + rev1.Name,
-		metricskey.PodName:                "the-best-activator",
-		metricskey.ContainerName:          "activator",
+		metricskey.PodName:       "the-best-activator",
+		metricskey.ContainerName: "activator",
 	}
-	metricstest.CheckLastValueData(t, "request_concurrency", wantTags, 3)
 
+	wantMetric := metricstest.FloatMetric("request_concurrency", 3, wantTags).WithResource(wantResource)
+	metricstest.AssertMetric(t, wantMetric)
 	reportCh <- time.Now()
 	<-cr.statCh
-
-	metricstest.CheckLastValueData(t, "request_concurrency", wantTags, 4)
+	*wantMetric.Values[0].Float64++
+	metricstest.AssertMetric(t, wantMetric)
 }
 
 func newTestReporter(t *testing.T) (*ConcurrencyReporter, context.Context, context.CancelFunc) {
diff --git a/pkg/activator/handler/metric_handler_test.go b/pkg/activator/handler/metric_handler_test.go
index 63a0b9e7f..2e1b9f69e 100644
--- a/pkg/activator/handler/metric_handler_test.go
+++ b/pkg/activator/handler/metric_handler_test.go
@@ -25,6 +25,7 @@ import (
 	"strconv"
 	"testing"
 
+	"go.opencensus.io/resource"
 	"k8s.io/apimachinery/pkg/types"
 	"knative.dev/pkg/metrics/metricskey"
 	"knative.dev/pkg/metrics/metricstest"
@@ -94,18 +95,24 @@ func TestRequestMetricHandler(t *testing.T) {
 					labelCode = http.StatusInternalServerError
 				}
 
+				wantResource := &resource.Resource{
+					Type: "knative_revision",
+					Labels: map[string]string{
+						metricskey.LabelNamespaceName:     rev.Namespace,
+						metricskey.LabelServiceName:       rev.Labels[serving.ServiceLabelKey],
+						metricskey.LabelConfigurationName: rev.Labels[serving.ConfigurationLabelKey],
+						metricskey.LabelRevisionName:      rev.Name,
+					},
+				}
 				wantTags := map[string]string{
 					metricskey.PodName:                testPod,
 					metricskey.ContainerName:          activator.Name,
-					metricskey.LabelNamespaceName:     rev.Namespace,
-					metricskey.LabelServiceName:       rev.Labels[serving.ServiceLabelKey],
-					metricskey.LabelConfigurationName: rev.Labels[serving.ConfigurationLabelKey],
-					metricskey.LabelRevisionName:      rev.Name,
 					metricskey.LabelResponseCode:      strconv.Itoa(labelCode),
 					metricskey.LabelResponseCodeClass: strconv.Itoa(labelCode/100) + "xx",
 				}
-				metricstest.CheckCountData(t, requestCountM.Name(), wantTags, 1)
-				metricstest.CheckStatsReported(t, responseTimeInMsecM.Name())
+
+				metricstest.AssertMetric(t, metricstest.IntMetric(requestCountM.Name(), 1, wantTags).WithResource(wantResource))
+				metricstest.AssertMetricExists(t, responseTimeInMsecM.Name())
 			}()
 
 			reqCtx := util.WithRevision(context.Background(), rev)
diff --git a/pkg/autoscaler/scaling/autoscaler_test.go b/pkg/autoscaler/scaling/autoscaler_test.go
index aa657e95e..81e967279 100644
--- a/pkg/autoscaler/scaling/autoscaler_test.go
+++ b/pkg/autoscaler/scaling/autoscaler_test.go
@@ -24,6 +24,7 @@ import (
 	"time"
 
 	"github.com/google/go-cmp/cmp"
+	"go.opencensus.io/resource"
 
 	"k8s.io/apimachinery/pkg/types"
 
@@ -47,12 +48,14 @@ const (
 	testNamespace = "in-this-namespace"
 )
 
-// Standard set of tags for the tests.
-var wantTags = map[string]string{
-	metricskey.LabelConfigurationName: "testConfig",
-	metricskey.LabelNamespaceName:     testNamespace,
-	metricskey.LabelRevisionName:      testRevision,
-	metricskey.LabelServiceName:       "testSvc",
+var wantResource = &resource.Resource{
+	Type: "knative_revision",
+	Labels: map[string]string{
+		metricskey.LabelConfigurationName: "testConfig",
+		metricskey.LabelNamespaceName:     testNamespace,
+		metricskey.LabelRevisionName:      testRevision,
+		metricskey.LabelServiceName:       "testSvc",
+	},
 }
 
 type fakePodCounter struct {
@@ -109,7 +112,7 @@ func TestAutoscalerStartMetrics(t *testing.T) {
 	metrics := &metricClient{StableConcurrency: 50.0, PanicConcurrency: 50.0}
 	newTestAutoscalerWithScalingMetric(t, 10, 100, metrics,
 		"concurrency", true /*startInPanic*/)
-	metricstest.CheckLastValueData(t, panicM.Name(), wantTags, 1)
+	metricstest.AssertMetric(t, metricstest.IntMetric(panicM.Name(), 1, nil).WithResource(wantResource))
 }
 
 func TestAutoscalerMetrics(t *testing.T) {
@@ -118,18 +121,21 @@ func TestAutoscalerMetrics(t *testing.T) {
 	metrics := &metricClient{StableConcurrency: 50.0, PanicConcurrency: 50.0}
 	a := newTestAutoscalerNoPC(t, 10, 100, metrics)
 	// Non-panic created autoscaler.
-	metricstest.CheckLastValueData(t, panicM.Name(), wantTags, 0)
+	metricstest.AssertMetric(t, metricstest.IntMetric(panicM.Name(), 0, nil).WithResource(wantResource))
 	ebc := expectedEBC(10, 100, 50, 1)
 	na := expectedNA(a, 1)
 	expectScale(t, a, time.Now(), ScaleResult{5, ebc, na, true})
 	spec := a.currentSpec()
 
-	metricstest.CheckLastValueData(t, stableRequestConcurrencyM.Name(), wantTags, 50)
-	metricstest.CheckLastValueData(t, panicRequestConcurrencyM.Name(), wantTags, 50)
-	metricstest.CheckLastValueData(t, desiredPodCountM.Name(), wantTags, 5)
-	metricstest.CheckLastValueData(t, targetRequestConcurrencyM.Name(), wantTags, spec.TargetValue)
-	metricstest.CheckLastValueData(t, excessBurstCapacityM.Name(), wantTags, float64(ebc))
-	metricstest.CheckLastValueData(t, panicM.Name(), wantTags, 1)
+	wantMetrics := []metricstest.Metric{
+		metricstest.FloatMetric(stableRequestConcurrencyM.Name(), 50, nil).WithResource(wantResource),
+		metricstest.FloatMetric(panicRequestConcurrencyM.Name(), 50, nil).WithResource(wantResource),
+		metricstest.IntMetric(desiredPodCountM.Name(), 5, nil).WithResource(wantResource),
+		metricstest.FloatMetric(targetRequestConcurrencyM.Name(), spec.TargetValue, nil).WithResource(wantResource),
+		metricstest.FloatMetric(excessBurstCapacityM.Name(), float64(ebc), nil).WithResource(wantResource),
+		metricstest.IntMetric(panicM.Name(), 1, nil).WithResource(wantResource),
+	}
+	metricstest.AssertMetric(t, wantMetrics...)
 }
 
 func TestAutoscalerMetricsWithRPS(t *testing.T) {
@@ -142,12 +148,15 @@ func TestAutoscalerMetricsWithRPS(t *testing.T) {
 	spec := a.currentSpec()
 
 	expectScale(t, a, time.Now().Add(61*time.Second), ScaleResult{10, ebc, na, true})
-	metricstest.CheckLastValueData(t, stableRPSM.Name(), wantTags, 100)
-	metricstest.CheckLastValueData(t, panicRPSM.Name(), wantTags, 100)
-	metricstest.CheckLastValueData(t, desiredPodCountM.Name(), wantTags, 10)
-	metricstest.CheckLastValueData(t, targetRPSM.Name(), wantTags, spec.TargetValue)
-	metricstest.CheckLastValueData(t, excessBurstCapacityM.Name(), wantTags, float64(ebc))
-	metricstest.CheckLastValueData(t, panicM.Name(), wantTags, 1)
+	wantMetrics := []metricstest.Metric{
+		metricstest.FloatMetric(stableRPSM.Name(), 100, nil).WithResource(wantResource),
+		metricstest.FloatMetric(panicRPSM.Name(), 100, nil).WithResource(wantResource),
+		metricstest.IntMetric(desiredPodCountM.Name(), 10, nil).WithResource(wantResource),
+		metricstest.FloatMetric(targetRPSM.Name(), spec.TargetValue, nil).WithResource(wantResource),
+		metricstest.FloatMetric(excessBurstCapacityM.Name(), float64(ebc), nil).WithResource(wantResource),
+		metricstest.IntMetric(panicM.Name(), 1, nil).WithResource(wantResource),
+	}
+	metricstest.AssertMetric(t, wantMetrics...)
 }
 
 func TestAutoscalerStableModeIncreaseWithConcurrencyDefault(t *testing.T) {
diff --git a/pkg/metrics/tags_test.go b/pkg/metrics/tags_test.go
index 2fd1ce59b..827f90945 100644
--- a/pkg/metrics/tags_test.go
+++ b/pkg/metrics/tags_test.go
@@ -26,8 +26,10 @@ import (
 	"knative.dev/pkg/metrics/metricstest"
 	_ "knative.dev/pkg/metrics/testing"
 
+	"go.opencensus.io/resource"
 	"go.opencensus.io/stats"
 	"go.opencensus.io/stats/view"
+	"go.opencensus.io/tag"
 )
 
 var testM = stats.Int64(
@@ -41,7 +43,7 @@ func register(t *testing.T) func() {
 			Description: "Number of pods autoscaler wants to allocate",
 			Measure:     testM,
 			Aggregation: view.LastValue(),
-			TagKeys:     append(CommonRevisionKeys, ResponseCodeKey, ResponseCodeClassKey, PodTagKey, ContainerTagKey),
+			TagKeys:     []tag.Key{ResponseCodeKey, ResponseCodeClassKey, PodTagKey, ContainerTagKey},
 		}); err != nil {
 		t.Fatal("Failed to register view:", err)
 	}
@@ -61,23 +63,18 @@ func TestContextsErrors(t *testing.T) {
 		if _, err := PodContext(v, v); err == nil {
 			t.Errorf("PodContext(%q) = nil, wanted an error", v)
 		}
-		if _, err := RevisionContext(v, v, v, v); err == nil {
-			t.Errorf("RevisionContext(%q) = nil, wanted an error", v)
-		}
 		if _, err := PodRevisionContext(v, v, v, v, v, v); err == nil {
 			t.Errorf("PodRevisionContext(%q) = nil, wanted an error", v)
 		}
-		if _, err := AugmentWithRevision(context.Background(), v, v, v, v); err == nil {
-			t.Errorf("AugmentWithRevision(%q) = nil, wanted an error", v)
-		}
 	}
 }
 
 func TestContexts(t *testing.T) {
 	tests := []struct {
-		name     string
-		ctx      context.Context
-		wantTags map[string]string
+		name         string
+		ctx          context.Context
+		wantTags     map[string]string
+		wantResource *resource.Resource
 	}{{
 		name: "pod context",
 		ctx: mustCtx(t, func() (context.Context, error) {
@@ -92,22 +89,30 @@ func TestContexts(t *testing.T) {
 		ctx: mustCtx(t, func() (context.Context, error) {
 			return RevisionContext("testns", "testsvc", "testcfg", "testrev")
 		}),
-		wantTags: map[string]string{
-			metricskey.LabelNamespaceName:     "testns",
-			metricskey.LabelServiceName:       "testsvc",
-			metricskey.LabelConfigurationName: "testcfg",
-			metricskey.LabelRevisionName:      "testrev",
+		wantTags: map[string]string{},
+		wantResource: &resource.Resource{
+			Type: "knative_revision",
+			Labels: map[string]string{
+				metricskey.LabelNamespaceName:     "testns",
+				metricskey.LabelServiceName:       "testsvc",
+				metricskey.LabelConfigurationName: "testcfg",
+				metricskey.LabelRevisionName:      "testrev",
+			},
 		},
 	}, {
 		name: "revision context (empty svc)",
 		ctx: mustCtx(t, func() (context.Context, error) {
 			return RevisionContext("testns", "", "testcfg", "testrev")
 		}),
-		wantTags: map[string]string{
-			metricskey.LabelNamespaceName:     "testns",
-			metricskey.LabelServiceName:       metricskey.ValueUnknown,
-			metricskey.LabelConfigurationName: "testcfg",
-			metricskey.LabelRevisionName:      "testrev",
+		wantTags: map[string]string{},
+		wantResource: &resource.Resource{
+			Type: "knative_revision",
+			Labels: map[string]string{
+				metricskey.LabelNamespaceName:     "testns",
+				metricskey.LabelServiceName:       metricskey.ValueUnknown,
+				metricskey.LabelConfigurationName: "testcfg",
+				metricskey.LabelRevisionName:      "testrev",
+			},
 		},
 	}, {
 		name: "pod revision context",
@@ -115,12 +120,17 @@ func TestContexts(t *testing.T) {
 			return PodRevisionContext("testpod", "testcontainer", "testns", "testsvc", "testcfg", "testrev")
 		}),
 		wantTags: map[string]string{
-			metricskey.PodName:                "testpod",
-			metricskey.ContainerName:          "testcontainer",
-			metricskey.LabelNamespaceName:     "testns",
-			metricskey.LabelServiceName:       "testsvc",
-			metricskey.LabelConfigurationName: "testcfg",
-			metricskey.LabelRevisionName:      "testrev",
+			metricskey.PodName:       "testpod",
+			metricskey.ContainerName: "testcontainer",
+		},
+		wantResource: &resource.Resource{
+			Type: "knative_revision",
+			Labels: map[string]string{
+				metricskey.LabelNamespaceName:     "testns",
+				metricskey.LabelServiceName:       "testsvc",
+				metricskey.LabelConfigurationName: "testcfg",
+				metricskey.LabelRevisionName:      "testrev",
+			},
 		},
 	}, {
 		name: "pod revision context (empty svc)",
@@ -128,12 +138,17 @@ func TestContexts(t *testing.T) {
 			return PodRevisionContext("testpod", "testcontainer", "testns", "", "testcfg", "testrev")
 		}),
 		wantTags: map[string]string{
-			metricskey.PodName:                "testpod",
-			metricskey.ContainerName:          "testcontainer",
-			metricskey.LabelNamespaceName:     "testns",
-			metricskey.LabelServiceName:       metricskey.ValueUnknown,
-			metricskey.LabelConfigurationName: "testcfg",
-			metricskey.LabelRevisionName:      "testrev",
+			metricskey.PodName:       "testpod",
+			metricskey.ContainerName: "testcontainer",
+		},
+		wantResource: &resource.Resource{
+			Type: "knative_revision",
+			Labels: map[string]string{
+				metricskey.LabelNamespaceName:     "testns",
+				metricskey.LabelServiceName:       metricskey.ValueUnknown,
+				metricskey.LabelConfigurationName: "testcfg",
+				metricskey.LabelRevisionName:      "testrev",
+			},
 		},
 	}, {
 		name: "pod revision context (empty svc)",
@@ -141,12 +156,17 @@ func TestContexts(t *testing.T) {
 			return PodRevisionContext("testpod", "testcontainer", "testns", "", "testcfg", "testrev")
 		}),
 		wantTags: map[string]string{
-			metricskey.PodName:                "testpod",
-			metricskey.ContainerName:          "testcontainer",
-			metricskey.LabelNamespaceName:     "testns",
-			metricskey.LabelServiceName:       metricskey.ValueUnknown,
-			metricskey.LabelConfigurationName: "testcfg",
-			metricskey.LabelRevisionName:      "testrev",
+			metricskey.PodName:       "testpod",
+			metricskey.ContainerName: "testcontainer",
+		},
+		wantResource: &resource.Resource{
+			Type: "knative_revision",
+			Labels: map[string]string{
+				metricskey.LabelNamespaceName:     "testns",
+				metricskey.LabelServiceName:       metricskey.ValueUnknown,
+				metricskey.LabelConfigurationName: "testcfg",
+				metricskey.LabelRevisionName:      "testrev",
+			},
 		},
 	}, {
 		name: "pod context augmented with revision",
@@ -155,32 +175,42 @@ func TestContexts(t *testing.T) {
 			if err != nil {
 				return ctx, err
 			}
-			return AugmentWithRevision(ctx, "testns", "testsvc", "testcfg", "testrev")
+			return AugmentWithRevision(ctx, "testns", "testsvc", "testcfg", "testrev"), nil
 		}),
 		wantTags: map[string]string{
-			metricskey.PodName:                "testpod",
-			metricskey.ContainerName:          "testcontainer",
-			metricskey.LabelNamespaceName:     "testns",
-			metricskey.LabelServiceName:       "testsvc",
-			metricskey.LabelConfigurationName: "testcfg",
-			metricskey.LabelRevisionName:      "testrev",
+			metricskey.PodName:       "testpod",
+			metricskey.ContainerName: "testcontainer",
+		},
+		wantResource: &resource.Resource{
+			Type: "knative_revision",
+			Labels: map[string]string{
+				metricskey.LabelNamespaceName:     "testns",
+				metricskey.LabelServiceName:       "testsvc",
+				metricskey.LabelConfigurationName: "testcfg",
+				metricskey.LabelRevisionName:      "testrev",
+			},
 		},
 	}, {
 		name: "pod revision context augmented with response",
 		ctx: mustCtx(t, func() (context.Context, error) {
-			ctx, err := PodRevisionContext("testpod", "testcontainer", "testns", "", "testcfg", "testrev")
+			ctx, err := PodRevisionContext("testpod", "testcontainer", "testns", "testsvc", "testcfg", "testrev")
 			return AugmentWithResponse(ctx, 200), err
 		}),
 		wantTags: map[string]string{
 			metricskey.PodName:                "testpod",
 			metricskey.ContainerName:          "testcontainer",
-			metricskey.LabelNamespaceName:     "testns",
-			metricskey.LabelServiceName:       metricskey.ValueUnknown,
-			metricskey.LabelConfigurationName: "testcfg",
-			metricskey.LabelRevisionName:      "testrev",
 			metricskey.LabelResponseCode:      "200",
 			metricskey.LabelResponseCodeClass: "2xx",
 		},
+		wantResource: &resource.Resource{
+			Type: "knative_revision",
+			Labels: map[string]string{
+				metricskey.LabelNamespaceName:     "testns",
+				metricskey.LabelServiceName:       "testsvc",
+				metricskey.LabelConfigurationName: "testcfg",
+				metricskey.LabelRevisionName:      "testrev",
+			},
+		},
 	}}
 
 	for _, test := range tests {
@@ -189,7 +219,7 @@ func TestContexts(t *testing.T) {
 			defer cancel()
 
 			pkgmetrics.Record(test.ctx, testM.M(42))
-			metricstest.CheckLastValueData(t, "test_metric", test.wantTags, 42)
+			metricstest.AssertMetric(t, metricstest.IntMetric("test_metric", 42, test.wantTags).WithResource(test.wantResource))
 		})
 	}
 }
diff --git a/pkg/queue/request_metric_test.go b/pkg/queue/request_metric_test.go
index 0a1fd26b2..0c8b8842b 100644
--- a/pkg/queue/request_metric_test.go
+++ b/pkg/queue/request_metric_test.go
@@ -22,6 +22,7 @@ import (
 	"net/http/httptest"
 	"testing"
 
+	"go.opencensus.io/resource"
 	"knative.dev/pkg/metrics/metricskey"
 	"knative.dev/pkg/metrics/metricstest"
 	_ "knative.dev/pkg/metrics/testing"
@@ -31,8 +32,8 @@ import (
 const targetURI = "http://example.com"
 
 func TestNewRequestMetricsHandlerFailure(t *testing.T) {
-	if _, err := NewRequestMetricsHandler(nil /*next*/, "shøüld fail", "a", "b", "c", "d"); err == nil {
-		t.Error("Should get error when StatsReporter is empty")
+	if _, err := NewRequestMetricsHandler(nil /*next*/, "a", "b", "c", "d", "shøüld fail"); err == nil {
+		t.Error("Should get error when tag value is not ascii")
 	}
 }
 
@@ -49,25 +50,30 @@ func TestRequestMetricsHandler(t *testing.T) {
 	handler.ServeHTTP(resp, req)
 
 	wantTags := map[string]string{
-		metricskey.LabelNamespaceName:     "ns",
-		metricskey.LabelRevisionName:      "rev",
-		metricskey.LabelServiceName:       "svc",
-		metricskey.LabelConfigurationName: "cfg",
 		metricskey.PodName:                "pod",
 		metricskey.ContainerName:          "queue-proxy",
 		metricskey.LabelResponseCode:      "200",
 		metricskey.LabelResponseCodeClass: "2xx",
 		"tag":                             disabledTagName,
 	}
+	wantResource := &resource.Resource{
+		Type: "knative_revision",
+		Labels: map[string]string{
+			metricskey.LabelNamespaceName:     "ns",
+			metricskey.LabelRevisionName:      "rev",
+			metricskey.LabelServiceName:       "svc",
+			metricskey.LabelConfigurationName: "cfg",
+		},
+	}
 
-	metricstest.CheckCountData(t, "request_count", wantTags, 1)
-	metricstest.CheckDistributionCount(t, "request_latencies", wantTags, 1) // Dummy latency range.
+	metricstest.AssertMetric(t, metricstest.IntMetric("request_count", 1, wantTags).WithResource(wantResource))
+	metricstest.AssertMetric(t, metricstest.DistributionCountOnlyMetric("request_latencies", 1, wantTags).WithResource(wantResource))
 
 	// A probe request should not be recorded.
 	req.Header.Set(network.ProbeHeaderName, "activator")
 	handler.ServeHTTP(resp, req)
-	metricstest.CheckCountData(t, "request_count", wantTags, 1)
-	metricstest.CheckDistributionCount(t, "request_latencies", wantTags, 1)
+	metricstest.AssertMetric(t, metricstest.IntMetric("request_count", 1, wantTags).WithResource(wantResource))
+	metricstest.AssertMetric(t, metricstest.DistributionCountOnlyMetric("request_latencies", 1, wantTags).WithResource(wantResource))
 }
 
 func TestRequestMetricsHandlerWithEnablingTagOnRequestMetrics(t *testing.T) {
@@ -85,18 +91,23 @@ func TestRequestMetricsHandlerWithEnablingTagOnRequestMetrics(t *testing.T) {
 	handler.ServeHTTP(resp, req)
 
 	wantTags := map[string]string{
-		metricskey.LabelNamespaceName:     "ns",
-		metricskey.LabelRevisionName:      "rev",
-		metricskey.LabelServiceName:       "svc",
-		metricskey.LabelConfigurationName: "cfg",
 		metricskey.PodName:                "pod",
 		metricskey.ContainerName:          "queue-proxy",
 		metricskey.LabelResponseCode:      "200",
 		metricskey.LabelResponseCodeClass: "2xx",
 		"tag":                             "test-tag",
 	}
+	wantResource := &resource.Resource{
+		Type: "knative_revision",
+		Labels: map[string]string{
+			metricskey.LabelNamespaceName:     "ns",
+			metricskey.LabelRevisionName:      "rev",
+			metricskey.LabelServiceName:       "svc",
+			metricskey.LabelConfigurationName: "cfg",
+		},
+	}
 
-	metricstest.CheckCountData(t, "request_count", wantTags, 1)
+	metricstest.AssertMetric(t, metricstest.IntMetric("request_count", 1, wantTags).WithResource(wantResource))
 
 	// Testing for default route
 	reset()
@@ -105,7 +116,7 @@ func TestRequestMetricsHandlerWithEnablingTagOnRequestMetrics(t *testing.T) {
 	req.Header.Set(network.DefaultRouteHeaderName, "true")
 	handler.ServeHTTP(resp, req)
 	wantTags["tag"] = defaultTagName
-	metricstest.CheckCountData(t, "request_count", wantTags, 1)
+	metricstest.AssertMetric(t, metricstest.IntMetric("request_count", 1, wantTags).WithResource(wantResource))
 
 	reset()
 	handler, _ = NewRequestMetricsHandler(baseHandler, "ns", "svc", "cfg", "rev", "pod")
@@ -113,7 +124,7 @@ func TestRequestMetricsHandlerWithEnablingTagOnRequestMetrics(t *testing.T) {
 	req.Header.Set(network.DefaultRouteHeaderName, "true")
 	handler.ServeHTTP(resp, req)
 	wantTags["tag"] = undefinedTagName
-	metricstest.CheckCountData(t, "request_count", wantTags, 1)
+	metricstest.AssertMetric(t, metricstest.IntMetric("request_count", 1, wantTags).WithResource(wantResource))
 
 	reset()
 	handler, _ = NewRequestMetricsHandler(baseHandler, "ns", "svc", "cfg", "rev", "pod")
@@ -121,7 +132,7 @@ func TestRequestMetricsHandlerWithEnablingTagOnRequestMetrics(t *testing.T) {
 	req.Header.Set(network.DefaultRouteHeaderName, "false")
 	handler.ServeHTTP(resp, req)
 	wantTags["tag"] = "test-tag"
-	metricstest.CheckCountData(t, "request_count", wantTags, 1)
+	metricstest.AssertMetric(t, metricstest.IntMetric("request_count", 1, wantTags).WithResource(wantResource))
 }
 
 func reset() {
@@ -148,18 +159,23 @@ func TestRequestMetricsHandlerPanickingHandler(t *testing.T) {
 			t.Error("Want ServeHTTP to panic, got nothing.")
 		}
 		wantTags := map[string]string{
-			metricskey.LabelNamespaceName:     "ns",
-			metricskey.LabelRevisionName:      "rev",
-			metricskey.LabelServiceName:       "svc",
-			metricskey.LabelConfigurationName: "cfg",
 			metricskey.PodName:                "pod",
 			metricskey.ContainerName:          "queue-proxy",
 			metricskey.LabelResponseCode:      "500",
 			metricskey.LabelResponseCodeClass: "5xx",
 			"tag":                             disabledTagName,
 		}
-		metricstest.CheckCountData(t, "request_count", wantTags, 1)
-		metricstest.CheckDistributionCount(t, "request_latencies", wantTags, 1) // Dummy latency range.
+		wantResource := &resource.Resource{
+			Type: "knative_revision",
+			Labels: map[string]string{
+				metricskey.LabelNamespaceName:     "ns",
+				metricskey.LabelRevisionName:      "rev",
+				metricskey.LabelServiceName:       "svc",
+				metricskey.LabelConfigurationName: "cfg",
+			},
+		}
+		metricstest.AssertMetric(t, metricstest.IntMetric("request_count", 1, wantTags).WithResource(wantResource))
+		metricstest.AssertMetric(t, metricstest.DistributionCountOnlyMetric("request_latencies", 1, wantTags).WithResource(wantResource))
 	}()
 	handler.ServeHTTP(resp, req)
 }
@@ -211,17 +227,23 @@ func TestAppRequestMetricsHandlerPanickingHandler(t *testing.T) {
 			t.Error("Want ServeHTTP to panic, got nothing.")
 		}
 		wantTags := map[string]string{
-			metricskey.LabelNamespaceName:     "ns",
-			metricskey.LabelRevisionName:      "rev",
-			metricskey.LabelServiceName:       "svc",
-			metricskey.LabelConfigurationName: "cfg",
 			metricskey.PodName:                "pod",
 			metricskey.ContainerName:          "queue-proxy",
 			metricskey.LabelResponseCode:      "500",
 			metricskey.LabelResponseCodeClass: "5xx",
 		}
-		metricstest.CheckCountData(t, "app_request_count", wantTags, 1)
-		metricstest.CheckDistributionCount(t, "app_request_latencies", wantTags, 1) // Dummy latency range.
+		wantResource := &resource.Resource{
+			Type: "knative_revision",
+			Labels: map[string]string{
+				metricskey.LabelNamespaceName:     "ns",
+				metricskey.LabelRevisionName:      "rev",
+				metricskey.LabelServiceName:       "svc",
+				metricskey.LabelConfigurationName: "cfg",
+			},
+		}
+
+		metricstest.AssertMetric(t, metricstest.IntMetric("app_request_count", 1, wantTags).WithResource(wantResource))
+		metricstest.AssertMetric(t, metricstest.DistributionCountOnlyMetric("app_request_latencies", 1, wantTags).WithResource(wantResource))
 	}()
 	handler.ServeHTTP(resp, req)
 }
@@ -241,24 +263,29 @@ func TestAppRequestMetricsHandler(t *testing.T) {
 	handler.ServeHTTP(resp, req)
 
 	wantTags := map[string]string{
-		metricskey.LabelNamespaceName:     "ns",
-		metricskey.LabelRevisionName:      "rev",
-		metricskey.LabelServiceName:       "svc",
-		metricskey.LabelConfigurationName: "cfg",
 		metricskey.PodName:                "pod",
 		metricskey.ContainerName:          "queue-proxy",
 		metricskey.LabelResponseCode:      "200",
 		metricskey.LabelResponseCodeClass: "2xx",
 	}
+	wantResource := &resource.Resource{
+		Type: "knative_revision",
+		Labels: map[string]string{
+			metricskey.LabelNamespaceName:     "ns",
+			metricskey.LabelRevisionName:      "rev",
+			metricskey.LabelServiceName:       "svc",
+			metricskey.LabelConfigurationName: "cfg",
+		},
+	}
 
-	metricstest.CheckCountData(t, "app_request_count", wantTags, 1)
-	metricstest.CheckDistributionCount(t, "app_request_latencies", wantTags, 1) // Dummy latency range.
+	metricstest.AssertMetric(t, metricstest.IntMetric("app_request_count", 1, wantTags).WithResource(wantResource))
+	metricstest.AssertMetric(t, metricstest.DistributionCountOnlyMetric("app_request_latencies", 1, wantTags).WithResource(wantResource))
 
 	// A probe request should not be recorded.
 	req.Header.Set(network.ProbeHeaderName, "activator")
 	handler.ServeHTTP(resp, req)
-	metricstest.CheckCountData(t, "app_request_count", wantTags, 1)
-	metricstest.CheckDistributionCount(t, "app_request_latencies", wantTags, 1)
+	metricstest.AssertMetric(t, metricstest.IntMetric("app_request_count", 1, wantTags).WithResource(wantResource))
+	metricstest.AssertMetric(t, metricstest.DistributionCountOnlyMetric("app_request_latencies", 1, wantTags).WithResource(wantResource))
 }
 
 func BenchmarkRequestMetricsHandler(b *testing.B) {
diff --git a/pkg/reconciler/autoscaling/kpa/kpa_test.go b/pkg/reconciler/autoscaling/kpa/kpa_test.go
index 2a46271e8..6a4e87065 100644
--- a/pkg/reconciler/autoscaling/kpa/kpa_test.go
+++ b/pkg/reconciler/autoscaling/kpa/kpa_test.go
@@ -54,6 +54,7 @@ import (
 	clientgotesting "k8s.io/client-go/testing"
 
 	"github.com/google/go-cmp/cmp"
+	"go.opencensus.io/resource"
 	"go.uber.org/atomic"
 	"golang.org/x/sync/errgroup"
 
@@ -1760,11 +1761,14 @@ var _ reconciler.ConfigStore = (*testConfigStore)(nil)
 
 func TestMetricsReporter(t *testing.T) {
 	pa := kpa(testNamespace, testRevision)
-	wantTags := map[string]string{
-		metricskey.LabelRevisionName:      testRevision,
-		metricskey.LabelNamespaceName:     testNamespace,
-		metricskey.LabelServiceName:       pa.Labels[serving.ServiceLabelKey],
-		metricskey.LabelConfigurationName: pa.Labels[serving.ConfigurationLabelKey],
+	wantResource := &resource.Resource{
+		Type: "knative_revision",
+		Labels: map[string]string{
+			metricskey.LabelRevisionName:      testRevision,
+			metricskey.LabelNamespaceName:     testNamespace,
+			metricskey.LabelServiceName:       pa.Labels[serving.ServiceLabelKey],
+			metricskey.LabelConfigurationName: pa.Labels[serving.ConfigurationLabelKey],
+		},
 	}
 	pc := podCounts{
 		want:        1982,
@@ -1774,11 +1778,14 @@ func TestMetricsReporter(t *testing.T) {
 		terminating: 1983,
 	}
 	reportMetrics(pa, pc)
-	metricstest.CheckLastValueData(t, "requested_pods", wantTags, 1982)
-	metricstest.CheckLastValueData(t, "actual_pods", wantTags, 1984)
-	metricstest.CheckLastValueData(t, "not_ready_pods", wantTags, 1988)
-	metricstest.CheckLastValueData(t, "pending_pods", wantTags, 1996)
-	metricstest.CheckLastValueData(t, "terminating_pods", wantTags, 1983)
+	wantMetrics := []metricstest.Metric{
+		metricstest.IntMetric("requested_pods", 1982, nil).WithResource(wantResource),
+		metricstest.IntMetric("actual_pods", 1984, nil).WithResource(wantResource),
+		metricstest.IntMetric("not_ready_pods", 1988, nil).WithResource(wantResource),
+		metricstest.IntMetric("pending_pods", 1996, nil).WithResource(wantResource),
+		metricstest.IntMetric("terminating_pods", 1983, nil).WithResource(wantResource),
+	}
+	metricstest.AssertMetric(t, wantMetrics...)
 
 	// Verify `want` is ignored, when it is equal to -1.
 	pc.want = -1
@@ -1786,11 +1793,14 @@ func TestMetricsReporter(t *testing.T) {
 	reportMetrics(pa, pc)
 
 	// Basically same values and change to `terminating` to verify reporting has occurred.
-	metricstest.CheckLastValueData(t, "requested_pods", wantTags, 1982)
-	metricstest.CheckLastValueData(t, "actual_pods", wantTags, 1984)
-	metricstest.CheckLastValueData(t, "not_ready_pods", wantTags, 1988)
-	metricstest.CheckLastValueData(t, "pending_pods", wantTags, 1996)
-	metricstest.CheckLastValueData(t, "terminating_pods", wantTags, 1955)
+	wantMetrics = []metricstest.Metric{
+		metricstest.IntMetric("requested_pods", 1982, nil).WithResource(wantResource),
+		metricstest.IntMetric("actual_pods", 1984, nil).WithResource(wantResource),
+		metricstest.IntMetric("not_ready_pods", 1988, nil).WithResource(wantResource),
+		metricstest.IntMetric("pending_pods", 1996, nil).WithResource(wantResource),
+		metricstest.IntMetric("terminating_pods", 1955, nil).WithResource(wantResource),
+	}
+	metricstest.AssertMetric(t, wantMetrics...)
 }
 
 func TestResolveScrapeTarget(t *testing.T) {
diff --git a/pkg/reconciler/labeler/labeler_both_test.go b/pkg/reconciler/labeler/labeler_both_test.go
new file mode 100644
index 000000000..d25013eda
--- /dev/null
+++ b/pkg/reconciler/labeler/labeler_both_test.go
@@ -0,0 +1,123 @@
+/*
+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.
+*/
+
+package labeler
+
+import (
+	"context"
+	"testing"
+
+	// Inject the fake informers that this controller needs.
+	servingclient "knative.dev/serving/pkg/client/injection/client/fake"
+	_ "knative.dev/serving/pkg/client/injection/informers/serving/v1/configuration/fake"
+	_ "knative.dev/serving/pkg/client/injection/informers/serving/v1/revision/fake"
+	_ "knative.dev/serving/pkg/client/injection/informers/serving/v1/route/fake"
+
+	corev1 "k8s.io/api/core/v1"
+	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+	"k8s.io/apimachinery/pkg/runtime"
+	"k8s.io/apimachinery/pkg/util/clock"
+	clientgotesting "k8s.io/client-go/testing"
+	routereconciler "knative.dev/serving/pkg/client/injection/reconciler/serving/v1/route"
+
+	"knative.dev/pkg/configmap"
+	"knative.dev/pkg/controller"
+	"knative.dev/pkg/logging"
+	cfgmap "knative.dev/serving/pkg/apis/config"
+	v1 "knative.dev/serving/pkg/apis/serving/v1"
+
+	. "knative.dev/pkg/reconciler/testing"
+	labelerv1 "knative.dev/serving/pkg/reconciler/labeler/v1"
+	labelerv2 "knative.dev/serving/pkg/reconciler/labeler/v2"
+	. "knative.dev/serving/pkg/reconciler/testing/v1"
+	. "knative.dev/serving/pkg/testing/v1"
+)
+
+func TestV2ReconcileAllowed(t *testing.T) {
+	now := metav1.Now()
+	fakeTime := now.Time
+
+	table := TableTest{{
+		Name: "change configurations",
+		Ctx:  setResponsiveGCFeature(context.Background(), cfgmap.Allowed),
+		Objects: []runtime.Object{
+			simpleRunLatest("default", "config-change", "new-config", WithRouteFinalizer),
+
+			rev("default", "old-config",
+				WithRevisionLabel("serving.knative.dev/route", "config-change"),
+				WithRevisionAnn("serving.knative.dev/routes", "config-change"),
+				WithRoutingState(v1.RoutingStateActive)),
+
+			simpleConfig("default", "new-config"),
+			rev("default", "new-config"),
+		},
+		WantPatches: []clientgotesting.PatchActionImpl{
+			// v1 sync
+			patchRemoveLabel("default", rev("default", "old-config").Name,
+				"serving.knative.dev/route"),
+			patchAddLabel("default", rev("default", "new-config").Name, "serving.knative.dev/route", "config-change"),
+			patchAddLabel("default", "new-config", "serving.knative.dev/route", "config-change"),
+
+			// v2 sync
+			patchRemoveRouteAnn("default", rev("default", "old-config").Name),
+			patchAddRouteAndServingStateLabel(
+				"default", rev("default", "new-config").Name, "config-change", now.Time),
+			patchAddRouteAnn("default", "new-config", "config-change"),
+		},
+		Key: "default/config-change",
+	}, {
+		Name: "failure while removing a rev annotation should return an error",
+		Ctx:  setResponsiveGCFeature(context.Background(), cfgmap.Allowed),
+		// Induce a failure during patching
+		WantErr: true,
+		WithReactors: []clientgotesting.ReactionFunc{
+			InduceFailure("patch", "revisions"),
+		},
+		Objects: []runtime.Object{
+			simpleRunLatest("default", "add-label-failure", "new-config", WithRouteFinalizer),
+			simpleConfig("default", "new-config",
+				WithConfigLabel("serving.knative.dev/route", "add-label-failure")),
+			rev("default", "new-config"),
+		},
+		WantPatches: []clientgotesting.PatchActionImpl{
+			patchAddLabel("default", rev("default", "new-config").Name,
+				"serving.knative.dev/route", "add-label-failure"),
+		},
+		WantEvents: []string{
+			Eventf(corev1.EventTypeWarning, "InternalError",
+				`failed to add route label to /, Kind= "new-config-dbnfd": inducing failure for patch revisions`),
+		},
+		Key: "default/add-label-failure",
+	}}
+
+	table.Test(t, MakeFactory(func(ctx context.Context, listers *Listers, cmw configmap.Watcher) controller.Reconciler {
+		clock := clock.NewFakeClock(fakeTime)
+		client := servingclient.Get(ctx)
+		cLister := listers.GetConfigurationLister()
+		cIndexer := listers.IndexerFor(&v1.Configuration{})
+		rLister := listers.GetRevisionLister()
+		rIndexer := listers.IndexerFor(&v1.Revision{})
+		r := &Reconciler{
+			caccV1: labelerv1.NewConfigurationAccessor(client, &NullTracker{}, cLister),
+			caccV2: labelerv2.NewConfigurationAccessor(client, &NullTracker{}, cLister, cIndexer, clock),
+			raccV1: labelerv1.NewRevisionAccessor(client, &NullTracker{}, rLister),
+			raccV2: labelerv2.NewRevisionAccessor(client, &NullTracker{}, rLister, rIndexer, clock),
+		}
+
+		return routereconciler.NewReconciler(ctx, logging.FromContext(ctx), servingclient.Get(ctx),
+			listers.GetRouteLister(), controller.GetEventRecorder(ctx), r)
+	}))
+}
diff --git a/pkg/reconciler/labeler/labelerv1_test.go b/pkg/reconciler/labeler/labelerv1_test.go
index c0ef4758a..58cff5220 100644
--- a/pkg/reconciler/labeler/labelerv1_test.go
+++ b/pkg/reconciler/labeler/labelerv1_test.go
@@ -18,6 +18,7 @@ package labeler
 
 import (
 	"context"
+	"fmt"
 	"testing"
 
 	// Inject the fake informers that this controller needs.
@@ -29,6 +30,7 @@ import (
 	corev1 "k8s.io/api/core/v1"
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 	"k8s.io/apimachinery/pkg/runtime"
+	"k8s.io/apimachinery/pkg/util/clock"
 	clientgotesting "k8s.io/client-go/testing"
 	routereconciler "knative.dev/serving/pkg/client/injection/reconciler/serving/v1/route"
 
@@ -36,15 +38,18 @@ import (
 	"knative.dev/pkg/controller"
 	"knative.dev/pkg/logging"
 	cfgmap "knative.dev/serving/pkg/apis/config"
+	v1 "knative.dev/serving/pkg/apis/serving/v1"
 
 	. "knative.dev/pkg/reconciler/testing"
+	labelerv1 "knative.dev/serving/pkg/reconciler/labeler/v1"
+	labelerv2 "knative.dev/serving/pkg/reconciler/labeler/v2"
 	. "knative.dev/serving/pkg/reconciler/testing/v1"
 	. "knative.dev/serving/pkg/testing/v1"
 )
 
-// This is heavily based on the way the OpenShift Ingress controller tests its reconciliation method.
 func TestV1Reconcile(t *testing.T) {
 	now := metav1.Now()
+	fakeTime := now.Time
 
 	table := TableTest{{
 		Name: "bad workqueue key",
@@ -259,6 +264,26 @@ func TestV1Reconcile(t *testing.T) {
 			Eventf(corev1.EventTypeNormal, "FinalizerUpdate", `Updated "delete-route" finalizers`),
 		},
 		Key: "default/delete-route",
+	}, {
+		Name:    "delete route failure",
+		Ctx:     setResponsiveGCFeature(context.Background(), cfgmap.Disabled),
+		WantErr: true,
+		WithReactors: []clientgotesting.ReactionFunc{
+			InduceFailure("patch", "configurations"),
+		},
+		Objects: []runtime.Object{
+			simpleRunLatest("default", "delete-route", "the-config", WithRouteFinalizer, WithRouteDeletionTimestamp(&now)),
+			simpleConfig("default", "the-config",
+				WithConfigLabel("serving.knative.dev/route", "delete-route")),
+		},
+		WantPatches: []clientgotesting.PatchActionImpl{
+			patchRemoveLabel("default", "the-config", "serving.knative.dev/route"),
+		},
+		WantEvents: []string{
+			Eventf(corev1.EventTypeWarning, "InternalError",
+				`failed to remove route label to /, Kind= "the-config": inducing failure for patch configurations`),
+		},
+		Key: "default/delete-route",
 	}, {
 		Name: "failure while removing a cfg annotation should return an error",
 		Ctx:  setResponsiveGCFeature(context.Background(), cfgmap.Disabled),
@@ -317,14 +342,36 @@ func TestV1Reconcile(t *testing.T) {
 
 	table.Test(t, MakeFactory(func(ctx context.Context, listers *Listers, cmw configmap.Watcher) controller.Reconciler {
 		ctx = setResponsiveGCFeature(ctx, cfgmap.Disabled)
+		clock := clock.NewFakeClock(fakeTime)
+		client := servingclient.Get(ctx)
+		cLister := listers.GetConfigurationLister()
+		cIndexer := listers.IndexerFor(&v1.Configuration{})
+		rLister := listers.GetRevisionLister()
+		rIndexer := listers.IndexerFor(&v1.Revision{})
 		r := &Reconciler{
-			client:              servingclient.Get(ctx),
-			configurationLister: listers.GetConfigurationLister(),
-			revisionLister:      listers.GetRevisionLister(),
-			tracker:             &NullTracker{},
+			caccV1: labelerv1.NewConfigurationAccessor(client, &NullTracker{}, cLister),
+			caccV2: labelerv2.NewConfigurationAccessor(client, &NullTracker{}, cLister, cIndexer, clock),
+			raccV1: labelerv1.NewRevisionAccessor(client, &NullTracker{}, rLister),
+			raccV2: labelerv2.NewRevisionAccessor(client, &NullTracker{}, rLister, rIndexer, clock),
 		}
 
 		return routereconciler.NewReconciler(ctx, logging.FromContext(ctx), servingclient.Get(ctx),
 			listers.GetRouteLister(), controller.GetEventRecorder(ctx), r)
 	}))
 }
+
+func patchRemoveLabel(namespace, name, key string) clientgotesting.PatchActionImpl {
+	return clientgotesting.PatchActionImpl{
+		Name:       name,
+		ActionImpl: clientgotesting.ActionImpl{Namespace: namespace},
+		Patch:      []byte(fmt.Sprintf(`{"metadata":{"labels":{%q:null}}}`, key)),
+	}
+}
+
+func patchAddLabel(namespace, name, key, value string) clientgotesting.PatchActionImpl {
+	return clientgotesting.PatchActionImpl{
+		Name:       name,
+		ActionImpl: clientgotesting.ActionImpl{Namespace: namespace},
+		Patch:      []byte(fmt.Sprintf(`{"metadata":{"labels":{%q:%q}}}`, key, value)),
+	}
+}
diff --git a/pkg/reconciler/labeler/labelerv2_test.go b/pkg/reconciler/labeler/labelerv2_test.go
index a3d9c589a..04cf7869d 100644
--- a/pkg/reconciler/labeler/labelerv2_test.go
+++ b/pkg/reconciler/labeler/labelerv2_test.go
@@ -44,11 +44,12 @@ import (
 	v1 "knative.dev/serving/pkg/apis/serving/v1"
 
 	. "knative.dev/pkg/reconciler/testing"
+	labelerv1 "knative.dev/serving/pkg/reconciler/labeler/v1"
+	labelerv2 "knative.dev/serving/pkg/reconciler/labeler/v2"
 	. "knative.dev/serving/pkg/reconciler/testing/v1"
 	. "knative.dev/serving/pkg/testing/v1"
 )
 
-// This is heavily based on the way the OpenShift Ingress controller tests its reconciliation method.
 func TestV2Reconcile(t *testing.T) {
 	now := metav1.Now()
 	fakeTime := now.Time
@@ -75,7 +76,7 @@ func TestV2Reconcile(t *testing.T) {
 			patchAddFinalizerAction("default", "first-reconcile"),
 			patchAddRouteAndServingStateLabel(
 				"default", rev("default", "the-config").Name, "first-reconcile", now.Time),
-			patchAddLabel("default", "the-config", "serving.knative.dev/route", "first-reconcile"),
+			patchAddRouteAnn("default", "the-config", "first-reconcile"),
 		},
 		WantEvents: []string{
 			Eventf(corev1.EventTypeNormal, "FinalizerUpdate", "Updated %q finalizers", "first-reconcile"),
@@ -94,8 +95,8 @@ func TestV2Reconcile(t *testing.T) {
 			patchAddFinalizerAction("default", "pinned-revision"),
 			patchAddRouteAndServingStateLabel(
 				"default", "the-revision", "pinned-revision", now.Time),
-			patchAddLabel("default", "the-config",
-				"serving.knative.dev/route", "pinned-revision"),
+			patchAddRouteAnn("default", "the-config",
+				"pinned-revision"),
 		},
 		WantEvents: []string{
 			Eventf(corev1.EventTypeNormal, "FinalizerUpdate", "Updated %q finalizers", "pinned-revision"),
@@ -107,9 +108,9 @@ func TestV2Reconcile(t *testing.T) {
 		Objects: []runtime.Object{
 			simpleRunLatest("default", "steady-state", "the-config", WithRouteFinalizer),
 			simpleConfig("default", "the-config",
-				WithConfigLabel("serving.knative.dev/route", "steady-state")),
+				WithConfigAnn("serving.knative.dev/routes", "steady-state")),
 			rev("default", "the-config",
-				WithRevisionLabel("serving.knative.dev/route", "steady-state"),
+				WithRevisionAnn("serving.knative.dev/routes", "steady-state"),
 				WithRoutingState(v1.RoutingStateActive),
 				WithRoutingStateModified(now.Time)),
 		},
@@ -126,8 +127,8 @@ func TestV2Reconcile(t *testing.T) {
 			patchAddFinalizerAction("default", "no-ready-revision"),
 			patchAddRouteAndServingStateLabel(
 				"default", rev("default", "the-config").Name, "no-ready-revision", now.Time),
-			patchAddLabel("default", "the-config",
-				"serving.knative.dev/route", "no-ready-revision"),
+			patchAddRouteAnn("default", "the-config",
+				"no-ready-revision"),
 		},
 		WantEvents: []string{
 			Eventf(corev1.EventTypeNormal, "FinalizerUpdate", "Updated %q finalizers", "no-ready-revision"),
@@ -140,9 +141,9 @@ func TestV2Reconcile(t *testing.T) {
 			simpleRunLatest("default", "transitioning-route", "old", WithRouteFinalizer,
 				WithSpecTraffic(configTraffic("new"))),
 			simpleConfig("default", "old",
-				WithConfigLabel("serving.knative.dev/route", "transitioning-route")),
+				WithConfigAnn("serving.knative.dev/routes", "transitioning-route")),
 			rev("default", "old",
-				WithRevisionLabel("serving.knative.dev/route", "transitioning-route"),
+				WithRevisionAnn("serving.knative.dev/routes", "transitioning-route"),
 				WithRoutingState(v1.RoutingStateActive)),
 			simpleConfig("default", "new"),
 			rev("default", "new"),
@@ -150,12 +151,12 @@ func TestV2Reconcile(t *testing.T) {
 		WantPatches: []clientgotesting.PatchActionImpl{
 			patchAddRouteAndServingStateLabel(
 				"default", rev("default", "new").Name, "transitioning-route", now.Time),
-			patchAddLabel("default", "new",
-				"serving.knative.dev/route", "transitioning-route"),
+			patchAddRouteAnn("default", "new",
+				"transitioning-route"),
 		},
 		Key: "default/transitioning-route",
 	}, {
-		Name: "failure adding label (revision)",
+		Name: "failure adding annotation (revision)",
 		Ctx:  setResponsiveGCFeature(context.Background(), cfgmap.Enabled),
 		// Induce a failure during patching
 		WantErr: true,
@@ -173,11 +174,11 @@ func TestV2Reconcile(t *testing.T) {
 		},
 		WantEvents: []string{
 			Eventf(corev1.EventTypeWarning, "InternalError",
-				`failed to add route label to Namespace=default Name="the-config-dbnfd": inducing failure for patch revisions`),
+				`failed to add route annotation to Namespace=default Name="the-config-dbnfd": inducing failure for patch revisions`),
 		},
 		Key: "default/add-label-failure",
 	}, {
-		Name: "failure adding label (configuration)",
+		Name: "failure adding annotation (configuration)",
 		Ctx:  setResponsiveGCFeature(context.Background(), cfgmap.Enabled),
 		// Induce a failure during patching
 		WantErr: true,
@@ -188,55 +189,53 @@ func TestV2Reconcile(t *testing.T) {
 			simpleRunLatest("default", "add-label-failure", "the-config", WithRouteFinalizer),
 			simpleConfig("default", "the-config"),
 			rev("default", "the-config",
-				WithRevisionLabel("serving.knative.dev/route", "add-label-failure"),
+				WithRevisionAnn("serving.knative.dev/routes", "add-label-failure"),
 				WithRoutingState(v1.RoutingStateActive),
 				WithRoutingStateModified(now.Time)),
 		},
 		WantPatches: []clientgotesting.PatchActionImpl{
-			patchAddLabel("default", "the-config", "serving.knative.dev/route", "add-label-failure"),
+			patchAddRouteAnn("default", "the-config", "add-label-failure"),
 		},
 		WantEvents: []string{
 			Eventf(corev1.EventTypeWarning, "InternalError",
-				`failed to add route label to Namespace=default Name="the-config": inducing failure for patch configurations`),
+				`failed to add route annotation to Namespace=default Name="the-config": inducing failure for patch configurations`),
 		},
 		Key: "default/add-label-failure",
 	}, {
-		Name:    "label config with incorrect label",
-		Ctx:     setResponsiveGCFeature(context.Background(), cfgmap.Enabled),
-		WantErr: true,
+		Name: "delete route existing ann",
+		Ctx:  setResponsiveGCFeature(context.Background(), cfgmap.Enabled),
 		Objects: []runtime.Object{
-			simpleRunLatest("default", "the-route", "the-config", WithRouteFinalizer),
+			simpleRunLatest("default", "delete-route", "the-config", WithRouteFinalizer, WithRouteDeletionTimestamp(&now)),
 			simpleConfig("default", "the-config",
-				WithConfigLabel("serving.knative.dev/route", "another-route")),
-			rev("default", "the-config",
-				WithRevisionLabel("serving.knative.dev/route", "another-route")),
+				WithConfigAnn("serving.knative.dev/routes", "delete-route,another-route")),
+		},
+		WantPatches: []clientgotesting.PatchActionImpl{
+			patchAddRouteAnn("default", "the-config", "another-route"),
+			patchRemoveFinalizerAction("default", "delete-route"),
 		},
 		WantEvents: []string{
-			Eventf(corev1.EventTypeWarning, "InternalError",
-				`failed to add route label to Namespace=default Name="the-config-dbnfd": `+
-					`resource already has route label "another-route", and cannot be referenced by "the-route"`),
+			Eventf(corev1.EventTypeNormal, "FinalizerUpdate", `Updated "delete-route" finalizers`),
 		},
-		Key: "default/the-route",
+		Key: "default/delete-route",
 	}, {
 		Name: "change configurations",
 		Ctx:  setResponsiveGCFeature(context.Background(), cfgmap.Enabled),
 		Objects: []runtime.Object{
 			simpleRunLatest("default", "config-change", "new-config", WithRouteFinalizer),
 			simpleConfig("default", "old-config",
-				WithConfigLabel("serving.knative.dev/route", "config-change")),
+				WithConfigAnn("serving.knative.dev/routes", "config-change")),
 			rev("default", "old-config",
-				WithRevisionLabel("serving.knative.dev/route", "config-change"),
+				WithRevisionAnn("serving.knative.dev/routes", "config-change"),
 				WithRoutingState(v1.RoutingStateActive)),
 			simpleConfig("default", "new-config"),
 			rev("default", "new-config"),
 		},
 		WantPatches: []clientgotesting.PatchActionImpl{
-			patchRemoveRouteAndServingStateLabel(
-				"default", rev("default", "old-config").Name, now.Time),
+			patchRemoveRouteAnn("default", rev("default", "old-config").Name),
 			patchAddRouteAndServingStateLabel(
 				"default", rev("default", "new-config").Name, "config-change", now.Time),
-			patchRemoveLabel("default", "old-config", "serving.knative.dev/route"),
-			patchAddLabel("default", "new-config", "serving.knative.dev/route", "config-change"),
+			patchRemoveRouteAnn("default", "old-config"),
+			patchAddRouteAnn("default", "new-config", "config-change"),
 		},
 		Key: "default/config-change",
 	}, {
@@ -246,9 +245,9 @@ func TestV2Reconcile(t *testing.T) {
 			simpleRunLatest("default", "config-update", "the-config", WithRouteFinalizer),
 			simpleConfig("default", "the-config",
 				WithLatestCreated("the-config-ecoge"),
-				WithConfigLabel("serving.knative.dev/route", "config-update")),
+				WithConfigAnn("serving.knative.dev/routes", "config-update")),
 			rev("default", "the-config",
-				WithRevisionLabel("serving.knative.dev/route", "config-update"),
+				WithRevisionAnn("serving.knative.dev/routes", "config-update"),
 				WithRoutingState(v1.RoutingStateActive)),
 			rev("default", "the-config",
 				WithRevName("the-config-ecoge")),
@@ -264,16 +263,36 @@ func TestV2Reconcile(t *testing.T) {
 		Objects: []runtime.Object{
 			simpleRunLatest("default", "delete-route", "the-config", WithRouteFinalizer, WithRouteDeletionTimestamp(&now)),
 			simpleConfig("default", "the-config",
-				WithConfigLabel("serving.knative.dev/route", "delete-route")),
+				WithConfigAnn("serving.knative.dev/routes", "delete-route")),
 		},
 		WantPatches: []clientgotesting.PatchActionImpl{
-			patchRemoveLabel("default", "the-config", "serving.knative.dev/route"),
+			patchRemoveRouteAnn("default", "the-config"),
 			patchRemoveFinalizerAction("default", "delete-route"),
 		},
 		WantEvents: []string{
 			Eventf(corev1.EventTypeNormal, "FinalizerUpdate", `Updated "delete-route" finalizers`),
 		},
 		Key: "default/delete-route",
+	}, {
+		Name:    "delete route failure",
+		Ctx:     setResponsiveGCFeature(context.Background(), cfgmap.Enabled),
+		WantErr: true,
+		WithReactors: []clientgotesting.ReactionFunc{
+			InduceFailure("patch", "configurations"),
+		},
+		Objects: []runtime.Object{
+			simpleRunLatest("default", "delete-route", "the-config", WithRouteFinalizer, WithRouteDeletionTimestamp(&now)),
+			simpleConfig("default", "the-config",
+				WithConfigAnn("serving.knative.dev/routes", "delete-route")),
+		},
+		WantPatches: []clientgotesting.PatchActionImpl{
+			patchRemoveRouteAnn("default", "the-config"),
+		},
+		WantEvents: []string{
+			Eventf(corev1.EventTypeWarning, "InternalError",
+				`failed to remove route annotation to /, Kind= "the-config": inducing failure for patch configurations`),
+		},
+		Key: "default/delete-route",
 	}, {
 		Name: "failure while removing a cfg annotation should return an error",
 		Ctx:  setResponsiveGCFeature(context.Background(), cfgmap.Enabled),
@@ -285,21 +304,21 @@ func TestV2Reconcile(t *testing.T) {
 		Objects: []runtime.Object{
 			simpleRunLatest("default", "delete-label-failure", "new-config", WithRouteFinalizer),
 			simpleConfig("default", "old-config",
-				WithConfigLabel("serving.knative.dev/route", "delete-label-failure")),
+				WithConfigAnn("serving.knative.dev/routes", "delete-label-failure")),
 			simpleConfig("default", "new-config",
-				WithConfigLabel("serving.knative.dev/route", "delete-label-failure")),
+				WithConfigAnn("serving.knative.dev/routes", "delete-label-failure")),
 			rev("default", "new-config",
-				WithRevisionLabel("serving.knative.dev/route", "delete-label-failure"),
+				WithRevisionAnn("serving.knative.dev/routes", "delete-label-failure"),
 				WithRoutingState(v1.RoutingStateActive),
 				WithRoutingStateModified(now.Time)),
 			rev("default", "old-config"),
 		},
 		WantPatches: []clientgotesting.PatchActionImpl{
-			patchRemoveLabel("default", "old-config", "serving.knative.dev/route"),
+			patchRemoveRouteAnn("default", "old-config"),
 		},
 		WantEvents: []string{
 			Eventf(corev1.EventTypeWarning, "InternalError",
-				`failed to remove route label to /, Kind= "old-config": inducing failure for patch configurations`),
+				`failed to remove route annotation to /, Kind= "old-config": inducing failure for patch configurations`),
 		},
 		Key: "default/delete-label-failure",
 	}, {
@@ -313,32 +332,37 @@ func TestV2Reconcile(t *testing.T) {
 		Objects: []runtime.Object{
 			simpleRunLatest("default", "delete-label-failure", "new-config", WithRouteFinalizer),
 			simpleConfig("default", "old-config",
-				WithConfigLabel("serving.knative.dev/route", "delete-label-failure")),
+				WithConfigAnn("serving.knative.dev/routes", "delete-label-failure")),
 			simpleConfig("default", "new-config",
-				WithConfigLabel("serving.knative.dev/route", "delete-label-failure")),
+				WithConfigAnn("serving.knative.dev/routes", "delete-label-failure")),
 			rev("default", "new-config",
-				WithRevisionLabel("serving.knative.dev/route", "delete-label-failure")),
+				WithRevisionAnn("serving.knative.dev/routes", "delete-label-failure")),
 			rev("default", "old-config",
-				WithRevisionLabel("serving.knative.dev/route", "delete-label-failure")),
+				WithRoutingState(v1.RoutingStateActive),
+				WithRevisionAnn("serving.knative.dev/routes", "delete-label-failure")),
 		},
 		WantPatches: []clientgotesting.PatchActionImpl{
-			patchRemoveRouteAndServingStateLabel(
-				"default", rev("default", "old-config").Name, now.Time),
+			patchRemoveRouteAnn("default", rev("default", "old-config").Name),
 		},
 		WantEvents: []string{
 			Eventf(corev1.EventTypeWarning, "InternalError",
-				`failed to remove route label to /, Kind= "old-config-dbnfd": inducing failure for patch revisions`),
+				`failed to remove route annotation to /, Kind= "old-config-dbnfd": inducing failure for patch revisions`),
 		},
 		Key: "default/delete-label-failure",
 	}}
 
 	table.Test(t, MakeFactory(func(ctx context.Context, listers *Listers, cmw configmap.Watcher) controller.Reconciler {
+		clock := clock.NewFakeClock(fakeTime)
+		client := servingclient.Get(ctx)
+		cLister := listers.GetConfigurationLister()
+		cIndexer := listers.IndexerFor(&v1.Configuration{})
+		rLister := listers.GetRevisionLister()
+		rIndexer := listers.IndexerFor(&v1.Revision{})
 		r := &Reconciler{
-			client:              servingclient.Get(ctx),
-			configurationLister: listers.GetConfigurationLister(),
-			revisionLister:      listers.GetRevisionLister(),
-			tracker:             &NullTracker{},
-			clock:               clock.NewFakeClock(fakeTime),
+			caccV1: labelerv1.NewConfigurationAccessor(client, &NullTracker{}, cLister),
+			caccV2: labelerv2.NewConfigurationAccessor(client, &NullTracker{}, cLister, cIndexer, clock),
+			raccV1: labelerv1.NewRevisionAccessor(client, &NullTracker{}, rLister),
+			raccV2: labelerv2.NewRevisionAccessor(client, &NullTracker{}, rLister, rIndexer, clock),
 		}
 
 		return routereconciler.NewReconciler(ctx, logging.FromContext(ctx), servingclient.Get(ctx),
@@ -415,73 +439,66 @@ func rev(namespace, name string, opts ...RevisionOption) *v1.Revision {
 	return rev
 }
 
-func patchRemoveLabel(namespace, name, key string) clientgotesting.PatchActionImpl {
-	action := clientgotesting.PatchActionImpl{}
-	action.Name = name
-	action.Namespace = namespace
-
-	patch := fmt.Sprintf(`{"metadata":{"labels":{%q:null}}}`, key)
-
-	action.Patch = []byte(patch)
-	return action
+func patchRemoveRouteAnn(namespace, name string) clientgotesting.PatchActionImpl {
+	return patchAddRouteAnn(namespace, name, "null")
 }
 
 func patchRemoveRouteAndServingStateLabel(namespace, name string, now time.Time) clientgotesting.PatchActionImpl {
 	return patchAddRouteAndServingStateLabel(namespace, name, "null", now)
 }
 
-func patchAddLabel(namespace, name, key, value string) clientgotesting.PatchActionImpl {
-	action := clientgotesting.PatchActionImpl{}
-	action.Name = name
-	action.Namespace = namespace
+func patchAddRouteAnn(namespace, name, value string) clientgotesting.PatchActionImpl {
+	action := clientgotesting.PatchActionImpl{
+		Name:       name,
+		ActionImpl: clientgotesting.ActionImpl{Namespace: namespace},
+	}
 
-	patch := fmt.Sprintf(`{"metadata":{"labels":{%q:%q}}}`, key, value)
+	// Note: the raw json `"key": null` removes a value, whereas an actual value
+	// called "null" would need quotes to parse as a string `"key":"null"`.
+	if value != "null" {
+		value = `"` + value + `"`
+	}
 
-	action.Patch = []byte(patch)
+	action.Patch = []byte(fmt.Sprintf(`{"metadata":{"annotations":{"serving.knative.dev/routes":%s}}}`, value))
 	return action
 }
 
 func patchAddRouteAndServingStateLabel(namespace, name, routeName string, now time.Time) clientgotesting.PatchActionImpl {
-	action := clientgotesting.PatchActionImpl{}
-	action.Name = name
-	action.Namespace = namespace
-
-	state := string(v1.RoutingStateReserve)
+	action := clientgotesting.PatchActionImpl{
+		Name:       name,
+		ActionImpl: clientgotesting.ActionImpl{Namespace: namespace},
+	}
 
 	// Note: the raw json `"key": null` removes a value, whereas an actual value
 	// called "null" would need quotes to parse as a string `"key":"null"`.
+	state := string(v1.RoutingStateReserve)
 	if routeName != "null" {
 		state = string(v1.RoutingStateActive)
 		routeName = `"` + routeName + `"`
 	}
 
-	patch := fmt.Sprintf(
-		`{"metadata":{"annotations":{"serving.knative.dev/routingStateModified":%q},`+
-			`"labels":{"serving.knative.dev/route":%s,`+
-			`"serving.knative.dev/routingState":%q}}}`, now.UTC().Format(time.RFC3339), routeName, state)
-
-	action.Patch = []byte(patch)
+	action.Patch = []byte(fmt.Sprintf(
+		`{"metadata":{"annotations":{"serving.knative.dev/routes":%s,`+
+			`"serving.knative.dev/routingStateModified":%q},`+
+			`"labels":{"serving.knative.dev/routingState":%q}}}`, routeName, now.UTC().Format(time.RFC3339), state))
 	return action
 }
 
 func patchAddFinalizerAction(namespace, name string) clientgotesting.PatchActionImpl {
-	resource := v1.Resource("routes")
-	finalizer := resource.String()
-
-	action := clientgotesting.PatchActionImpl{}
-	action.Name = name
-	action.Namespace = namespace
-	patch := fmt.Sprintf(`{"metadata":{"finalizers":[%q],"resourceVersion":""}}`, finalizer)
-	action.Patch = []byte(patch)
-	return action
+	p := fmt.Sprintf(`{"metadata":{"finalizers":[%q],"resourceVersion":""}}`, v1.Resource("routes").String())
+	return clientgotesting.PatchActionImpl{
+		Name:       name,
+		ActionImpl: clientgotesting.ActionImpl{Namespace: namespace},
+		Patch:      []byte(p),
+	}
 }
 
 func patchRemoveFinalizerAction(namespace, name string) clientgotesting.PatchActionImpl {
-	action := clientgotesting.PatchActionImpl{}
-	action.Name = name
-	action.Namespace = namespace
-	action.Patch = []byte(`{"metadata":{"finalizers":[],"resourceVersion":""}}`)
-	return action
+	return clientgotesting.PatchActionImpl{
+		Name:       name,
+		ActionImpl: clientgotesting.ActionImpl{Namespace: namespace},
+		Patch:      []byte(`{"metadata":{"finalizers":[],"resourceVersion":""}}`),
+	}
 }
 
 func TestNew(t *testing.T) {
diff --git a/pkg/testing/v1/configuration.go b/pkg/testing/v1/configuration.go
index 56c3bcc05..0c7d88b21 100644
--- a/pkg/testing/v1/configuration.go
+++ b/pkg/testing/v1/configuration.go
@@ -106,6 +106,13 @@ func WithConfigLabel(key, value string) ConfigOption {
 	}
 }
 
+// WithConfigAnn attaches a particular label to the configuration.
+func WithConfigAnn(key, value string) ConfigOption {
+	return func(config *v1.Configuration) {
+		config.Annotations = kmeta.UnionMaps(config.Annotations, map[string]string{key: value})
+	}
+}
+
 // WithConfigOwnersRemoved clears the owner references of this Configuration.
 func WithConfigOwnersRemoved(cfg *v1.Configuration) {
 	cfg.OwnerReferences = nil
diff --git a/pkg/testing/v1/revision.go b/pkg/testing/v1/revision.go
index df2c5156e..9ee74f7d4 100644
--- a/pkg/testing/v1/revision.go
+++ b/pkg/testing/v1/revision.go
@@ -210,6 +210,13 @@ func WithRevisionLabel(key, value string) RevisionOption {
 	}
 }
 
+// WithRevisionAnn attaches a particular label to the revision.
+func WithRevisionAnn(key, value string) RevisionOption {
+	return func(rev *v1.Revision) {
+		rev.Annotations = kmeta.UnionMaps(rev.Annotations, map[string]string{key: value})
+	}
+}
+
 // WithContainerStatuses sets the .Status.ContainerStatuses to the Revision.
 func WithContainerStatuses(containerStatus []v1.ContainerStatuses) RevisionOption {
 	return func(r *v1.Revision) {