Skip to content

gRPC max frame size benchmark experiment: dynamic #20

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 22 additions & 0 deletions netty/src/main/java/io/grpc/netty/AbstractNettyHandler.java
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
package io.grpc.netty;

import static com.google.common.base.Preconditions.checkNotNull;
import static io.netty.handler.codec.http2.Http2CodecUtil.MAX_FRAME_SIZE_UPPER_BOUND;
import static io.netty.handler.codec.http2.Http2CodecUtil.getEmbeddedHttp2Exception;

import com.google.common.annotations.VisibleForTesting;
Expand All @@ -28,6 +29,7 @@
import io.netty.handler.codec.http2.Http2ConnectionDecoder;
import io.netty.handler.codec.http2.Http2ConnectionEncoder;
import io.netty.handler.codec.http2.Http2Exception;
import io.netty.handler.codec.http2.Http2FrameReader;
import io.netty.handler.codec.http2.Http2LocalFlowController;
import io.netty.handler.codec.http2.Http2Settings;
import io.netty.handler.codec.http2.Http2Stream;
Expand All @@ -42,24 +44,28 @@ abstract class AbstractNettyHandler extends GrpcHttp2ConnectionHandler {

private final int initialConnectionWindow;
private final FlowControlPinger flowControlPing;
private int windowSizeToFrameRatio;

private boolean autoTuneFlowControlOn;
private ChannelHandlerContext ctx;
private boolean initialWindowSent = false;
private final Ticker ticker;

private static final long BDP_MEASUREMENT_PING = 1234;
private final Http2FrameReader frameReader;

AbstractNettyHandler(
ChannelPromise channelUnused,
Http2ConnectionDecoder decoder,
Http2ConnectionEncoder encoder,
Http2FrameReader frameReader,
Http2Settings initialSettings,
ChannelLogger negotiationLogger,
boolean autoFlowControl,
PingLimiter pingLimiter,
Ticker ticker) {
super(channelUnused, decoder, encoder, initialSettings, negotiationLogger);
this.frameReader = frameReader;

// During a graceful shutdown, wait until all streams are closed.
gracefulShutdownTimeoutMillis(GRACEFUL_SHUTDOWN_NO_TIMEOUT);
Expand All @@ -73,6 +79,8 @@ abstract class AbstractNettyHandler extends GrpcHttp2ConnectionHandler {
}
this.flowControlPing = new FlowControlPinger(pingLimiter);
this.ticker = checkNotNull(ticker, "ticker");
this.windowSizeToFrameRatio =
Integer.parseInt(System.getenv().getOrDefault("GRPC_WINDOW_TO_FRAME_RATIO", "0"));
}

@Override
Expand Down Expand Up @@ -130,6 +138,11 @@ void setAutoTuneFlowControl(boolean isOn) {
autoTuneFlowControlOn = isOn;
}

@VisibleForTesting
void setWindowToFrameRatio(int ratio) {
windowSizeToFrameRatio = ratio;
}

/**
* Class for handling flow control pinging and flow control window updates as necessary.
*/
Expand Down Expand Up @@ -216,6 +229,15 @@ public void updateWindow() throws Http2Exception {
fc.initialWindowSize(targetWindow);
Http2Settings settings = new Http2Settings();
settings.initialWindowSize(targetWindow);
if (windowSizeToFrameRatio > 0) {
// Netty default, gRPC default: 0x4000 = 16,384 B = 16 KiB
// Absolute maximum (inclusive): 0xFFFFFF = 16,777,215 B = (16 MiB - 1 B)
// (defined in https://www.rfc-editor.org/rfc/rfc9113.html#SETTINGS_MAX_FRAME_SIZE)
int frameSize = Math.min(targetWindow / windowSizeToFrameRatio, MAX_FRAME_SIZE_UPPER_BOUND);
settings.maxFrameSize(frameSize);
frameReader.configuration().frameSizePolicy().maxFrameSize(frameSize);

}
frameWriter().writeSettings(ctx(), settings, ctx().newPromise());
}

Expand Down
4 changes: 3 additions & 1 deletion netty/src/main/java/io/grpc/netty/NettyClientHandler.java
Original file line number Diff line number Diff line change
Expand Up @@ -231,6 +231,7 @@ static NettyClientHandler newHandler(
return new NettyClientHandler(
decoder,
encoder,
frameReader,
settings,
negotiationLogger,
lifecycleManager,
Expand All @@ -248,6 +249,7 @@ static NettyClientHandler newHandler(
private NettyClientHandler(
Http2ConnectionDecoder decoder,
Http2ConnectionEncoder encoder,
Http2FrameReader frameReader,
Http2Settings settings,
ChannelLogger negotiationLogger,
ClientTransportLifecycleManager lifecycleManager,
Expand All @@ -260,7 +262,7 @@ private NettyClientHandler(
boolean autoFlowControl,
PingLimiter pingLimiter,
Ticker ticker) {
super(/* channelUnused= */ null, decoder, encoder, settings,
super(/* channelUnused= */ null, decoder, encoder, frameReader, settings,
negotiationLogger, autoFlowControl, pingLimiter, ticker);
this.lifecycleManager = lifecycleManager;
this.keepAliveManager = keepAliveManager;
Expand Down
5 changes: 3 additions & 2 deletions netty/src/main/java/io/grpc/netty/NettyServerHandler.java
Original file line number Diff line number Diff line change
Expand Up @@ -258,7 +258,7 @@ static NettyServerHandler newHandler(
transportListener,
streamTracerFactories,
transportTracer,
decoder, encoder, settings,
decoder, encoder, frameReader, settings,
maxMessageSize,
keepAliveTimeInNanos, keepAliveTimeoutInNanos,
maxConnectionIdleInNanos,
Expand All @@ -276,6 +276,7 @@ private NettyServerHandler(
TransportTracer transportTracer,
Http2ConnectionDecoder decoder,
Http2ConnectionEncoder encoder,
Http2FrameReader frameReader,
Http2Settings settings,
int maxMessageSize,
long keepAliveTimeInNanos,
Expand All @@ -287,7 +288,7 @@ private NettyServerHandler(
boolean autoFlowControl,
Attributes eagAttributes,
Ticker ticker) {
super(channelUnused, decoder, encoder, settings, new ServerChannelLogger(),
super(channelUnused, decoder, encoder, frameReader, settings, new ServerChannelLogger(),
autoFlowControl, null, ticker);

final MaxConnectionIdleManager maxConnectionIdleManager;
Expand Down
20 changes: 15 additions & 5 deletions netty/src/test/java/io/grpc/netty/NettyHandlerTestBase.java
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
package io.grpc.netty;

import static com.google.common.base.Charsets.UTF_8;
import static io.netty.handler.codec.http2.Http2CodecUtil.DEFAULT_MAX_FRAME_SIZE;
import static io.netty.handler.codec.http2.Http2CodecUtil.DEFAULT_WINDOW_SIZE;
import static org.junit.Assert.assertEquals;
import static org.mockito.AdditionalAnswers.delegatesTo;
Expand Down Expand Up @@ -55,6 +56,7 @@
import io.netty.handler.codec.http2.Http2ConnectionHandler;
import io.netty.handler.codec.http2.Http2Exception;
import io.netty.handler.codec.http2.Http2FrameReader;
import io.netty.handler.codec.http2.Http2FrameSizePolicy;
import io.netty.handler.codec.http2.Http2FrameWriter;
import io.netty.handler.codec.http2.Http2Headers;
import io.netty.handler.codec.http2.Http2HeadersDecoder;
Expand Down Expand Up @@ -571,39 +573,47 @@ public void testPingBackoff() throws Exception {

@Test
public void bdpPingWindowResizing() throws Exception {
this.flowControlWindow = 1024 * 8;
int windowToFrameRatio = 4;
flowControlWindow = 1024 * 32;
manualSetUp();
makeStream();

AbstractNettyHandler handler = (AbstractNettyHandler) handler();
handler.setAutoTuneFlowControl(true);
handler.setWindowToFrameRatio(windowToFrameRatio);
Http2LocalFlowController localFlowController = connection().local().flowController();

long pingData = handler.flowControlPing().payload();
int initialWindowSize = localFlowController.initialWindowSize();
byte[] data1Kb = initXkbBuffer(1);
byte[] data10Kb = initXkbBuffer(10);
byte[] data40Kb = initXkbBuffer(40);
Http2FrameSizePolicy frameSizePolicy = frameReader.configuration().frameSizePolicy();

readXCopies(1, data1Kb); // initiate ping
fakeClock().forwardNanos(2);
readPingAck(pingData); // should not resize window because of small target window
assertEquals(initialWindowSize, localFlowController.initialWindowSize());
assertEquals(DEFAULT_MAX_FRAME_SIZE, frameSizePolicy.maxFrameSize());

readXCopies(2, data10Kb); // initiate ping on first
readXCopies(2, data40Kb); // initiate ping on first
fakeClock().forwardNanos(200);
readPingAck(pingData); // should resize window
int windowSizeA = localFlowController.initialWindowSize();
Assert.assertNotEquals(initialWindowSize, windowSizeA);
assertEquals(windowSizeA / windowToFrameRatio, frameSizePolicy.maxFrameSize());

readXCopies(3, data10Kb); // initiate ping w/ first 10K packet
readXCopies(3, data40Kb); // initiate ping w/ first 10K packet
fakeClock().forwardNanos(5000);
readPingAck(pingData); // should not resize window as bandwidth didn't increase
Assert.assertEquals(windowSizeA, localFlowController.initialWindowSize());
assertEquals(windowSizeA / windowToFrameRatio, frameSizePolicy.maxFrameSize());

readXCopies(6, data10Kb); // initiate ping with fist packet
readXCopies(6, data40Kb); // initiate ping with fist packet
fakeClock().forwardNanos(100);
readPingAck(pingData); // should resize window
int windowSizeB = localFlowController.initialWindowSize();
Assert.assertNotEquals(windowSizeA, windowSizeB);
assertEquals(windowSizeB / windowToFrameRatio, frameSizePolicy.maxFrameSize());
}

private void readPingAck(long pingData) throws Exception {
Expand Down