diff --git a/README.md b/README.md
index af289db..62cd281 100755
--- a/README.md
+++ b/README.md
@@ -118,7 +118,7 @@ const SupportPackageIsVersion1 = true
```
-## func [ConfigureInterceptors]()
+## func [ConfigureInterceptors]()
```go
func ConfigureInterceptors(DoNotLogGRPCReflection bool, traceHeaderName string, responseTimeLogLevel string, responseTimeLogErrorOnly bool)
@@ -127,7 +127,7 @@ func ConfigureInterceptors(DoNotLogGRPCReflection bool, traceHeaderName string,
ConfigureInterceptors configures the interceptors package with the provided settings.
-## func [InitializeVTProto]()
+## func [InitializeVTProto]()
```go
func InitializeVTProto()
@@ -138,7 +138,7 @@ InitializeVTProto initializes the vtproto package for use with the service
https://github.com/planetscale/vtprotobuf?tab=readme-ov-file#mixing-protobuf-implementations-with-grpc
-## func [OTELMeterProvider]()
+## func [OTELMeterProvider]()
```go
func OTELMeterProvider() otelmetric.MeterProvider
@@ -147,7 +147,7 @@ func OTELMeterProvider() otelmetric.MeterProvider
OTELMeterProvider returns the global OTel MeterProvider. This is a convenience accessor for code that needs the interface type.
-## func [SetOTELGRPCClientOptions]()
+## func [SetOTELGRPCClientOptions]()
```go
func SetOTELGRPCClientOptions(opts ...otelgrpc.Option)
@@ -156,7 +156,7 @@ func SetOTELGRPCClientOptions(opts ...otelgrpc.Option)
Deprecated: Use SetOTELOptions instead. Only applies when OTEL\_USE\_LEGACY\_INSTRUMENTATION=true.
-## func [SetOTELGRPCServerOptions]()
+## func [SetOTELGRPCServerOptions]()
```go
func SetOTELGRPCServerOptions(opts ...otelgrpc.Option)
@@ -165,7 +165,7 @@ func SetOTELGRPCServerOptions(opts ...otelgrpc.Option)
Deprecated: Use SetOTELOptions instead. Only applies when OTEL\_USE\_LEGACY\_INSTRUMENTATION=true.
-## func [SetOTELOptions]()
+## func [SetOTELOptions]()
```go
func SetOTELOptions(opts grpcotel.Options)
@@ -174,7 +174,7 @@ func SetOTELOptions(opts grpcotel.Options)
SetOTELOptions configures the native gRPC stats/opentelemetry integration. Must be called during init, before the gRPC server starts. When set, processConfig\(\) will NOT overwrite these with auto\-built options.
-## func [SetupAutoMaxProcs]()
+## func [SetupAutoMaxProcs]()
```go
func SetupAutoMaxProcs()
@@ -192,7 +192,7 @@ func SetupEnvironment(env string)
SetupEnvironment sets the environment This is used to identify the environment in Sentry and New Relic env is the environment to set for the service \(e.g. prod, staging, dev\)
-## func [SetupHystrixPrometheus]()
+## func [SetupHystrixPrometheus]()
```go
func SetupHystrixPrometheus()
@@ -210,7 +210,7 @@ func SetupLogger(logLevel string, jsonlogs bool) error
SetupLogger sets up the logger It uses the coldbrew logger to log messages to stdout logLevel is the log level to set for the logger jsonlogs is a boolean to enable or disable json logs
-## func [SetupNROpenTelemetry]()
+## func [SetupNROpenTelemetry]()
```go
func SetupNROpenTelemetry(serviceName, license, version string, ratio float64) error
@@ -237,7 +237,7 @@ func SetupNewRelic(serviceName, apiKey string, tracing bool) error
SetupNewRelic sets up the New Relic tracing and monitoring agent for the service It uses the New Relic Go Agent to send traces to New Relic One APM and Insights serviceName is the name of the service apiKey is the New Relic license key tracing is a boolean to enable or disable tracing
-## func [SetupOTELMetrics]()
+## func [SetupOTELMetrics]()
```go
func SetupOTELMetrics(config OTLPConfig, interval time.Duration) (*sdkmetric.MeterProvider, error)
@@ -248,7 +248,7 @@ SetupOTELMetrics creates a MeterProvider with an OTLP gRPC exporter that reuses
Call this after SetupOpenTelemetry so the shared resource is available.
-## func [SetupOpenTelemetry]()
+## func [SetupOpenTelemetry]()
```go
func SetupOpenTelemetry(config OTLPConfig) error
@@ -324,7 +324,7 @@ type CB interface {
```
-### func [New]()
+### func [New]()
```go
func New(c config.Config) CB
diff --git a/config/README.md b/config/README.md
index 7f87c78..37da074 100755
--- a/config/README.md
+++ b/config/README.md
@@ -144,19 +144,19 @@ type Config struct {
// MaxConnectionIdle is a duration for the amount of time after which an
// idle connection would be closed by sending a GoAway. Idleness duration is
// defined since the most recent time the number of outstanding RPCs became
- // zero or the connection establishment.
+ // zero or the connection establishment. Set to -1 to disable (infinite).
// https://github.com/grpc/grpc-go/blob/v1.48.0/keepalive/keepalive.go#L50
- GRPCServerMaxConnectionIdleInSeconds int `envconfig:"GRPC_SERVER_MAX_CONNECTION_IDLE_IN_SECONDS"`
+ GRPCServerMaxConnectionIdleInSeconds int `envconfig:"GRPC_SERVER_MAX_CONNECTION_IDLE_IN_SECONDS" default:"300"`
// MaxConnectionAge is a duration for the maximum amount of time a
// connection may exist before it will be closed by sending a GoAway. A
// random jitter of +/-10% will be added to MaxConnectionAge to spread out
- // connection storms.
+ // connection storms. Set to -1 to disable (infinite).
// https://github.com/grpc/grpc-go/blob/v1.48.0/keepalive/keepalive.go#L50
- GRPCServerMaxConnectionAgeInSeconds int `envconfig:"GRPC_SERVER_MAX_CONNECTION_AGE_IN_SECONDS"`
+ GRPCServerMaxConnectionAgeInSeconds int `envconfig:"GRPC_SERVER_MAX_CONNECTION_AGE_IN_SECONDS" default:"1800"`
// MaxConnectionAgeGrace is an additive period after MaxConnectionAge after
- // which the connection will be forcibly closed.
+ // which the connection will be forcibly closed. Set to -1 to disable (infinite).
// https://github.com/grpc/grpc-go/blob/v1.48.0/keepalive/keepalive.go#L50
- GRPCServerMaxConnectionAgeGraceInSeconds int `envconfig:"GRPC_SERVER_MAX_CONNECTION_AGE_GRACE_IN_SECONDS"`
+ GRPCServerMaxConnectionAgeGraceInSeconds int `envconfig:"GRPC_SERVER_MAX_CONNECTION_AGE_GRACE_IN_SECONDS" default:"30"`
// DisableAutoMaxProcs disables the automatic setting of GOMAXPROCS
// This is useful when running in a container where the container runtime sets GOMAXPROCS for you already
diff --git a/config/config.go b/config/config.go
index b54d4c2..30ba825 100644
--- a/config/config.go
+++ b/config/config.go
@@ -76,19 +76,19 @@ type Config struct {
// MaxConnectionIdle is a duration for the amount of time after which an
// idle connection would be closed by sending a GoAway. Idleness duration is
// defined since the most recent time the number of outstanding RPCs became
- // zero or the connection establishment.
+ // zero or the connection establishment. Set to -1 to disable (infinite).
// https://github.com/grpc/grpc-go/blob/v1.48.0/keepalive/keepalive.go#L50
- GRPCServerMaxConnectionIdleInSeconds int `envconfig:"GRPC_SERVER_MAX_CONNECTION_IDLE_IN_SECONDS"`
+ GRPCServerMaxConnectionIdleInSeconds int `envconfig:"GRPC_SERVER_MAX_CONNECTION_IDLE_IN_SECONDS" default:"300"`
// MaxConnectionAge is a duration for the maximum amount of time a
// connection may exist before it will be closed by sending a GoAway. A
// random jitter of +/-10% will be added to MaxConnectionAge to spread out
- // connection storms.
+ // connection storms. Set to -1 to disable (infinite).
// https://github.com/grpc/grpc-go/blob/v1.48.0/keepalive/keepalive.go#L50
- GRPCServerMaxConnectionAgeInSeconds int `envconfig:"GRPC_SERVER_MAX_CONNECTION_AGE_IN_SECONDS"`
+ GRPCServerMaxConnectionAgeInSeconds int `envconfig:"GRPC_SERVER_MAX_CONNECTION_AGE_IN_SECONDS" default:"1800"`
// MaxConnectionAgeGrace is an additive period after MaxConnectionAge after
- // which the connection will be forcibly closed.
+ // which the connection will be forcibly closed. Set to -1 to disable (infinite).
// https://github.com/grpc/grpc-go/blob/v1.48.0/keepalive/keepalive.go#L50
- GRPCServerMaxConnectionAgeGraceInSeconds int `envconfig:"GRPC_SERVER_MAX_CONNECTION_AGE_GRACE_IN_SECONDS"`
+ GRPCServerMaxConnectionAgeGraceInSeconds int `envconfig:"GRPC_SERVER_MAX_CONNECTION_AGE_GRACE_IN_SECONDS" default:"30"`
// DisableAutoMaxProcs disables the automatic setting of GOMAXPROCS
// This is useful when running in a container where the container runtime sets GOMAXPROCS for you already
diff --git a/core.go b/core.go
index 2694d3d..77ade5c 100644
--- a/core.go
+++ b/core.go
@@ -628,9 +628,9 @@ func (c *cb) getGRPCServerOptions() []grpc.ServerOption {
so = append(so, grpc.MaxSendMsgSize(c.config.GRPCMaxSendMsgSize))
}
- if c.config.GRPCServerMaxConnectionAgeGraceInSeconds > 0 ||
- c.config.GRPCServerMaxConnectionAgeInSeconds > 0 ||
- c.config.GRPCServerMaxConnectionIdleInSeconds > 0 {
+ if c.config.GRPCServerMaxConnectionAgeGraceInSeconds != 0 ||
+ c.config.GRPCServerMaxConnectionAgeInSeconds != 0 ||
+ c.config.GRPCServerMaxConnectionIdleInSeconds != 0 {
option := keepalive.ServerParameters{}
if c.config.GRPCServerMaxConnectionIdleInSeconds > 0 {
option.MaxConnectionIdle = time.Duration(
diff --git a/core_coverage_test.go b/core_coverage_test.go
index 7176694..65d8e29 100644
--- a/core_coverage_test.go
+++ b/core_coverage_test.go
@@ -449,12 +449,78 @@ func TestGetCustomHeaderMatcher_EmptyPrefix(t *testing.T) {
// --- Group 2: gRPC Server Options ---
-func TestGetGRPCServerOptions_Default(t *testing.T) {
+func testKeepaliveBaseline() int {
+ base := &cb{config: config.Config{}}
+ return len(base.getGRPCServerOptions())
+}
+
+func TestGetGRPCServerOptions_WithEnvconfigDefaults(t *testing.T) {
// removed t.Parallel() — core tests mutate package-level globals
- c := &cb{config: config.Config{}}
+ // Validates behavior when config has the envconfig default values (300, 1800, 30).
+ // Keepalive option should be appended beyond the baseline.
+ baseline := testKeepaliveBaseline()
+ c := &cb{config: config.Config{
+ GRPCServerMaxConnectionIdleInSeconds: 300,
+ GRPCServerMaxConnectionAgeInSeconds: 1800,
+ GRPCServerMaxConnectionAgeGraceInSeconds: 30,
+ }}
opts := c.getGRPCServerOptions()
- if len(opts) < 2 {
- t.Fatalf("expected at least 2 server options, got %d", len(opts))
+ if len(opts) <= baseline {
+ t.Fatalf("expected keepalive option beyond baseline %d, got %d", baseline, len(opts))
+ }
+}
+
+func TestGetGRPCServerOptions_KeepaliveDisabledWithNegativeOne(t *testing.T) {
+ // removed t.Parallel() — core tests mutate package-level globals
+ // Setting all values to -1 should still append keepalive params (outer != 0 check),
+ // but individual parameters are not set on ServerParameters (inner > 0 check),
+ // so gRPC uses infinity for each.
+ baseline := testKeepaliveBaseline()
+ c := &cb{config: config.Config{
+ GRPCServerMaxConnectionIdleInSeconds: -1,
+ GRPCServerMaxConnectionAgeInSeconds: -1,
+ GRPCServerMaxConnectionAgeGraceInSeconds: -1,
+ }}
+ opts := c.getGRPCServerOptions()
+ if len(opts) <= baseline {
+ t.Fatalf("expected keepalive option beyond baseline %d, got %d", baseline, len(opts))
+ }
+}
+
+func TestGetGRPCServerOptions_KeepaliveMixed(t *testing.T) {
+ // removed t.Parallel() — core tests mutate package-level globals
+ // Mix of -1 (disabled) and positive (enabled) values should still append
+ // keepalive params relative to the all-zero baseline.
+ baseline := testKeepaliveBaseline()
+ c := &cb{config: config.Config{
+ GRPCServerMaxConnectionIdleInSeconds: -1,
+ GRPCServerMaxConnectionAgeInSeconds: 1800,
+ GRPCServerMaxConnectionAgeGraceInSeconds: 30,
+ }}
+ opts := c.getGRPCServerOptions()
+ if len(opts) <= baseline {
+ t.Fatalf("expected keepalive option beyond baseline %d, got %d", baseline, len(opts))
+ }
+}
+
+func TestGetGRPCServerOptions_KeepaliveAllZero(t *testing.T) {
+ // removed t.Parallel() — core tests mutate package-level globals
+ // All zeros should NOT add keepalive params (outer != 0 check is false).
+ // Compare against a positive-value config to prove zero omits keepalive.
+ zeroConfig := &cb{config: config.Config{
+ GRPCServerMaxConnectionIdleInSeconds: 0,
+ GRPCServerMaxConnectionAgeInSeconds: 0,
+ GRPCServerMaxConnectionAgeGraceInSeconds: 0,
+ }}
+ positiveConfig := &cb{config: config.Config{
+ GRPCServerMaxConnectionIdleInSeconds: 300,
+ GRPCServerMaxConnectionAgeInSeconds: 1800,
+ GRPCServerMaxConnectionAgeGraceInSeconds: 30,
+ }}
+ zeroOpts := zeroConfig.getGRPCServerOptions()
+ positiveOpts := positiveConfig.getGRPCServerOptions()
+ if len(zeroOpts) >= len(positiveOpts) {
+ t.Fatalf("expected fewer options with all-zero keepalive than positive, got %d vs %d", len(zeroOpts), len(positiveOpts))
}
}
@@ -1303,7 +1369,7 @@ func TestSetupOpenTelemetry_MissingServiceName(t *testing.T) {
}
func TestConfigureInterceptors_BothBranches(t *testing.T) {
- ConfigureInterceptors(true, "X-My-Trace", "info", false)
+ ConfigureInterceptors(true, "X-My-Trace", "info", false, 60)
}
func TestConfig_Validate_HTTPCompressionMinSize(t *testing.T) {
diff --git a/go.mod b/go.mod
index ab5026d..40b3c30 100644
--- a/go.mod
+++ b/go.mod
@@ -1,6 +1,6 @@
module github.com/go-coldbrew/core
-go 1.25.8
+go 1.25.9
require (
github.com/afex/hystrix-go v0.0.0-20180502004556-fa1af6a1f4f5