From 94ec4fe4037f91eb46b5120e1f7bd27d8c178f3a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ste=CC=81phane=20Duchesneau?= Date: Thu, 17 Sep 2026 10:18:41 -0400 Subject: [PATCH 1/4] Send and receive gRPC messages up to 3.5 GiB Firehose blocks can reach 3 GiB. Clients received at most 1 GiB and servers sent at most 2 GiB. Server receive limits stay as they are since requests are small. --- CHANGELOG.md | 1 + client.go | 13 +++++++++++-- server/standard/server.go | 2 ++ 3 files changed, 14 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2351518..aa6bc95 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## Unreleased +- Clients created with `NewClientConn` (and the `NewInternalClient`/`NewExternalClient` helpers) send and receive messages up to `dgrpc.MaxMessageSize` (3.5 GiB) instead of receiving up to 1 GiB and sending up to 2 GiB. Standard servers send messages up to `dgrpc.MaxMessageSize` instead of 2 GiB. Server receive limits are unchanged. - Add zstd support for connectrpc server - Remove connectrpc error middleware by default. You need to add it `connectrpc.NewErrorsInterceptor` - Added abiliy to map connectrpc error diff --git a/client.go b/client.go index ddf6d24..5d5d25a 100644 --- a/client.go +++ b/client.go @@ -17,6 +17,7 @@ package dgrpc import ( "crypto/tls" "fmt" + "math" "time" "go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc" @@ -31,7 +32,14 @@ var roundrobinDialOption = grpc.WithDefaultServiceConfig(`{"loadBalancingConfig" var insecureDialOption = grpc.WithTransportCredentials(insecure.NewCredentials()) var tlsClientDialOption = grpc.WithTransportCredentials(credentials.NewClientTLSFromCert(nil, "")) -var largeRecvMsgSizeCallOption = grpc.MaxCallRecvMsgSize(1024 * 1024 * 1024) +// MaxMessageSize is the largest gRPC message in bytes that clients created by this package +// send or receive and that servers created by this package send. It fits blocks up to 3 GiB +// with room for the response around them. gRPC frames a message with a 4 bytes length, so +// it cannot go past 4 GiB. +const MaxMessageSize = min(3584*1024*1024, math.MaxInt) + +var largeRecvMsgSizeCallOption = grpc.MaxCallRecvMsgSize(MaxMessageSize) +var largeSendMsgSizeCallOption = grpc.MaxCallSendMsgSize(MaxMessageSize) var hangOnResolveErrorCallOption = grpc.WaitForReady(true) var hangOnResolveErrorDialOption = grpc.WithDefaultCallOptions(hangOnResolveErrorCallOption) @@ -86,7 +94,7 @@ func NewExternalClientConn(remoteAddr string, extraOpts ...grpc.DialOption) (*gr } // NewClientConn creates a default gRPC ClientConn with round robin, keepalive, OpenTelemetry tracing and -// large receive message size (max 1 GiB) configured. +// large send and receive message size (max [MaxMessageSize]) configured. // // If the remoteAddr starts with "xds://", it will use xDS credentials, transport credentials are not // configured and default gRPC applies which is full TLS. @@ -99,6 +107,7 @@ func NewClientConn(remoteAddr string, extraOpts ...grpc.DialOption) (*grpc.Clien grpc.WithStatsHandler(otelgrpc.NewClientHandler()), grpc.WithDefaultCallOptions( largeRecvMsgSizeCallOption, + largeSendMsgSizeCallOption, ), } diff --git a/server/standard/server.go b/server/standard/server.go index 588cbc5..9d63cc6 100644 --- a/server/standard/server.go +++ b/server/standard/server.go @@ -28,6 +28,7 @@ import ( grpc_zap "github.com/grpc-ecosystem/go-grpc-middleware/logging/zap" grpc_ctxtags "github.com/grpc-ecosystem/go-grpc-middleware/tags" grpc_prometheus "github.com/grpc-ecosystem/go-grpc-prometheus" + "github.com/streamingfast/dgrpc" "github.com/streamingfast/dgrpc/server" "github.com/streamingfast/dgrpc/server/tracelog" "github.com/streamingfast/shutter" @@ -410,6 +411,7 @@ func newGRPCServer(options *server.Options) *grpc.Server { s := grpc.NewServer( append([]grpc.ServerOption{ grpc.StatsHandler(otelgrpc.NewServerHandler(otelgrpc.WithTracerProvider(tracerProvider))), + grpc.MaxSendMsgSize(dgrpc.MaxMessageSize), grpc.KeepaliveEnforcementPolicy( keepalive.EnforcementPolicy{ MinTime: 15 * time.Second, From 8e1ac72142dd7a45c1828a70a418ffcf22ef43d6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ste=CC=81phane=20Duchesneau?= Date: Thu, 17 Sep 2026 10:31:18 -0400 Subject: [PATCH 2/4] Limit responses to 2 GiB and keep request limits Requests are small, only responses carry blocks. --- CHANGELOG.md | 2 +- client.go | 17 +++++++---------- server/standard/server.go | 2 +- 3 files changed, 9 insertions(+), 12 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index aa6bc95..730ac8f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,7 +5,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## Unreleased -- Clients created with `NewClientConn` (and the `NewInternalClient`/`NewExternalClient` helpers) send and receive messages up to `dgrpc.MaxMessageSize` (3.5 GiB) instead of receiving up to 1 GiB and sending up to 2 GiB. Standard servers send messages up to `dgrpc.MaxMessageSize` instead of 2 GiB. Server receive limits are unchanged. +- Clients created with `NewClientConn` (and the `NewInternalClient`/`NewExternalClient` helpers) receive messages up to `dgrpc.MaxResponseSize` (2 GiB) instead of 1 GiB. Standard servers send messages up to `dgrpc.MaxResponseSize`, one byte more than the grpc-go default. Request limits (client send, server receive) are unchanged. - Add zstd support for connectrpc server - Remove connectrpc error middleware by default. You need to add it `connectrpc.NewErrorsInterceptor` - Added abiliy to map connectrpc error diff --git a/client.go b/client.go index 5d5d25a..57f96d1 100644 --- a/client.go +++ b/client.go @@ -32,14 +32,12 @@ var roundrobinDialOption = grpc.WithDefaultServiceConfig(`{"loadBalancingConfig" var insecureDialOption = grpc.WithTransportCredentials(insecure.NewCredentials()) var tlsClientDialOption = grpc.WithTransportCredentials(credentials.NewClientTLSFromCert(nil, "")) -// MaxMessageSize is the largest gRPC message in bytes that clients created by this package -// send or receive and that servers created by this package send. It fits blocks up to 3 GiB -// with room for the response around them. gRPC frames a message with a 4 bytes length, so -// it cannot go past 4 GiB. -const MaxMessageSize = min(3584*1024*1024, math.MaxInt) - -var largeRecvMsgSizeCallOption = grpc.MaxCallRecvMsgSize(MaxMessageSize) -var largeSendMsgSizeCallOption = grpc.MaxCallSendMsgSize(MaxMessageSize) +// MaxResponseSize is the largest gRPC response message in bytes that clients created by this +// package receive and that servers created by this package send. Requests keep the gRPC +// limits, they are expected to be small. +const MaxResponseSize = min(2*1024*1024*1024, math.MaxInt) + +var largeRecvMsgSizeCallOption = grpc.MaxCallRecvMsgSize(MaxResponseSize) var hangOnResolveErrorCallOption = grpc.WaitForReady(true) var hangOnResolveErrorDialOption = grpc.WithDefaultCallOptions(hangOnResolveErrorCallOption) @@ -94,7 +92,7 @@ func NewExternalClientConn(remoteAddr string, extraOpts ...grpc.DialOption) (*gr } // NewClientConn creates a default gRPC ClientConn with round robin, keepalive, OpenTelemetry tracing and -// large send and receive message size (max [MaxMessageSize]) configured. +// large receive message size (max [MaxResponseSize]) configured. // // If the remoteAddr starts with "xds://", it will use xDS credentials, transport credentials are not // configured and default gRPC applies which is full TLS. @@ -107,7 +105,6 @@ func NewClientConn(remoteAddr string, extraOpts ...grpc.DialOption) (*grpc.Clien grpc.WithStatsHandler(otelgrpc.NewClientHandler()), grpc.WithDefaultCallOptions( largeRecvMsgSizeCallOption, - largeSendMsgSizeCallOption, ), } diff --git a/server/standard/server.go b/server/standard/server.go index 9d63cc6..8cf3465 100644 --- a/server/standard/server.go +++ b/server/standard/server.go @@ -411,7 +411,7 @@ func newGRPCServer(options *server.Options) *grpc.Server { s := grpc.NewServer( append([]grpc.ServerOption{ grpc.StatsHandler(otelgrpc.NewServerHandler(otelgrpc.WithTracerProvider(tracerProvider))), - grpc.MaxSendMsgSize(dgrpc.MaxMessageSize), + grpc.MaxSendMsgSize(dgrpc.MaxResponseSize), grpc.KeepaliveEnforcementPolicy( keepalive.EnforcementPolicy{ MinTime: 15 * time.Second, From 3602f772284293ecd606d5e6aff93ae9fe8b153e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ste=CC=81phane=20Duchesneau?= Date: Thu, 17 Sep 2026 11:08:27 -0400 Subject: [PATCH 3/4] Add a test workflow for the required test check --- .github/workflows/test.yml | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 .github/workflows/test.yml diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml new file mode 100644 index 0000000..1857810 --- /dev/null +++ b/.github/workflows/test.yml @@ -0,0 +1,20 @@ +name: test + +on: + pull_request: + push: + branches: + - "develop" + +jobs: + test: + runs-on: ubuntu-24.04 + steps: + - uses: actions/checkout@v4 + + - uses: actions/setup-go@v5 + with: + go-version-file: go.mod + + - name: Test + run: go test ./... From 5048d72c66443bca5e5001df6e02dc3c69949bae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ste=CC=81phane=20Duchesneau?= Date: Thu, 17 Sep 2026 11:40:40 -0400 Subject: [PATCH 4/4] Match the grpc-go default server send limit --- CHANGELOG.md | 2 +- client.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 730ac8f..ea1bece 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,7 +5,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## Unreleased -- Clients created with `NewClientConn` (and the `NewInternalClient`/`NewExternalClient` helpers) receive messages up to `dgrpc.MaxResponseSize` (2 GiB) instead of 1 GiB. Standard servers send messages up to `dgrpc.MaxResponseSize`, one byte more than the grpc-go default. Request limits (client send, server receive) are unchanged. +- Clients created with `NewClientConn` (and the `NewInternalClient`/`NewExternalClient` helpers) receive messages up to `dgrpc.MaxResponseSize` (2 GiB − 1 byte, `math.MaxInt32`) instead of 1 GiB. Standard servers send messages up to `dgrpc.MaxResponseSize`, the same as the grpc-go default. Request limits (client send, server receive) are unchanged. - Add zstd support for connectrpc server - Remove connectrpc error middleware by default. You need to add it `connectrpc.NewErrorsInterceptor` - Added abiliy to map connectrpc error diff --git a/client.go b/client.go index 57f96d1..0e94919 100644 --- a/client.go +++ b/client.go @@ -35,7 +35,7 @@ var tlsClientDialOption = grpc.WithTransportCredentials(credentials.NewClientTLS // MaxResponseSize is the largest gRPC response message in bytes that clients created by this // package receive and that servers created by this package send. Requests keep the gRPC // limits, they are expected to be small. -const MaxResponseSize = min(2*1024*1024*1024, math.MaxInt) +const MaxResponseSize = math.MaxInt32 var largeRecvMsgSizeCallOption = grpc.MaxCallRecvMsgSize(MaxResponseSize) var hangOnResolveErrorCallOption = grpc.WaitForReady(true)