Skip to content

Commit aa7fd90

Browse files
change implementation to use sks mode directly
1 parent 08b8289 commit aa7fd90

11 files changed

Lines changed: 169 additions & 131 deletions

File tree

config/core/300-resources/podautoscaler.yaml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -181,6 +181,9 @@ spec:
181181
description: DesiredScale shows the current desired number of replicas for the revision.
182182
type: integer
183183
format: int32
184+
metricsPaused:
185+
description: MetricsPaused to determine whether metric scraping should be paused
186+
type: boolean
184187
metricsServiceName:
185188
description: |-
186189
MetricsServiceName is the K8s Service name that provides revision metrics.

docs/serving-api.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -497,6 +497,17 @@ int32
497497
<p>ActualScale shows the actual number of replicas for the revision.</p>
498498
</td>
499499
</tr>
500+
<tr>
501+
<td>
502+
<code>metricsPaused</code><br/>
503+
<em>
504+
bool
505+
</em>
506+
</td>
507+
<td>
508+
<p>MetricsPaused to determine whether metric scraping should be paused</p>
509+
</td>
510+
</tr>
500511
</tbody>
501512
</table>
502513
<h3 id="autoscaling.internal.knative.dev/v1alpha1.PodScalable">PodScalable

pkg/apis/autoscaling/v1alpha1/pa_types.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,9 @@ type PodAutoscalerStatus struct {
127127

128128
// ActualScale shows the actual number of replicas for the revision.
129129
ActualScale *int32 `json:"actualScale,omitempty"`
130+
131+
// MetricsPaused to determine whether metric scraping should be paused
132+
MetricsPaused bool `json:"metricsPaused,omitempty"`
130133
}
131134

132135
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object

pkg/autoscaler/metrics/collector.go

Lines changed: 25 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,10 @@ type Collector interface {
7171
// Watch registers a singleton function to call when a specific collector's status changes.
7272
// The passed name is the namespace/name of the metric owned by the respective collector.
7373
Watch(func(types.NamespacedName))
74+
// Pause metric collection
75+
Pause(metric *autoscalingv1alpha1.Metric)
76+
// Resume metric collection
77+
Resume(metric *autoscalingv1alpha1.Metric)
7478
}
7579

7680
// MetricClient surfaces the metrics that can be obtained via the collector.
@@ -82,12 +86,6 @@ type MetricClient interface {
8286
// StableAndPanicRPS returns both the stable and the panic RPS
8387
// for the given replica as of the given time.
8488
StableAndPanicRPS(key types.NamespacedName, now time.Time) (float64, float64, error)
85-
86-
// Pause metric collection
87-
Pause(key types.NamespacedName)
88-
89-
// Resume metric collection
90-
Resume(key types.NamespacedName)
9189
}
9290

9391
// MetricCollector manages collection of metrics for many entities.
@@ -172,7 +170,9 @@ func (c *MetricCollector) Record(key types.NamespacedName, now time.Time, stat S
172170
}
173171
}
174172

175-
func (c *MetricCollector) Pause(key types.NamespacedName) {
173+
func (c *MetricCollector) Pause(metric *autoscalingv1alpha1.Metric) {
174+
key := types.NamespacedName{Namespace: metric.Namespace, Name: metric.Name}
175+
176176
c.collectionsMutex.RLock()
177177
defer c.collectionsMutex.RUnlock()
178178

@@ -181,7 +181,9 @@ func (c *MetricCollector) Pause(key types.NamespacedName) {
181181
}
182182
}
183183

184-
func (c *MetricCollector) Resume(key types.NamespacedName) {
184+
func (c *MetricCollector) Resume(metric *autoscalingv1alpha1.Metric) {
185+
key := types.NamespacedName{Namespace: metric.Namespace, Name: metric.Name}
186+
185187
c.collectionsMutex.RLock()
186188
defer c.collectionsMutex.RUnlock()
187189

@@ -272,11 +274,12 @@ type (
272274
rpsPanicBuckets windowAverager
273275

274276
// Fields relevant for metric scraping specifically.
275-
scraper StatsScraper
276-
lastErr error
277-
grp sync.WaitGroup
278-
paused bool
279-
stopCh chan struct{}
277+
creationTime time.Time
278+
scraper StatsScraper
279+
lastErr error
280+
grp sync.WaitGroup
281+
paused bool
282+
stopCh chan struct{}
280283
}
281284
)
282285

@@ -309,6 +312,7 @@ func newCollection(metric *autoscalingv1alpha1.Metric, scraper StatsScraper, clo
309312
}
310313
}
311314

315+
creationTime := time.Now()
312316
c := &collection{
313317
metric: metric,
314318
concurrencyBuckets: bucketCtor(
@@ -321,8 +325,9 @@ func newCollection(metric *autoscalingv1alpha1.Metric, scraper StatsScraper, clo
321325
metric.Spec.PanicWindow, config.BucketSize),
322326
scraper: scraper,
323327

324-
stopCh: make(chan struct{}),
325-
paused: false,
328+
stopCh: make(chan struct{}),
329+
creationTime: creationTime,
330+
paused: false,
326331
}
327332

328333
key := types.NamespacedName{Namespace: metric.Namespace, Name: metric.Name}
@@ -339,7 +344,11 @@ func newCollection(metric *autoscalingv1alpha1.Metric, scraper StatsScraper, clo
339344
case <-c.stopCh:
340345
return
341346
case <-scrapeTicker.C():
342-
if c.getPaused() {
347+
now := time.Now()
348+
timeSinceCreation := now.Sub(c.creationTime)
349+
stableWindow := 2 * c.metric.Spec.StableWindow
350+
// initially wait two stable windows to allow initial scale to zero due to activator behavior
351+
if timeSinceCreation > 2*stableWindow && c.getPaused() {
343352
continue
344353
}
345354
scraper := c.getScraper()

pkg/autoscaler/scaling/autoscaler.go

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -297,11 +297,6 @@ func (a *autoscaler) Scale(logger *zap.SugaredLogger, now time.Time) ScaleResult
297297
observedPanicValue, spec.TargetBurstCapacity, excessBCF))
298298
}
299299

300-
// Resume pod scraping if excess burst capacity >= 0
301-
if excessBCF >= 0 {
302-
a.metricClient.Resume(metricKey)
303-
}
304-
305300
a.metrics.Record(
306301
excessBCF,
307302
int64(desiredPodCount),
@@ -310,11 +305,6 @@ func (a *autoscaler) Scale(logger *zap.SugaredLogger, now time.Time) ScaleResult
310305
spec.TargetValue,
311306
)
312307

313-
// pause after recording concurrency if excess burst capacity is < 0
314-
if excessBCF < 0 {
315-
a.metricClient.Pause(metricKey)
316-
}
317-
318308
return ScaleResult{
319309
DesiredPodCount: desiredPodCount,
320310
ExcessBurstCapacity: int32(excessBCF),

pkg/autoscaler/scaling/autoscaler_test.go

Lines changed: 0 additions & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -871,51 +871,6 @@ func TestNewFail(t *testing.T) {
871871
}
872872
}
873873

874-
func TestPausingCollection(t *testing.T) {
875-
reader := metric.NewManualReader()
876-
key := types.NamespacedName{Namespace: testNamespace, Name: testRevision}
877-
mp := metric.NewMeterProvider(metric.WithReader(reader))
878-
attrs := attribute.NewSet(attribute.String("foo", "bar"))
879-
testColl := &testCollection{
880-
paused: false,
881-
}
882-
metrics := &metricClient{
883-
StableConcurrency: 9.0,
884-
PanicConcurrency: 9.0,
885-
StableRPS: 9.0,
886-
PanicRPS: 9.0,
887-
collections: map[types.NamespacedName]*testCollection{
888-
key: testColl,
889-
},
890-
}
891-
deciderSpec := &DeciderSpec{
892-
TargetValue: 3,
893-
TotalValue: 4,
894-
TargetBurstCapacity: 1,
895-
PanicThreshold: 2,
896-
MaxScaleUpRate: 10,
897-
MaxScaleDownRate: 10,
898-
StableWindow: stableWindow,
899-
}
900-
901-
pc := fakePodCounter{
902-
readyCount: 1,
903-
}
904-
a := newAutoscaler(attrs, mp, testNamespace, testRevision, metrics, pc, deciderSpec, nil)
905-
now := time.Now()
906-
_ = a.Scale(logtesting.TestLogger(t), now)
907-
if metrics.collections[key].getPaused() != true {
908-
t.Errorf("metric collection should be paused but is not")
909-
}
910-
metrics.SetStableAndPanicConcurrency(3, 3)
911-
a.Update(deciderSpec)
912-
now = time.Now()
913-
_ = a.Scale(logtesting.TestLogger(t), now)
914-
if metrics.collections[key].getPaused() != false {
915-
t.Errorf("metric collection should be resumed but was paused")
916-
}
917-
}
918-
919874
// staticMetricClient returns stable/panic concurrency and RPS with static value, i.e. 10.
920875
var staticMetricClient = metricClient{
921876
StableConcurrency: 10.0,
@@ -924,29 +879,15 @@ var staticMetricClient = metricClient{
924879
PanicRPS: 10.0,
925880
}
926881

927-
// test collection type
928-
type testCollection struct {
929-
paused bool
930-
}
931-
932882
// metricClient is a fake implementation of autoscaler.metricClient for testing.
933883
type metricClient struct {
934884
StableConcurrency float64
935885
PanicConcurrency float64
936886
StableRPS float64
937887
PanicRPS float64
938-
collections map[types.NamespacedName]*testCollection
939888
ErrF func(key types.NamespacedName, now time.Time) error
940889
}
941890

942-
func (c *testCollection) setPause(paused bool) {
943-
c.paused = paused
944-
}
945-
946-
func (c *testCollection) getPaused() bool {
947-
return c.paused
948-
}
949-
950891
// SetStableAndPanicConcurrency sets the stable and panic concurrencies.
951892
func (mc *metricClient) SetStableAndPanicConcurrency(s, p float64) {
952893
mc.StableConcurrency, mc.PanicConcurrency = s, p
@@ -972,20 +913,6 @@ func (mc *metricClient) StableAndPanicRPS(key types.NamespacedName, now time.Tim
972913
return mc.StableRPS, mc.PanicRPS, err
973914
}
974915

975-
// Pauses metric collection
976-
func (mc *metricClient) Pause(key types.NamespacedName) {
977-
if collection, exists := mc.collections[key]; exists {
978-
collection.setPause(true)
979-
}
980-
}
981-
982-
// Resumes metric collection
983-
func (mc *metricClient) Resume(key types.NamespacedName) {
984-
if collection, exists := mc.collections[key]; exists {
985-
collection.setPause(false)
986-
}
987-
}
988-
989916
func BenchmarkAutoscaler(b *testing.B) {
990917
metrics := &metricClient{StableConcurrency: 50.0, PanicConcurrency: 10}
991918
a := newTestAutoscalerNoPC(10, 101, metrics)

pkg/reconciler/autoscaling/kpa/kpa.go

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,13 +104,16 @@ func (c *Reconciler) ReconcileKind(ctx context.Context, pa *autoscalingv1alpha1.
104104
return fmt.Errorf("error reconciling SKS: %w", err)
105105
}
106106
pa.Status.MarkSKSNotReady(noPrivateServiceName) // In both cases this is true.
107+
pa.Status.MetricsPaused = false // unpause metrics
107108
c.computeStatus(ctx, pa, podCounts{want: scaleUnknown}, logger)
108109
return nil
109110
}
110111

111112
pa.Status.MetricsServiceName = sks.Status.PrivateServiceName
112113
decider, err := c.reconcileDecider(ctx, pa)
113114
if err != nil {
115+
// unpause incase of this error
116+
pa.Status.MetricsPaused = false
114117
return fmt.Errorf("error reconciling Decider: %w", err)
115118
}
116119

@@ -171,6 +174,27 @@ func (c *Reconciler) ReconcileKind(ctx context.Context, pa *autoscalingv1alpha1.
171174
pa.Status.MarkSKSNotReady(sks.Status.GetCondition(nv1alpha1.ServerlessServiceConditionReady).GetMessage())
172175
}
173176

177+
shouldPause := false
178+
isSKSReady := pa.Status.GetCondition(autoscalingv1alpha1.PodAutoscalerConditionSKSReady).IsTrue()
179+
// only try probe if sks should be in proxy mode and SKS is ready, otherwise always unpause
180+
if sks.Spec.Mode == nv1alpha1.SKSOperationModeProxy && isSKSReady {
181+
r, err := c.scaler.activatorProbe(pa, c.scaler.transport)
182+
if err != nil {
183+
shouldPause = false
184+
} else {
185+
if r {
186+
logger.Debug("activator probed, pausing metrics")
187+
}
188+
shouldPause = r
189+
}
190+
} else {
191+
shouldPause = false
192+
}
193+
194+
if shouldPause != pa.Status.MetricsPaused {
195+
pa.Status.MetricsPaused = shouldPause
196+
}
197+
174198
logger.Infof("PA scale got=%d, want=%d, desiredPods=%d ebc=%d", ready, want,
175199
decider.Status.DesiredScale, decider.Status.ExcessBurstCapacity)
176200

0 commit comments

Comments
 (0)