Skip to content

Huge performance degradation when fragmentation is enabled #994

Description

@koldat

When one define custom mtu to be used for fragment size it significantly degrades performance. Attached example code sends 1M records with size 5 bytes. Before fix it takes 39 seconds. After fix it takes 5 seconds (same time as with no custom fragmentation). We need to enable it, because Websocket max data size is 64kB and we support both transports.

Expected Behavior

Fragmentation setup should not affect performance. Especially when data buffers are smaller than mtu.

Actual Behavior

There is huge performance degradation.

Steps to Reproduce

publicclassRsocketTest {
privatestaticfinalLoggerlogger = LoggerFactory.getLogger(RsocketTest.class);
publicstaticvoidmain(String[] args) {
RSocketrsocket = newRSocket() {
@OverridepublicFlux<Payload> requestStream(Payloadpayload) {
ByteBufferdata = ByteBuffer.wrap(newbyte[5]);
returnFlux.generate(() -> newAtomicInteger(1000000), (state, s) -> {
if (state.decrementAndGet() == 0) {
s.complete();
} else {
s.next(DefaultPayload.create(data));
}
returnstate;
});
}
};
RSocketServer.create(SocketAcceptor.with(rsocket))
.fragment(60000)
.bind(TcpServerTransport.create(
TcpServer.create().host("localhost").port(7000)))
.subscribe();
RSocketsocket = RSocketConnector.create()
.fragment(60000)
.connect(TcpClientTransport.create(
TcpClient.create().host("localhost").port(7000)))
.block();
Flux<Payload> res = socket.requestStream(DefaultPayload.create(ByteBuffer.wrap("Hello".getBytes())));
longstart = System.currentTimeMillis();
res
.limitRate(1000)
.blockLast();
logger.info("Call took: {}", System.currentTimeMillis() - start);
socket.dispose();
}
}

Possible Solution

FragmentationDuplexConnection change (I am creating PR now):

@OverridepublicMono<Void> send(Publisher<ByteBuf> frames) {
returndelegate.send(Flux.from(frames)
.concatMap(frame -> {
FrameTypeframeType = FrameHeaderCodec.frameType(frame);
intreadableBytes = frame.readableBytes();
if (!shouldFragment(frameType, readableBytes)) {
returnFlux.just(frame);
}
returnlogFragments(Flux.from(fragmentFrame(alloc(), mtu, frame, frameType)));
}));
}
@OverridepublicMono<Void> sendOne(ByteBufframe) {
FrameTypeframeType = FrameHeaderCodec.frameType(frame);
intreadableBytes = frame.readableBytes();
if (!shouldFragment(frameType, readableBytes)) {
returndelegate.sendOne(frame);
}
Flux<ByteBuf> fragments = Flux.from(fragmentFrame(alloc(), mtu, frame, frameType));
fragments = logFragments(fragments);
returndelegate.send(fragments);
}
protectedFlux<ByteBuf> logFragments(Flux<ByteBuf> fragments) {
if (logger.isDebugEnabled()) {
fragments =
fragments.doOnNext(
byteBuf -> {
logger.debug(
"{} - stream id {} - frame type {} - \n {}",
type,
FrameHeaderCodec.streamId(byteBuf),
FrameHeaderCodec.frameType(byteBuf),
ByteBufUtil.prettyHexDump(byteBuf));
});
}
returnfragments;
}

Your Environment

  • RSocket version(s) used: 1.0.2

@OlegDokuka can you take a look? Can we please include this into 1.0.4?

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions