diff --git a/.github/ISSUE_TEMPLATE/2_bug_report.yml b/.github/ISSUE_TEMPLATE/2_bug_report.yml index d8b690f7..206d0043 100644 --- a/.github/ISSUE_TEMPLATE/2_bug_report.yml +++ b/.github/ISSUE_TEMPLATE/2_bug_report.yml @@ -64,11 +64,10 @@ body: label: Kubernetes Version description: What version of Kubernetes that are you running? options: + - "1.28" + - "1.27" - "1.26" - - "1.25" - - "1.24" - - "1.23" - - "< 1.23" + - "< 1.26" - "Other" validations: required: false diff --git a/.github/workflows/e2e-tests.yaml b/.github/workflows/e2e-tests.yaml index 3aabecb4..c07af026 100644 --- a/.github/workflows/e2e-tests.yaml +++ b/.github/workflows/e2e-tests.yaml @@ -16,7 +16,7 @@ jobs: strategy: fail-fast: false matrix: - kubernetesVersion: [v1.28, v1.27, v1.26, v1.25] + kubernetesVersion: [v1.28, v1.27, v1.26] include: - kubernetesVersion: v1.28 kindImage: kindest/node:v1.28.0@sha256:b7a4cad12c197af3ba43202d3efe03246b3f0793f162afb40a33c923952d5b31 @@ -24,8 +24,6 @@ jobs: kindImage: kindest/node:v1.27.3@sha256:3966ac761ae0136263ffdb6cfd4db23ef8a83cba8a463690e98317add2c9ba72 - kubernetesVersion: v1.26 kindImage: kindest/node:v1.26.6@sha256:6e2d8b28a5b601defe327b98bd1c2d1930b49e5d8c512e1895099e4504007adb - - kubernetesVersion: v1.25 - kindImage: kindest/node:v1.25.11@sha256:227fa11ce74ea76a0474eeefb84cb75d8dad1b08638371ecf0e86259b35be0c8 steps: - name: Install prerequisites run: | @@ -91,7 +89,7 @@ jobs: strategy: fail-fast: false matrix: - kubernetesVersion: [v1.28, v1.27, v1.26, v1.25] + kubernetesVersion: [v1.28, v1.27, v1.26] include: - kubernetesVersion: v1.28 kindImage: kindest/node:v1.28.0@sha256:b7a4cad12c197af3ba43202d3efe03246b3f0793f162afb40a33c923952d5b31 @@ -99,8 +97,6 @@ jobs: kindImage: kindest/node:v1.27.3@sha256:3966ac761ae0136263ffdb6cfd4db23ef8a83cba8a463690e98317add2c9ba72 - kubernetesVersion: v1.26 kindImage: kindest/node:v1.26.6@sha256:6e2d8b28a5b601defe327b98bd1c2d1930b49e5d8c512e1895099e4504007adb - - kubernetesVersion: v1.25 - kindImage: kindest/node:v1.25.11@sha256:227fa11ce74ea76a0474eeefb84cb75d8dad1b08638371ecf0e86259b35be0c8 steps: - uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 # v4 diff --git a/CHANGELOG.md b/CHANGELOG.md index a7152534..0cbd9e2b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -24,8 +24,7 @@ This changelog keeps track of work items that have been completed and are ready ### Improvements -- **General**: TODO ([#TODO](https://github.com/kedacore/http-add-on/issues/TODO)) -- **Scaler**: Decrease memory usage by increasing stream interval from 5ms to 200ms. +- **Scaler**: Decrease memory usage by allowing increasing stream interval configuration ([#745](https://github.com/kedacore/http-add-on/pull/745)) ### Fixes diff --git a/config/interceptor/deployment.yaml b/config/interceptor/deployment.yaml index 447233a9..3d62e983 100644 --- a/config/interceptor/deployment.yaml +++ b/config/interceptor/deployment.yaml @@ -24,8 +24,6 @@ spec: - name: interceptor image: ghcr.io/kedacore/http-add-on-interceptor env: - - name: KEDA_HTTP_ROUTING_TABLE_UPDATE_DURATION_MS - value: "500" - name: KEDA_HTTP_CURRENT_NAMESPACE value: "keda" - name: KEDA_HTTP_PROXY_PORT diff --git a/config/scaler/deployment.yaml b/config/scaler/deployment.yaml index e0ea4f5b..652b4c66 100644 --- a/config/scaler/deployment.yaml +++ b/config/scaler/deployment.yaml @@ -33,7 +33,7 @@ spec: value: "keda-http-add-on-interceptor-admin" - name: KEDA_HTTP_SCALER_TARGET_ADMIN_PORT value: "9090" - - name: KEDA_HTTP_SCALER_TARGET_PENDING_REQUESTS_INTERCEPTOR + - name: KEDA_HTTP_SCALER_STREAM_INTERVAL_MS value: "200" ports: - name: grpc diff --git a/docs/install.md b/docs/install.md index 6ef039db..3562e9a3 100644 --- a/docs/install.md +++ b/docs/install.md @@ -75,7 +75,7 @@ $ helm upgrade kedahttp ./charts/keda-add-ons-http \ | HTTP Add-On version | KEDA version | Kubernetes version | |---------------------|--------------|--------------------| -| 0.6.0 | v2.12 | v1.25 - v1.28 | +| 0.6.0 | v2.12 | v1.26 - v1.28 | | 0.5.1 | v2.10 | v1.24 - v1.26 | | 0.5.0 | v2.9 | v1.23 - v1.25 | diff --git a/pkg/util/env_resolver.go b/pkg/util/env_resolver.go new file mode 100644 index 00000000..b01cc567 --- /dev/null +++ b/pkg/util/env_resolver.go @@ -0,0 +1,54 @@ +/* +Copyright 2023 The KEDA 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 util + +import ( + "os" + "strconv" + "time" +) + +func ResolveOsEnvBool(envName string, defaultValue bool) (bool, error) { + valueStr, found := os.LookupEnv(envName) + + if found && valueStr != "" { + return strconv.ParseBool(valueStr) + } + + return defaultValue, nil +} + +func ResolveOsEnvInt(envName string, defaultValue int) (int, error) { + valueStr, found := os.LookupEnv(envName) + + if found && valueStr != "" { + return strconv.Atoi(valueStr) + } + + return defaultValue, nil +} + +func ResolveOsEnvDuration(envName string) (*time.Duration, error) { + valueStr, found := os.LookupEnv(envName) + + if found && valueStr != "" { + value, err := time.ParseDuration(valueStr) + return &value, err + } + + return nil, nil +} diff --git a/pkg/util/env_resolver_test.go b/pkg/util/env_resolver_test.go new file mode 100644 index 00000000..cb673a5c --- /dev/null +++ b/pkg/util/env_resolver_test.go @@ -0,0 +1,108 @@ +package util + +import ( + "testing" + "time" + + "github.com/stretchr/testify/assert" +) + +func TestResolveMissingOsEnvBool(t *testing.T) { + actual, err := ResolveOsEnvBool("missing_bool", true) + assert.True(t, actual) + assert.Nil(t, err) + + t.Setenv("empty_bool", "") + actual, err = ResolveOsEnvBool("empty_bool", true) + assert.True(t, actual) + assert.Nil(t, err) +} + +func TestResolveInvalidOsEnvBool(t *testing.T) { + t.Setenv("blank_bool", " ") + actual, err := ResolveOsEnvBool("blank_bool", true) + assert.False(t, actual) + assert.NotNil(t, err) + + t.Setenv("invalid_bool", "deux heures") + actual, err = ResolveOsEnvBool("invalid_bool", true) + assert.False(t, actual) + assert.NotNil(t, err) +} + +func TestResolveValidOsEnvBool(t *testing.T) { + t.Setenv("valid_bool", "true") + actual, err := ResolveOsEnvBool("valid_bool", false) + assert.True(t, actual) + assert.Nil(t, err) + + t.Setenv("valid_bool", "false") + actual, err = ResolveOsEnvBool("valid_bool", true) + assert.False(t, actual) + assert.Nil(t, err) +} + +func TestResolveMissingOsEnvInt(t *testing.T) { + actual, err := ResolveOsEnvInt("missing_int", 1) + assert.Equal(t, 1, actual) + assert.Nil(t, err) + + t.Setenv("empty_int", "") + actual, err = ResolveOsEnvInt("empty_int", 1) + assert.Equal(t, 1, actual) + assert.Nil(t, err) +} + +func TestResolveInvalidOsEnvInt(t *testing.T) { + t.Setenv("blank_int", " ") + actual, err := ResolveOsEnvInt("blank_int", 1) + assert.Equal(t, 0, actual) + assert.NotNil(t, err) + + t.Setenv("invalid_int", "deux heures") + actual, err = ResolveOsEnvInt("invalid_int", 1) + assert.Equal(t, 0, actual) + assert.NotNil(t, err) +} + +func TestResolveValidOsEnvInt(t *testing.T) { + t.Setenv("valid_int", "2") + actual, err := ResolveOsEnvInt("valid_int", 1) + assert.Equal(t, 2, actual) + assert.Nil(t, err) +} + +func TestResolveMissingOsEnvDuration(t *testing.T) { + actual, err := ResolveOsEnvDuration("missing_duration") + assert.Nil(t, actual) + assert.Nil(t, err) + + t.Setenv("empty_duration", "") + actual, err = ResolveOsEnvDuration("empty_duration") + assert.Nil(t, actual) + assert.Nil(t, err) +} + +func TestResolveInvalidOsEnvDuration(t *testing.T) { + t.Setenv("blank_duration", " ") + actual, err := ResolveOsEnvDuration("blank_duration") + assert.Equal(t, time.Duration(0), *actual) + assert.NotNil(t, err) + + t.Setenv("invalid_duration", "deux heures") + actual, err = ResolveOsEnvDuration("invalid_duration") + assert.Equal(t, time.Duration(0), *actual) + assert.NotNil(t, err) +} + +func TestResolveValidOsEnvDuration(t *testing.T) { + t.Setenv("valid_duration_seconds", "8s") + actual, err := ResolveOsEnvDuration("valid_duration_seconds") + assert.Equal(t, time.Duration(8)*time.Second, *actual) + assert.Nil(t, err) + + t.Setenv("valid_duration_minutes", "30m") + actual, err = ResolveOsEnvDuration("valid_duration_minutes") + assert.Equal(t, time.Duration(30)*time.Minute, *actual) + assert.Nil(t, err) +} diff --git a/scaler/handlers.go b/scaler/handlers.go index 41ae58f4..a572f069 100644 --- a/scaler/handlers.go +++ b/scaler/handlers.go @@ -17,12 +17,24 @@ import ( informershttpv1alpha1 "github.com/kedacore/http-add-on/operator/generated/informers/externalversions/http/v1alpha1" "github.com/kedacore/http-add-on/pkg/k8s" + "github.com/kedacore/http-add-on/pkg/util" ) const ( keyInterceptorTargetPendingRequests = "interceptorTargetPendingRequests" ) +var streamInterval time.Duration + +func init() { + defaultMS := 200 + timeoutMS, err := util.ResolveOsEnvInt("KEDA_HTTP_SCALER_STREAM_INTERVAL_MS", defaultMS) + if err != nil { + timeoutMS = defaultMS + } + streamInterval = time.Duration(timeoutMS) * time.Millisecond +} + type impl struct { lggr logr.Logger pinger *queuePinger @@ -82,9 +94,9 @@ func (e *impl) StreamIsActive( server externalscaler.ExternalScaler_StreamIsActiveServer, ) error { // this function communicates with KEDA via the 'server' parameter. - // we call server.Send (below) every 200ms, which tells it to immediately + // we call server.Send (below) every streamInterval, which tells it to immediately // ping our IsActive RPC - ticker := time.NewTicker(200 * time.Millisecond) + ticker := time.NewTicker(streamInterval) defer ticker.Stop() for { select {