From 73a5e7df21681038912cccfddf8aec484d3cfe20 Mon Sep 17 00:00:00 2001 From: rrossi-sc Date: Mon, 6 Jul 2026 15:10:39 +1000 Subject: [PATCH] feat: add configurable gRPC message size limits Add MaxRecvMsgSize and MaxSendMsgSize options to GRPCServerConfig to allow configuring server-side gRPC message size limits. This enables operators to increase the default 4MiB limit for both external API calls and internal dispatch operations. New flags: - --grpc-max-recv-msg-size (default: 4194304) - --grpc-max-send-msg-size (default: 4194304) - --dispatch-cluster-max-recv-msg-size (default: 4194304) - --dispatch-cluster-max-send-msg-size (default: 4194304) Use case: LookupSubjects with complex permission schemas involving recursive relations (e.g., group#downwards_member) and set operations can exceed the default limit due to internal CollectingDispatchStream buffering during Union/Intersection/Exclusion operations. Fixes #3213 --- pkg/cmd/util/util.go | 29 +++++++++++++++++++--------- pkg/cmd/util/zz_generated.options.go | 18 +++++++++++++++++ 2 files changed, 38 insertions(+), 9 deletions(-) diff --git a/pkg/cmd/util/util.go b/pkg/cmd/util/util.go index 197dc9a635..497b3fcb19 100644 --- a/pkg/cmd/util/util.go +++ b/pkg/cmd/util/util.go @@ -32,15 +32,17 @@ import ( const BufferedNetwork string = "buffnet" type GRPCServerConfig struct { - Address string `debugmap:"visible"` - Network string `debugmap:"visible" default:"tcp"` - TLSCertPath string `debugmap:"visible"` - TLSKeyPath string `debugmap:"visible"` - MaxConnAge time.Duration `debugmap:"visible" default:"30s"` - Enabled bool `debugmap:"visible"` - BufferSize int `debugmap:"visible"` - ClientCAPath string `debugmap:"visible"` - MaxWorkers uint32 `debugmap:"visible"` + Address string `debugmap:"visible"` + Network string `debugmap:"visible" default:"tcp"` + TLSCertPath string `debugmap:"visible"` + TLSKeyPath string `debugmap:"visible"` + MaxConnAge time.Duration `debugmap:"visible" default:"30s"` + Enabled bool `debugmap:"visible"` + BufferSize int `debugmap:"visible"` + ClientCAPath string `debugmap:"visible"` + MaxWorkers uint32 `debugmap:"visible"` + MaxRecvMsgSize int `debugmap:"visible" default:"4194304"` + MaxSendMsgSize int `debugmap:"visible" default:"4194304"` flagPrefix string } @@ -64,6 +66,8 @@ func RegisterGRPCServerFlags(flags *pflag.FlagSet, config *GRPCServerConfig, fla flags.DurationVar(&config.MaxConnAge, flagPrefix+"-max-conn-age", 30*time.Second, "how long a connection serving "+serviceName+" should be able to live") flags.BoolVar(&config.Enabled, flagPrefix+"-enabled", defaultEnabled, "enable "+serviceName+" gRPC server") flags.Uint32Var(&config.MaxWorkers, flagPrefix+"-max-workers", 0, "set the number of workers for this server (0 value means 1 worker per request)") + flags.IntVar(&config.MaxRecvMsgSize, flagPrefix+"-max-recv-msg-size", 4*1024*1024, "maximum size in bytes of gRPC messages the server can receive") + flags.IntVar(&config.MaxSendMsgSize, flagPrefix+"-max-send-msg-size", 4*1024*1024, "maximum size in bytes of gRPC messages the server can send") } // Complete takes a set of default options and returns a completed server @@ -78,6 +82,13 @@ func (c *GRPCServerConfig) Complete(level zerolog.Level, svcRegistrationFn func( MaxConnectionAge: c.MaxConnAge, }), grpc.NumStreamWorkers(c.MaxWorkers)) + if c.MaxRecvMsgSize > 0 { + opts = append(opts, grpc.MaxRecvMsgSize(c.MaxRecvMsgSize)) + } + if c.MaxSendMsgSize > 0 { + opts = append(opts, grpc.MaxSendMsgSize(c.MaxSendMsgSize)) + } + tlsOpts, certWatcher, err := c.tlsOpts() if err != nil { return nil, err diff --git a/pkg/cmd/util/zz_generated.options.go b/pkg/cmd/util/zz_generated.options.go index 1c44dd3d6f..836a40ed56 100644 --- a/pkg/cmd/util/zz_generated.options.go +++ b/pkg/cmd/util/zz_generated.options.go @@ -39,6 +39,8 @@ func (g *GRPCServerConfig) ToOption() GRPCServerConfigOption { to.BufferSize = g.BufferSize to.ClientCAPath = g.ClientCAPath to.MaxWorkers = g.MaxWorkers + to.MaxRecvMsgSize = g.MaxRecvMsgSize + to.MaxSendMsgSize = g.MaxSendMsgSize } } @@ -80,6 +82,8 @@ func (g *GRPCServerConfig) DebugMap() map[string]any { debugMap["ClientCAPath"] = g.ClientCAPath } debugMap["MaxWorkers"] = g.MaxWorkers + debugMap["MaxRecvMsgSize"] = g.MaxRecvMsgSize + debugMap["MaxSendMsgSize"] = g.MaxSendMsgSize return debugMap } @@ -183,6 +187,20 @@ func WithMaxWorkers(maxWorkers uint32) GRPCServerConfigOption { } } +// WithMaxRecvMsgSize returns an option that can set MaxRecvMsgSize on a GRPCServerConfig +func WithMaxRecvMsgSize(maxRecvMsgSize int) GRPCServerConfigOption { + return func(g *GRPCServerConfig) { + g.MaxRecvMsgSize = maxRecvMsgSize + } +} + +// WithMaxSendMsgSize returns an option that can set MaxSendMsgSize on a GRPCServerConfig +func WithMaxSendMsgSize(maxSendMsgSize int) GRPCServerConfigOption { + return func(g *GRPCServerConfig) { + g.MaxSendMsgSize = maxSendMsgSize + } +} + type HTTPServerConfigOption func(h *HTTPServerConfig) // NewHTTPServerConfigWithOptions creates a new HTTPServerConfig with the passed in options set