Code review comment: Since `writeAndFlush` is only used to send messages to the `ServerHandler` to trigger channel close, I don't think it is necessary to introduce `writeAndFlush `. This is a change in my mind(Maybe not the best way, but just an example): ```suggestion         CompletableFuture<Void> inactiveFuture = new CompletableFuture<>();         CompletableFuture<Channel> serverChannelFuture = new CompletableFuture<>();          NettyProtocol protocol =                 new NettyProtocol(null, null) {                      @Override                     public ChannelHandler[] getServerChannelHandlers() {                         return new ChannelHandler[] {                             // Close on read                             new ChannelInboundHandlerAdapter() {                                 @Override                                 public void channelRegistered(ChannelHandlerContext ctx)                                         throws Exception {                                     super.channelRegistered(ctx);                                     serverChannelFuture.complete(ctx.channel());                                 }                             }                         };                     }                      @Override                     public ChannelHandler[] getClientChannelHandlers() {                         return new ChannelHandler[] {                             new ChannelInactiveFutureHandler(inactiveFuture)                         };                     }                 };         NettyTestUtil.NettyServerAndClient serverAndClient = initServerAndClient(protocol);          PartitionRequestClientFactory factory =                 new PartitionRequestClientFactory(                         serverAndClient.client(), 2, 1, connectionReuseEnabled);          ConnectionID connectionID = serverAndClient.getConnectionID(RESOURCE_ID, 0);         NettyPartitionRequestClient oldClient = factory.createPartitionRequestClient(connectionID);                  // close server channel         assertThat(serverChannelFuture).isDone();         Channel channel = serverChannelFuture.get();         channel.close();          inactiveFuture.get();          NettyPartitionRequestClient newClient = factory.createPartitionRequestClient(connectionID);         assertThat(newClient).as("Factory should create a new client.").isNotSameAs(oldClient);         shutdown(serverAndClient); ``` 1. No longer rely on the client to send messages to close the server channel. 2. Is the assertion of `hasError` really necessary? Our test is just to verify that `NettyPartitionRequestClient` is different.

Code under review:
void testConnectionReuseWhenRemoteCloseAndNoInputChannel() throws Exception {
        CompletableFuture<Void> inactiveFuture = new CompletableFuture<>();
        NettyProtocol protocol =
                new NettyProtocol(null, null) {

                    @Override
                    public ChannelHandler[] getServerChannelHandlers() {
                        return new ChannelHandler[] {
                            
                            new ChannelInboundHandlerAdapter() {
                                @Override
                                public void channelRead(ChannelHandlerContext ctx, Object msg) {
                                    ctx.channel().close();
                                }
                            }
                        };
                    }

                    @Override
                    public ChannelHandler[] getClientChannelHandlers() {
                        return new ChannelHandler[] {
                            new ChannelInactiveFutureHandler(inactiveFuture)
                        };
                    }
                };
        NettyTestUtil.NettyServerAndClient serverAndClient = initServerAndClient(protocol);

        PartitionRequestClientFactory factory =
                new PartitionRequestClientFactory(
                        serverAndClient.client(), 2, 1, connectionReuseEnabled);

        ConnectionID connectionID = serverAndClient.getConnectionID(RESOURCE_ID, 0);
        NettyPartitionRequestClient oldClient = factory.createPartitionRequestClient(connectionID);

        assertThat(oldClient.hasError()).isFalse();

        
        oldClient.writeAndFlush(Unpooled.buffer().writerIndex(16));

        inactiveFuture.get();
        assertThat(oldClient.hasError()).isTrue();

        NettyPartitionRequestClient newClient = factory.createPartitionRequestClient(connectionID);
        assertThat(newClient).as("Factory should create a new client.").isNotSameAs(oldClient);
        shutdown(serverAndClient);
    }

Line commented on: NettyPartitionRequestClient newClient = factory.createPartitionRequestClient(connectionID);

Revised code:
void testConnectionReuseWhenRemoteCloseAndNoInputChannel() throws Exception {
        CompletableFuture<Void> inactiveFuture = new CompletableFuture<>();
        CompletableFuture<Channel> serverChannelFuture = new CompletableFuture<>();
        NettyProtocol protocol =
                new NettyProtocol(null, null) {
                    @Override
                    public ChannelHandler[] getServerChannelHandlers() {
                        return new ChannelHandler[] {
                            
                            new ChannelInboundHandlerAdapter() {
                                @Override
                                public void channelRegistered(ChannelHandlerContext ctx)
                                        throws Exception {
                                    super.channelRegistered(ctx);
                                    serverChannelFuture.complete(ctx.channel());
                                }
                            }
                        };
                    }

                    @Override
                    public ChannelHandler[] getClientChannelHandlers() {
                        return new ChannelHandler[] {
                            new ChannelInactiveFutureHandler(inactiveFuture)
                        };
                    }
                };
        NettyTestUtil.NettyServerAndClient serverAndClient = initServerAndClient(protocol);

        PartitionRequestClientFactory factory =
                new PartitionRequestClientFactory(
                        serverAndClient.client(), 2, 1, connectionReuseEnabled);

        ConnectionID connectionID = serverAndClient.getConnectionID(RESOURCE_ID, 0);
        NettyPartitionRequestClient oldClient = factory.createPartitionRequestClient(connectionID);

        
        Channel channel = serverChannelFuture.get();
        channel.close();
        inactiveFuture.get();
        NettyPartitionRequestClient newClient = factory.createPartitionRequestClient(connectionID);
        assertThat(newClient).as("Factory should create a new client.").isNotSameAs(oldClient);
        shutdown(serverAndClient);
    }