From be87ee59754d3724d41a1519fa45b68cad2f319b Mon Sep 17 00:00:00 2001 From: ajanikow <12255597+ajanikow@users.noreply.github.com> Date: Tue, 14 Oct 2025 16:12:58 +0200 Subject: [PATCH 1/8] [Bugfix] [Platform] Increase memory limit for Inventory --- CHANGELOG.md | 1 + .../resources/config_map_gateway.go | 45 +++++++++---------- .../gateway/gateway_config_destination.go | 28 ++++++++++++ .../gateway_config_destination_static.go | 13 ++++++ .../gateway_config_destination_type.go | 5 ++- pkg/util/constants/gateway.go | 1 + 6 files changed, 67 insertions(+), 26 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 36d920248..ff0a38982 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,7 @@ # Change Log ## [master](https://github.com/arangodb/kube-arangodb/tree/master) (N/A) +- (Bugfix) (Platform) Increase memory limit for Inventory ## [1.3.1](https://github.com/arangodb/kube-arangodb/tree/1.3.1) (2025-10-07) - (Documentation) Add ArangoPlatformStorage Docs & Examples diff --git a/pkg/deployment/resources/config_map_gateway.go b/pkg/deployment/resources/config_map_gateway.go index 4f12e01c7..75b6bbcd5 100644 --- a/pkg/deployment/resources/config_map_gateway.go +++ b/pkg/deployment/resources/config_map_gateway.go @@ -26,7 +26,6 @@ import ( "path" "path/filepath" - "google.golang.org/protobuf/encoding/protojson" core "k8s.io/api/core/v1" kerrors "k8s.io/apimachinery/pkg/api/errors" meta "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -108,18 +107,8 @@ func (r *Resources) ensureGatewayConfig(ctx context.Context, cachedStatus inspec }, } - _, baseGatewayCfgYamlChecksum, _, err := cfg.RenderYAML() - if err != nil { - return errors.WithStack(errors.Wrapf(err, "Failed to render gateway config")) - } - - inventory.Arangodb = pbInventoryV1.NewArangoDBConfiguration(r.context.GetSpec(), r.context.GetStatus()) - inventory.Configuration = &pbInventoryV1.InventoryConfiguration{ - Hash: baseGatewayCfgYamlChecksum, - } - cfg.Destinations[utilConstants.EnvoyInventoryConfigDestination] = gateway.ConfigDestination{ - Type: util.NewType(gateway.ConfigDestinationTypeStatic), + Type: util.NewType(gateway.ConfigDestinationTypeFile), Match: util.NewType(gateway.ConfigMatchPath), AuthExtension: &gateway.ConfigAuthZExtension{ AuthZExtension: map[string]string{ @@ -127,18 +116,13 @@ func (r *Resources) ensureGatewayConfig(ctx context.Context, cachedStatus inspec pbImplEnvoyAuthV3Shared.AuthConfigAuthPassModeKey: string(networkingApi.ArangoRouteSpecAuthenticationPassModeRemove), }, }, - Static: &gateway.ConfigDestinationStatic[*pbInventoryV1.Inventory]{ - Code: util.NewType[uint32](200), - Response: inventory, - Marshaller: ugrpc.Marshal[*pbInventoryV1.Inventory], - Options: []util.Mod[protojson.MarshalOptions]{ - ugrpc.WithUseProtoNames(true), - ugrpc.WithEmitDefaultValues(true), - }, + File: gateway.ConfigDestinationFile{ + File: path.Join(utilConstants.GatewayVolumeMountDir, utilConstants.InventoryFileName), + Code: 200, }, } - gatewayCfgYaml, _, _, err := cfg.RenderYAML() + gatewayCfgYaml, gatewayCfgYamlChecksum, _, err := cfg.RenderYAML() if err != nil { return errors.WithStack(errors.Wrapf(err, "Failed to render gateway config")) } @@ -153,23 +137,36 @@ func (r *Resources) ensureGatewayConfig(ctx context.Context, cachedStatus inspec return errors.WithStack(errors.Wrapf(err, "Failed to render gateway lds config")) } + inventory.Arangodb = pbInventoryV1.NewArangoDBConfiguration(r.context.GetSpec(), r.context.GetStatus()) + inventory.Configuration = &pbInventoryV1.InventoryConfiguration{ + Hash: gatewayCfgYamlChecksum, + } + + inventoryData, err := ugrpc.Marshal(inventory, ugrpc.WithUseProtoNames(true), ugrpc.WithEmitDefaultValues(true)) + if err != nil { + return errors.WithStack(errors.Wrapf(err, "Failed to render gateway inventory")) + } + + gatewayChecksum := util.SHA256FromStringArray(gatewayCfgYamlChecksum, util.SHA256(inventoryData)) + if err := r.ensureGatewayConfigMap(ctx, cachedStatus, configMaps, GetGatewayConfigMapName(r.context.GetAPIObject().GetName()), map[string]string{ utilConstants.GatewayConfigFileName: string(gatewayCfgYaml), - utilConstants.GatewayConfigChecksum: baseGatewayCfgYamlChecksum, + utilConstants.GatewayConfigChecksum: gatewayChecksum, + utilConstants.InventoryFileName: string(inventoryData), }); err != nil { return err } if err := r.ensureGatewayConfigMap(ctx, cachedStatus, configMaps, GetGatewayConfigMapName(r.context.GetAPIObject().GetName(), "cds"), map[string]string{ utilConstants.GatewayConfigFileName: string(gatewayCfgCDSYaml), - utilConstants.GatewayConfigChecksum: baseGatewayCfgYamlChecksum, + utilConstants.GatewayConfigChecksum: gatewayChecksum, }); err != nil { return err } if err := r.ensureGatewayConfigMap(ctx, cachedStatus, configMaps, GetGatewayConfigMapName(r.context.GetAPIObject().GetName(), "lds"), map[string]string{ utilConstants.GatewayConfigFileName: string(gatewayCfgLDSYaml), - utilConstants.GatewayConfigChecksum: baseGatewayCfgYamlChecksum, + utilConstants.GatewayConfigChecksum: gatewayChecksum, }); err != nil { return err } diff --git a/pkg/deployment/resources/gateway/gateway_config_destination.go b/pkg/deployment/resources/gateway/gateway_config_destination.go index d552b9d9d..ffc39da61 100644 --- a/pkg/deployment/resources/gateway/gateway_config_destination.go +++ b/pkg/deployment/resources/gateway/gateway_config_destination.go @@ -83,6 +83,8 @@ type ConfigDestination struct { ResponseHeaders map[string]string `json:"responseHeaders,omitempty"` Static ConfigDestinationStaticInterface `json:"static,omitempty"` + + File ConfigDestinationFileInterface `json:"file,omitempty"` } func (c *ConfigDestination) Validate() error { @@ -91,6 +93,13 @@ func (c *ConfigDestination) Validate() error { } switch c.Type.Get() { + case ConfigDestinationTypeFile: + return shared.WithErrors( + shared.PrefixResourceError("type", c.Type.Validate()), + shared.PrefixResourceError("path", shared.ValidateAPIPath(c.GetPath())), + shared.PrefixResourceError("pathType", shared.ValidateOptionalInterface(c.Match)), + shared.PrefixResourceError("authExtension", c.AuthExtension.Validate()), + ) case ConfigDestinationTypeStatic: return shared.WithErrors( shared.PrefixResourceError("type", c.Type.Validate()), @@ -202,6 +211,25 @@ func (c *ConfigDestination) appendRouteAction(route *pbEnvoyRouteV3.Route, name } return nil } + if c.Type.Get() == ConfigDestinationTypeFile { + if c.File == nil { + return errors.Errorf("File response is not defined!") + } + path, code := c.File.StaticResponse() + + // Return static response + route.Action = &pbEnvoyRouteV3.Route_DirectResponse{ + DirectResponse: &pbEnvoyRouteV3.DirectResponseAction{ + Status: code, + Body: &pbEnvoyCoreV3.DataSource{ + Specifier: &pbEnvoyCoreV3.DataSource_Filename{ + Filename: path, + }, + }, + }, + } + return nil + } route.Action = &pbEnvoyRouteV3.Route_Route{ Route: &pbEnvoyRouteV3.RouteAction{ diff --git a/pkg/deployment/resources/gateway/gateway_config_destination_static.go b/pkg/deployment/resources/gateway/gateway_config_destination_static.go index d0d432c82..c518cdbec 100644 --- a/pkg/deployment/resources/gateway/gateway_config_destination_static.go +++ b/pkg/deployment/resources/gateway/gateway_config_destination_static.go @@ -29,6 +29,19 @@ import ( "github.com/arangodb/kube-arangodb/pkg/util" ) +type ConfigDestinationFileInterface interface { + StaticResponse() (string, uint32) +} + +type ConfigDestinationFile struct { + File string + Code uint32 +} + +func (c ConfigDestinationFile) StaticResponse() (string, uint32) { + return c.File, c.Code +} + type ConfigDestinationStaticInterface interface { Validate() error StaticResponse() ([]byte, uint32, error) diff --git a/pkg/deployment/resources/gateway/gateway_config_destination_type.go b/pkg/deployment/resources/gateway/gateway_config_destination_type.go index 0a266468d..82b64f3a2 100644 --- a/pkg/deployment/resources/gateway/gateway_config_destination_type.go +++ b/pkg/deployment/resources/gateway/gateway_config_destination_type.go @@ -35,6 +35,7 @@ const ( ConfigDestinationTypeHTTP ConfigDestinationType = iota ConfigDestinationTypeHTTPS ConfigDestinationTypeStatic + ConfigDestinationTypeFile ) func (c *ConfigDestinationType) Get() ConfigDestinationType { @@ -43,7 +44,7 @@ func (c *ConfigDestinationType) Get() ConfigDestinationType { } switch v := *c; v { - case ConfigDestinationTypeHTTP, ConfigDestinationTypeHTTPS, ConfigDestinationTypeStatic: + case ConfigDestinationTypeHTTP, ConfigDestinationTypeHTTPS, ConfigDestinationTypeStatic, ConfigDestinationTypeFile: return v default: return ConfigDestinationTypeHTTP @@ -79,7 +80,7 @@ func (c *ConfigDestinationType) RenderUpstreamTransportSocket(protocol *ConfigDe func (c *ConfigDestinationType) Validate() error { switch c.Get() { - case ConfigDestinationTypeHTTP, ConfigDestinationTypeHTTPS, ConfigDestinationTypeStatic: + case ConfigDestinationTypeHTTP, ConfigDestinationTypeHTTPS, ConfigDestinationTypeStatic, ConfigDestinationTypeFile: return nil default: return errors.Errorf("Invalid destination type") diff --git a/pkg/util/constants/gateway.go b/pkg/util/constants/gateway.go index 73b235642..a8601cffd 100644 --- a/pkg/util/constants/gateway.go +++ b/pkg/util/constants/gateway.go @@ -35,6 +35,7 @@ const ( GatewayConfigChecksum = "gateway.checksum" GatewayConfigFileName = "gateway.yaml" + InventoryFileName = "inventory.json" GatewayConfigChecksumENV = "GATEWAY_CONFIG_CHECKSUM" GatewayVolumeMountDir = "/etc/gateway/core/" From 2cfbee8d63eed67c7bbdf6926d554189bb9c23b0 Mon Sep 17 00:00:00 2001 From: ajanikow <12255597+ajanikow@users.noreply.github.com> Date: Wed, 15 Oct 2025 10:38:33 +0200 Subject: [PATCH 2/8] Iter --- pkg/deployment/resources/config_map_gateway.go | 9 +++++---- pkg/util/constants/gateway.go | 1 + 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/pkg/deployment/resources/config_map_gateway.go b/pkg/deployment/resources/config_map_gateway.go index 75b6bbcd5..342d9fec5 100644 --- a/pkg/deployment/resources/config_map_gateway.go +++ b/pkg/deployment/resources/config_map_gateway.go @@ -147,26 +147,27 @@ func (r *Resources) ensureGatewayConfig(ctx context.Context, cachedStatus inspec return errors.WithStack(errors.Wrapf(err, "Failed to render gateway inventory")) } - gatewayChecksum := util.SHA256FromStringArray(gatewayCfgYamlChecksum, util.SHA256(inventoryData)) + inventoryChecksum := util.SHA256FromStringArray(gatewayCfgYamlChecksum, util.SHA256(inventoryData)) if err := r.ensureGatewayConfigMap(ctx, cachedStatus, configMaps, GetGatewayConfigMapName(r.context.GetAPIObject().GetName()), map[string]string{ utilConstants.GatewayConfigFileName: string(gatewayCfgYaml), - utilConstants.GatewayConfigChecksum: gatewayChecksum, + utilConstants.GatewayConfigChecksum: gatewayCfgYamlChecksum, utilConstants.InventoryFileName: string(inventoryData), + utilConstants.InventoryChecksum: inventoryChecksum, }); err != nil { return err } if err := r.ensureGatewayConfigMap(ctx, cachedStatus, configMaps, GetGatewayConfigMapName(r.context.GetAPIObject().GetName(), "cds"), map[string]string{ utilConstants.GatewayConfigFileName: string(gatewayCfgCDSYaml), - utilConstants.GatewayConfigChecksum: gatewayChecksum, + utilConstants.GatewayConfigChecksum: gatewayCfgYamlChecksum, }); err != nil { return err } if err := r.ensureGatewayConfigMap(ctx, cachedStatus, configMaps, GetGatewayConfigMapName(r.context.GetAPIObject().GetName(), "lds"), map[string]string{ utilConstants.GatewayConfigFileName: string(gatewayCfgLDSYaml), - utilConstants.GatewayConfigChecksum: gatewayChecksum, + utilConstants.GatewayConfigChecksum: gatewayCfgYamlChecksum, }); err != nil { return err } diff --git a/pkg/util/constants/gateway.go b/pkg/util/constants/gateway.go index a8601cffd..0ac8cd88c 100644 --- a/pkg/util/constants/gateway.go +++ b/pkg/util/constants/gateway.go @@ -35,6 +35,7 @@ const ( GatewayConfigChecksum = "gateway.checksum" GatewayConfigFileName = "gateway.yaml" + InventoryChecksum = "inventory.checksum" InventoryFileName = "inventory.json" GatewayConfigChecksumENV = "GATEWAY_CONFIG_CHECKSUM" From 86d5322846f576f9c7afb491794571f78a21851e Mon Sep 17 00:00:00 2001 From: ajanikow <12255597+ajanikow@users.noreply.github.com> Date: Wed, 15 Oct 2025 11:06:45 +0200 Subject: [PATCH 3/8] Iter --- cmd/cmd.go | 3 +++ pkg/operator/operator.go | 3 ++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/cmd/cmd.go b/cmd/cmd.go index c78231547..f584286d8 100644 --- a/cmd/cmd.go +++ b/cmd/cmd.go @@ -187,6 +187,7 @@ var ( platformProbe probe.ReadyProbe schedulerProbe probe.ReadyProbe k2KClusterSyncProbe probe.ReadyProbe + threads int ) func init() { @@ -258,6 +259,7 @@ func init() { f.StringArrayVar(&metricsOptions.excludedMetricPrefixes, "metrics.excluded-prefixes", nil, "List of the excluded metrics prefixes") f.BoolVar(&operatorImageDiscovery.defaultStatusDiscovery, "image.discovery.status", true, "Discover Operator Image from Pod Status by default. When disabled Pod Spec is used.") f.DurationVar(&operatorImageDiscovery.timeout, "image.discovery.timeout", time.Minute, "Timeout for image discovery process") + f.IntVar(&threads, "threads", 16, "Number of the worker threads") if err := logging.Init(&cmdMain); err != nil { panic(err.Error()) } @@ -607,6 +609,7 @@ func newOperatorConfigAndDeps(id, namespace, name string) (operator.Config, oper ReconciliationDelay: operatorOptions.reconciliationDelay, ShutdownDelay: shutdownOptions.delay, ShutdownTimeout: shutdownOptions.timeout, + Threads: threads, } deps := operator.Dependencies{ Client: client, diff --git a/pkg/operator/operator.go b/pkg/operator/operator.go index 93b0144c8..49e83c59c 100644 --- a/pkg/operator/operator.go +++ b/pkg/operator/operator.go @@ -128,6 +128,7 @@ type Config struct { ReconciliationDelay time.Duration ShutdownDelay time.Duration ShutdownTimeout time.Duration + Threads int } type Dependencies struct { @@ -366,7 +367,7 @@ func (o *Operator) onStartOperatorV2(operatorType operatorV2type, stop <-chan st prometheus.MustRegister(operator) - operator.Start(8, stop) + operator.Start(o.Threads, stop) o.Dependencies.MlProbe.SetReady() o.Dependencies.AnalyticsProbe.SetReady() From e8ed542b465dacdd145a941463de25939680da37 Mon Sep 17 00:00:00 2001 From: ajanikow <12255597+ajanikow@users.noreply.github.com> Date: Wed, 15 Oct 2025 12:27:50 +0200 Subject: [PATCH 4/8] Iter --- README.md | 1 + chart/kube-arangodb-arm64/values.yaml | 2 ++ chart/kube-arangodb-enterprise-arm64/values.yaml | 2 ++ chart/kube-arangodb-enterprise/values.yaml | 2 ++ chart/kube-arangodb/templates/deployment.yaml | 1 + chart/kube-arangodb/values.yaml | 1 + docs/cli/arangodb_operator.md | 1 + pkg/deployment/resources/config_map_gateway.go | 4 ++-- .../resources/gateway/gateway_config.go | 7 +++++-- pkg/util/constants/gateway.go | 2 ++ pkg/util/kclient/ratelimiter_impl.go | 16 ++++++++++++++++ 11 files changed, 35 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index bc3c8bfcf..2c7c41181 100644 --- a/README.md +++ b/README.md @@ -231,6 +231,7 @@ Flags: --server.tls-secret-name string Name of secret containing tls.crt & tls.key for HTTPS server (if empty, self-signed certificate is used) --shutdown.delay duration The delay before running shutdown handlers (default 2s) --shutdown.timeout duration Timeout for shutdown handlers (default 30s) + --threads int Number of the worker threads (default 16) --timeout.agency duration The Agency read timeout (default 10s) --timeout.arangod duration The request timeout to the ArangoDB (default 5s) --timeout.arangod-check duration The version check request timeout to the ArangoDB (default 2s) diff --git a/chart/kube-arangodb-arm64/values.yaml b/chart/kube-arangodb-arm64/values.yaml index 0c1c70465..ae67c81c9 100644 --- a/chart/kube-arangodb-arm64/values.yaml +++ b/chart/kube-arangodb-arm64/values.yaml @@ -25,6 +25,8 @@ operator: allowChaos: false nodeSelector: {} enableCRDManagement: true + enableCRDCreation: true + threads: 8 features: deployment: true deploymentReplications: true diff --git a/chart/kube-arangodb-enterprise-arm64/values.yaml b/chart/kube-arangodb-enterprise-arm64/values.yaml index bdef90363..65b670a04 100644 --- a/chart/kube-arangodb-enterprise-arm64/values.yaml +++ b/chart/kube-arangodb-enterprise-arm64/values.yaml @@ -25,6 +25,8 @@ operator: allowChaos: false nodeSelector: {} enableCRDManagement: true + enableCRDCreation: true + threads: 8 features: deployment: true deploymentReplications: true diff --git a/chart/kube-arangodb-enterprise/values.yaml b/chart/kube-arangodb-enterprise/values.yaml index 8a1a532fa..d8f9a17e0 100644 --- a/chart/kube-arangodb-enterprise/values.yaml +++ b/chart/kube-arangodb-enterprise/values.yaml @@ -25,6 +25,8 @@ operator: allowChaos: false nodeSelector: {} enableCRDManagement: true + enableCRDCreation: true + threads: 8 features: deployment: true deploymentReplications: true diff --git a/chart/kube-arangodb/templates/deployment.yaml b/chart/kube-arangodb/templates/deployment.yaml index 58195da9c..3ecadcaaf 100644 --- a/chart/kube-arangodb/templates/deployment.yaml +++ b/chart/kube-arangodb/templates/deployment.yaml @@ -92,6 +92,7 @@ spec: imagePullPolicy: {{ .Values.operator.imagePullPolicy }} image: {{ .Values.operator.image }} args: + - --threads={{ .Values.operator.threads }} {{- if .Values.certificate.enabled }} - --server.tls-secret-name={{ template "kube-arangodb.operatorName" . }}-cert - --api.tls-secret-name={{ template "kube-arangodb.operatorName" . }}-cert diff --git a/chart/kube-arangodb/values.yaml b/chart/kube-arangodb/values.yaml index 9976152a6..ac71b9bf1 100644 --- a/chart/kube-arangodb/values.yaml +++ b/chart/kube-arangodb/values.yaml @@ -27,6 +27,7 @@ operator: nodeSelector: {} enableCRDManagement: true enableCRDCreation: true + threads: 8 features: deployment: true deploymentReplications: true diff --git a/docs/cli/arangodb_operator.md b/docs/cli/arangodb_operator.md index 3ba7dc45f..9261eab47 100644 --- a/docs/cli/arangodb_operator.md +++ b/docs/cli/arangodb_operator.md @@ -113,6 +113,7 @@ Flags: --server.tls-secret-name string Name of secret containing tls.crt & tls.key for HTTPS server (if empty, self-signed certificate is used) --shutdown.delay duration The delay before running shutdown handlers (default 2s) --shutdown.timeout duration Timeout for shutdown handlers (default 30s) + --threads int Number of the worker threads (default 16) --timeout.agency duration The Agency read timeout (default 10s) --timeout.arangod duration The request timeout to the ArangoDB (default 5s) --timeout.arangod-check duration The version check request timeout to the ArangoDB (default 2s) diff --git a/pkg/deployment/resources/config_map_gateway.go b/pkg/deployment/resources/config_map_gateway.go index 342d9fec5..85d2810cc 100644 --- a/pkg/deployment/resources/config_map_gateway.go +++ b/pkg/deployment/resources/config_map_gateway.go @@ -147,13 +147,13 @@ func (r *Resources) ensureGatewayConfig(ctx context.Context, cachedStatus inspec return errors.WithStack(errors.Wrapf(err, "Failed to render gateway inventory")) } - inventoryChecksum := util.SHA256FromStringArray(gatewayCfgYamlChecksum, util.SHA256(inventoryData)) + gatewayChecksum := util.SHA256FromStringArray(gatewayCfgYamlChecksum, util.SHA256(inventoryData)) if err := r.ensureGatewayConfigMap(ctx, cachedStatus, configMaps, GetGatewayConfigMapName(r.context.GetAPIObject().GetName()), map[string]string{ utilConstants.GatewayConfigFileName: string(gatewayCfgYaml), utilConstants.GatewayConfigChecksum: gatewayCfgYamlChecksum, utilConstants.InventoryFileName: string(inventoryData), - utilConstants.InventoryChecksum: inventoryChecksum, + utilConstants.InventoryChecksum: gatewayChecksum, }); err != nil { return err } diff --git a/pkg/deployment/resources/gateway/gateway_config.go b/pkg/deployment/resources/gateway/gateway_config.go index cc7d22936..9277ec461 100644 --- a/pkg/deployment/resources/gateway/gateway_config.go +++ b/pkg/deployment/resources/gateway/gateway_config.go @@ -334,9 +334,11 @@ func (c Config) RenderFilters() ([]*pbEnvoyListenerV3.Filter, error) { CodecType: httpConnectionManagerAPI.HttpConnectionManager_AUTO, ServerHeaderTransformation: httpConnectionManagerAPI.HttpConnectionManager_PASS_THROUGH, MergeSlashes: c.Options.GetMergeSlashes(), + RouteSpecifier: &httpConnectionManagerAPI.HttpConnectionManager_RouteConfig{ RouteConfig: &pbEnvoyRouteV3.RouteConfiguration{ - Name: "default", + Name: "default", + MaxDirectResponseBodySizeBytes: wrapperspb.UInt32(utilConstants.MaxInventorySize), VirtualHosts: []*pbEnvoyRouteV3.VirtualHost{ { Name: "default", @@ -431,7 +433,8 @@ func (c Config) HttpToHttpsChain() (*pbEnvoyListenerV3.FilterChain, error) { CodecType: httpConnectionManagerAPI.HttpConnectionManager_AUTO, RouteSpecifier: &httpConnectionManagerAPI.HttpConnectionManager_RouteConfig{ RouteConfig: &pbEnvoyRouteV3.RouteConfiguration{ - Name: "local_http", + Name: "local_http", + MaxDirectResponseBodySizeBytes: wrapperspb.UInt32(utilConstants.MaxInventorySize), VirtualHosts: []*pbEnvoyRouteV3.VirtualHost{ { Name: "local_http", diff --git a/pkg/util/constants/gateway.go b/pkg/util/constants/gateway.go index 0ac8cd88c..05f1a26e1 100644 --- a/pkg/util/constants/gateway.go +++ b/pkg/util/constants/gateway.go @@ -53,4 +53,6 @@ const ( MemberAuthVolumeMountDir = "/etc/gateway/auth/" MemberAuthVolumeName = "member-auth" + + MaxInventorySize uint32 = 4 * 1024 * 1024 ) diff --git a/pkg/util/kclient/ratelimiter_impl.go b/pkg/util/kclient/ratelimiter_impl.go index e5ae6dcbe..29b346c3f 100644 --- a/pkg/util/kclient/ratelimiter_impl.go +++ b/pkg/util/kclient/ratelimiter_impl.go @@ -57,6 +57,10 @@ func (r *rateLimiter) Accept() { r.lock.Lock() defer r.lock.Unlock() + if r.qps < 1 { + return + } + now := r.clock.Now() r.clock.Sleep(r.limiter.ReserveN(now, 1).DelayFrom(now)) } @@ -68,6 +72,10 @@ func (r *rateLimiter) QPS() float32 { r.lock.Lock() defer r.lock.Unlock() + if r.qps < 1 { + return 0 + } + return r.qps } @@ -75,6 +83,10 @@ func (r *rateLimiter) Wait(ctx context.Context) error { r.lock.Lock() defer r.lock.Unlock() + if r.qps < 1 { + return nil + } + return r.limiter.Wait(ctx) } @@ -82,5 +94,9 @@ func (r *rateLimiter) TryAccept() bool { r.lock.Lock() defer r.lock.Unlock() + if r.qps < 1 { + return true + } + return r.limiter.AllowN(r.clock.Now(), 1) } From cfbf0fd4b4d4231f65d4677230609eb72a487b32 Mon Sep 17 00:00:00 2001 From: ajanikow <12255597+ajanikow@users.noreply.github.com> Date: Thu, 16 Oct 2025 09:52:14 +0200 Subject: [PATCH 5/8] Iter --- pkg/util/kclient/ratelimiter_impl.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/util/kclient/ratelimiter_impl.go b/pkg/util/kclient/ratelimiter_impl.go index 29b346c3f..0529e9e11 100644 --- a/pkg/util/kclient/ratelimiter_impl.go +++ b/pkg/util/kclient/ratelimiter_impl.go @@ -73,7 +73,7 @@ func (r *rateLimiter) QPS() float32 { defer r.lock.Unlock() if r.qps < 1 { - return 0 + return 256 * 256 } return r.qps From a320789723d12b2cce6ad3c076d66a19d46df361 Mon Sep 17 00:00:00 2001 From: ajanikow <12255597+ajanikow@users.noreply.github.com> Date: Thu, 16 Oct 2025 10:55:36 +0200 Subject: [PATCH 6/8] Iter --- README.md | 2 +- cmd/cmd.go | 2 +- docs/cli/arangodb_operator.md | 2 +- pkg/util/kclient/ratelimiter.go | 4 ++ pkg/util/kclient/ratelimiter_impl.go | 2 +- pkg/util/kclient/ratelimiter_test.go | 93 ++++++++++++++++++++++++++++ pkg/util/parallel.go | 42 ++++++++++++- 7 files changed, 142 insertions(+), 5 deletions(-) create mode 100644 pkg/util/kclient/ratelimiter_test.go diff --git a/README.md b/README.md index 2c7c41181..5c428ff67 100644 --- a/README.md +++ b/README.md @@ -201,7 +201,7 @@ Flags: --internal.scaling-integration Enable Scaling Integration --kubernetes.burst int Burst for the k8s API (default 256) --kubernetes.max-batch-size int Size of batch during objects read (default 256) - --kubernetes.qps float32 Number of queries per second for k8s API (default 32) + --kubernetes.qps float32 Number of queries per second for k8s API. If set to 0 or less, API calls won't be throttled (default 32) --leader.label.skip Skips Leader Label for the Pod --log.format string Set log format. Allowed values: 'pretty', 'JSON'. If empty, default format is used (default "pretty") --log.level stringArray Set log levels in format or =. Possible loggers: action, agency, api-server, assertion, backup-operator, chaos-monkey, crd, deployment, deployment-ci, deployment-reconcile, deployment-replication, deployment-resilience, deployment-resources, deployment-storage, deployment-storage-pc, deployment-storage-service, generic-parent-operator, helm, http, inspector, integration-authn-v1, integration-config-v1, integration-envoy-auth-v3, integration-envoy-auth-v3-impl-auth-bearer, integration-envoy-auth-v3-impl-auth-cookie, integration-envoy-auth-v3-impl-custom-openid, integration-envoy-auth-v3-impl-pass-mode, integration-meta-v1, integration-scheduler-v2, integration-shutdown-v1, integration-storage-v1-s3, integration-storage-v2, integrations, k8s-client, kubernetes, kubernetes-access, kubernetes-client, kubernetes-informer, monitor, networking-route-operator, operator, operator-arangojob-handler, operator-v2, operator-v2-event, operator-v2-worker, panics, platform-chart-operator, platform-pod-shutdown, platform-service-operator, platform-storage-operator, pod_compare, root, root-event-recorder, scheduler-batchjob-operator, scheduler-cronjob-operator, scheduler-deployment-operator, scheduler-pod-operator, scheduler-profile-operator, server, server-authentication, webhook (default [info]) diff --git a/cmd/cmd.go b/cmd/cmd.go index f584286d8..7c795f23d 100644 --- a/cmd/cmd.go +++ b/cmd/cmd.go @@ -249,7 +249,7 @@ func init() { f.BoolVar(&operatorOptions.scalingIntegrationEnabled, "internal.scaling-integration", false, "Enable Scaling Integration") f.DurationVar(&operatorOptions.reconciliationDelay, "reconciliation.delay", 0, "Delay between reconciliation loops (<= 0 -> Disabled)") f.Int64Var(&operatorKubernetesOptions.maxBatchSize, "kubernetes.max-batch-size", globals.DefaultKubernetesRequestBatchSize, "Size of batch during objects read") - f.Float32Var(&operatorKubernetesOptions.qps, "kubernetes.qps", kclient.DefaultQPS, "Number of queries per second for k8s API") + f.Float32Var(&operatorKubernetesOptions.qps, "kubernetes.qps", kclient.DefaultQPS, "Number of queries per second for k8s API. If set to 0 or less, API calls won't be throttled") f.IntVar(&operatorKubernetesOptions.burst, "kubernetes.burst", kclient.DefaultBurst, "Burst for the k8s API") f.BoolVar(&crdOptions.install, "crd.install", true, "Install missing CRD if access is possible") f.StringArrayVar(&crdOptions.preserveUnknownFields, "crd.preserve-unknown-fields", nil, "Controls which CRD should have enabled preserve unknown fields in validation schema =. To apply for all, use crd-name 'all'.") diff --git a/docs/cli/arangodb_operator.md b/docs/cli/arangodb_operator.md index 9261eab47..e5a635305 100644 --- a/docs/cli/arangodb_operator.md +++ b/docs/cli/arangodb_operator.md @@ -83,7 +83,7 @@ Flags: --internal.scaling-integration Enable Scaling Integration --kubernetes.burst int Burst for the k8s API (default 256) --kubernetes.max-batch-size int Size of batch during objects read (default 256) - --kubernetes.qps float32 Number of queries per second for k8s API (default 32) + --kubernetes.qps float32 Number of queries per second for k8s API. If set to 0 or less, API calls won't be throttled (default 32) --leader.label.skip Skips Leader Label for the Pod --log.format string Set log format. Allowed values: 'pretty', 'JSON'. If empty, default format is used (default "pretty") --log.level stringArray Set log levels in format or =. Possible loggers: action, agency, api-server, assertion, backup-operator, chaos-monkey, crd, deployment, deployment-ci, deployment-reconcile, deployment-replication, deployment-resilience, deployment-resources, deployment-storage, deployment-storage-pc, deployment-storage-service, generic-parent-operator, helm, http, inspector, integration-authn-v1, integration-config-v1, integration-envoy-auth-v3, integration-envoy-auth-v3-impl-auth-bearer, integration-envoy-auth-v3-impl-auth-cookie, integration-envoy-auth-v3-impl-custom-openid, integration-envoy-auth-v3-impl-pass-mode, integration-meta-v1, integration-scheduler-v2, integration-shutdown-v1, integration-storage-v1-s3, integration-storage-v2, integrations, k8s-client, kubernetes, kubernetes-access, kubernetes-client, kubernetes-informer, monitor, networking-route-operator, operator, operator-arangojob-handler, operator-v2, operator-v2-event, operator-v2-worker, panics, platform-chart-operator, platform-pod-shutdown, platform-service-operator, platform-storage-operator, pod_compare, root, root-event-recorder, scheduler-batchjob-operator, scheduler-cronjob-operator, scheduler-deployment-operator, scheduler-pod-operator, scheduler-profile-operator, server, server-authentication, webhook (default [info]) diff --git a/pkg/util/kclient/ratelimiter.go b/pkg/util/kclient/ratelimiter.go index 35055de96..9f72a70d4 100644 --- a/pkg/util/kclient/ratelimiter.go +++ b/pkg/util/kclient/ratelimiter.go @@ -45,6 +45,10 @@ func GetDefaultRateLimiter() flowcontrol.RateLimiter { } func GetRateLimiter(name string) flowcontrol.RateLimiter { + return getRateLimiter(name) +} + +func getRateLimiter(name string) *rateLimiter { rateLimitersLock.Lock() defer rateLimitersLock.Unlock() diff --git a/pkg/util/kclient/ratelimiter_impl.go b/pkg/util/kclient/ratelimiter_impl.go index 0529e9e11..4ea64461a 100644 --- a/pkg/util/kclient/ratelimiter_impl.go +++ b/pkg/util/kclient/ratelimiter_impl.go @@ -1,7 +1,7 @@ // // DISCLAIMER // -// Copyright 2016-2022 ArangoDB GmbH, Cologne, Germany +// Copyright 2016-2025 ArangoDB GmbH, Cologne, Germany // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/pkg/util/kclient/ratelimiter_test.go b/pkg/util/kclient/ratelimiter_test.go new file mode 100644 index 000000000..cee9fc363 --- /dev/null +++ b/pkg/util/kclient/ratelimiter_test.go @@ -0,0 +1,93 @@ +// +// DISCLAIMER +// +// Copyright 2025 ArangoDB GmbH, Cologne, Germany +// +// 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. +// +// Copyright holder is ArangoDB GmbH, Cologne, Germany +// + +package kclient + +import ( + "context" + "testing" + "time" + + "github.com/stretchr/testify/require" + + "github.com/arangodb/kube-arangodb/pkg/util" + "github.com/arangodb/kube-arangodb/pkg/util/shutdown" +) + +func waitForExecution(ctx context.Context, count int, rl *rateLimiter) error { + return util.ParallelProcessErr(func(in int) error { + return rl.Wait(ctx) + }, 16, util.IntInput(count)) +} + +func Test_RateLimiter(t *testing.T) { + rl := getRateLimiter("TEST1") + + rl.setQPS(1) + rl.setBurst(1) + + s := time.Now() + + require.NoError(t, waitForExecution(shutdown.Context(), 3, rl)) + + require.True(t, time.Since(s) > 2*time.Second) + require.True(t, time.Since(s) < 3*time.Second) +} + +func Test_RateLimiter_Multi(t *testing.T) { + rl := getRateLimiter("TEST2") + + rl.setQPS(128) + rl.setBurst(1) + + s := time.Now() + + require.NoError(t, waitForExecution(shutdown.Context(), 200, rl)) + + require.True(t, time.Since(s) > 1*time.Second) + require.True(t, time.Since(s) < 2*time.Second) +} + +func Test_RateLimiter_Multi_Large(t *testing.T) { + rl := getRateLimiter("TEST2L") + + rl.setQPS(128) + rl.setBurst(1) + + s := time.Now() + + require.NoError(t, waitForExecution(shutdown.Context(), 257, rl)) + + require.True(t, time.Since(s) > 2*time.Second) + require.True(t, time.Since(s) < 3*time.Second) +} + +func Test_RateLimiter_EnsureErrorTrack(t *testing.T) { + rl := getRateLimiter("TEST3") + + rl.setQPS(1) + rl.setBurst(1) + + ctx, cancel := context.WithTimeout(shutdown.Context(), time.Second) + defer cancel() + + require.NoError(t, rl.Wait(ctx)) + require.Error(t, rl.Wait(ctx)) +} diff --git a/pkg/util/parallel.go b/pkg/util/parallel.go index 03fd0f77d..ac81095f3 100644 --- a/pkg/util/parallel.go +++ b/pkg/util/parallel.go @@ -20,7 +20,39 @@ package util -import "sync" +import ( + "sync" + + "github.com/arangodb/kube-arangodb/pkg/util/errors" +) + +func ParallelProcessErr[T any](caller func(in T) error, threads int, in []T) error { + errs := ParallelProcessOutput[T, error](caller, threads, in) + + return errors.Errors(errs...) +} + +func ParallelProcessOutput[T, O any](caller func(in T) O, threads int, in []T) []O { + r := ParallelInput(IntInput(len(in))) + ret := make([]O, len(in)) + var wg sync.WaitGroup + + for id := 0; id < threads; id++ { + wg.Add(1) + + go func() { + defer wg.Done() + + for id := range r { + ret[id] = caller(in[id]) + } + }() + } + + wg.Wait() + + return ret +} func ParallelProcess[T any](caller func(in T), threads int, in []T) { r := ParallelInput(in) @@ -42,6 +74,14 @@ func ParallelProcess[T any](caller func(in T), threads int, in []T) { wg.Wait() } +func IntInput(count int) []int { + var r = make([]int, count) + for i := 0; i < count; i++ { + r[i] = i + } + return r +} + func ParallelInput[T any](in []T) <-chan T { r := make(chan T) From 50d5161aec09ffa89c3c2db2fe976115b7e98f35 Mon Sep 17 00:00:00 2001 From: ajanikow <12255597+ajanikow@users.noreply.github.com> Date: Thu, 16 Oct 2025 14:58:44 +0200 Subject: [PATCH 7/8] Iter --- pkg/util/parallel.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/util/parallel.go b/pkg/util/parallel.go index ac81095f3..c4de7abb2 100644 --- a/pkg/util/parallel.go +++ b/pkg/util/parallel.go @@ -1,7 +1,7 @@ // // DISCLAIMER // -// Copyright 2016-2024 ArangoDB GmbH, Cologne, Germany +// Copyright 2016-2025 ArangoDB GmbH, Cologne, Germany // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. From c01341a8fec7f1bcff2400ef3199326e71f005f4 Mon Sep 17 00:00:00 2001 From: ajanikow <12255597+ajanikow@users.noreply.github.com> Date: Fri, 17 Oct 2025 07:55:24 +0000 Subject: [PATCH 8/8] Iter --- chart/kube-arangodb-arm64/templates/deployment.yaml | 1 + chart/kube-arangodb-enterprise-arm64/templates/deployment.yaml | 1 + chart/kube-arangodb-enterprise/templates/deployment.yaml | 1 + 3 files changed, 3 insertions(+) diff --git a/chart/kube-arangodb-arm64/templates/deployment.yaml b/chart/kube-arangodb-arm64/templates/deployment.yaml index 58195da9c..3ecadcaaf 100644 --- a/chart/kube-arangodb-arm64/templates/deployment.yaml +++ b/chart/kube-arangodb-arm64/templates/deployment.yaml @@ -92,6 +92,7 @@ spec: imagePullPolicy: {{ .Values.operator.imagePullPolicy }} image: {{ .Values.operator.image }} args: + - --threads={{ .Values.operator.threads }} {{- if .Values.certificate.enabled }} - --server.tls-secret-name={{ template "kube-arangodb.operatorName" . }}-cert - --api.tls-secret-name={{ template "kube-arangodb.operatorName" . }}-cert diff --git a/chart/kube-arangodb-enterprise-arm64/templates/deployment.yaml b/chart/kube-arangodb-enterprise-arm64/templates/deployment.yaml index 58195da9c..3ecadcaaf 100644 --- a/chart/kube-arangodb-enterprise-arm64/templates/deployment.yaml +++ b/chart/kube-arangodb-enterprise-arm64/templates/deployment.yaml @@ -92,6 +92,7 @@ spec: imagePullPolicy: {{ .Values.operator.imagePullPolicy }} image: {{ .Values.operator.image }} args: + - --threads={{ .Values.operator.threads }} {{- if .Values.certificate.enabled }} - --server.tls-secret-name={{ template "kube-arangodb.operatorName" . }}-cert - --api.tls-secret-name={{ template "kube-arangodb.operatorName" . }}-cert diff --git a/chart/kube-arangodb-enterprise/templates/deployment.yaml b/chart/kube-arangodb-enterprise/templates/deployment.yaml index 58195da9c..3ecadcaaf 100644 --- a/chart/kube-arangodb-enterprise/templates/deployment.yaml +++ b/chart/kube-arangodb-enterprise/templates/deployment.yaml @@ -92,6 +92,7 @@ spec: imagePullPolicy: {{ .Values.operator.imagePullPolicy }} image: {{ .Values.operator.image }} args: + - --threads={{ .Values.operator.threads }} {{- if .Values.certificate.enabled }} - --server.tls-secret-name={{ template "kube-arangodb.operatorName" . }}-cert - --api.tls-secret-name={{ template "kube-arangodb.operatorName" . }}-cert