@Test public void commandRetriedChannelClosesWhileFlush() throws Exception { assumeTrue(Version.identify().get("netty-transport").artifactVersion().startsWith("4.0.2")); RedisCommands<String, String> connection = client.connect().sync(); RedisCommands<String, String> verificationConnection = client.connect().sync(); RedisChannelWriter<String, String> channelWriter = getRedisChannelHandler(connection).getChannelWriter(); connection.set(key, "1"); assertThat(verificationConnection.get(key)).isEqualTo("1"); final CountDownLatch block = new CountDownLatch(1); ConnectionWatchdog connectionWatchdog = Connections.getConnectionWatchdog(connection.getStatefulConnection()); AsyncCommand<String, String, Object> command = getBlockOnEncodeCommand(block); channelWriter.write(command); connectionWatchdog.setReconnectSuspended(true); Channel channel = getChannel(getRedisChannelHandler(connection)); channel.unsafe().disconnect(channel.newPromise()); assertThat(channel.isOpen()).isFalse(); assertThat(command.isCancelled()).isFalse(); assertThat(command.isDone()).isFalse(); block.countDown(); assertThat(command.await(2, TimeUnit.SECONDS)).isFalse(); connectionWatchdog.setReconnectSuspended(false); connectionWatchdog.scheduleReconnect(); assertThat(command.await(2, TimeUnit.SECONDS)).isTrue(); assertThat(command.isCancelled()).isFalse(); assertThat(command.isDone()).isTrue(); assertThat(verificationConnection.get(key)).isEqualTo("2"); assertThat(getQueue(getRedisChannelHandler(connection))).isEmpty(); assertThat(getCommandBuffer(getRedisChannelHandler(connection))).isEmpty(); connection.close(); verificationConnection.close(); }
@Test public void commandNotExecutedChannelClosesWhileFlush() throws Exception { RedisCommands<String, String> connection = client.connect().sync(); RedisCommands<String, String> verificationConnection = client.connect().sync(); RedisChannelWriter<String, String> channelWriter = getRedisChannelHandler(connection).getChannelWriter(); connection.set(key, "1"); assertThat(verificationConnection.get(key)).isEqualTo("1"); final CountDownLatch block = new CountDownLatch(1); AsyncCommand<String, String, Object> command = new AsyncCommand<String, String, Object>( new Command<>( CommandType.INCR, new IntegerOutput(CODEC), new CommandArgs<>(CODEC).addKey(key))) { @Override public void encode(ByteBuf buf) { try { block.await(); } catch (InterruptedException e) { } super.encode(buf); } }; channelWriter.write(command); Channel channel = getChannel(getRedisChannelHandler(connection)); channel.unsafe().disconnect(channel.newPromise()); assertThat(channel.isOpen()).isFalse(); assertThat(command.isCancelled()).isFalse(); assertThat(command.isDone()).isFalse(); block.countDown(); assertThat(command.await(2, TimeUnit.SECONDS)).isTrue(); assertThat(command.isCancelled()).isFalse(); assertThat(command.isDone()).isTrue(); assertThat(verificationConnection.get(key)).isEqualTo("1"); assertThat(getQueue(getRedisChannelHandler(connection))).isEmpty(); assertThat(getCommandBuffer(getRedisChannelHandler(connection))).isEmpty(); connection.close(); }
private ChannelFuture doBind(final SocketAddress localAddress) { final ChannelFuture regFuture = initAndRegister(); final Channel channel = regFuture.channel(); if (regFuture.cause() != null) { return regFuture; } if (regFuture.isDone()) { // At this point we know that the registration was complete and succesful. ChannelPromise promise = channel.newPromise(); doBind0(regFuture, channel, localAddress, promise); return promise; } else { // Registration future is almost always fulfilled already, but just in case it's not. final PendingRegistrationPromise promise = new PendingRegistrationPromise(channel); regFuture.addListener( new ChannelFutureListener() { @Override public void operationComplete(ChannelFuture future) throws Exception { Throwable cause = future.cause(); if (cause != null) { // Registration on the EventLoop failed so fail the ChannelPromise directly to not // cause an // IllegalStateException once we try to access the EventLoop of the Channel. promise.setFailure(cause); } else { // Registration was successful, so set the correct executor to use. // See https://github.com/netty/netty/issues/2586 promise.executor = channel.eventLoop(); } doBind0(regFuture, channel, localAddress, promise); } }); return promise; } }
@Override public ChannelFuture register(Channel channel) { super.register(channel).syncUninterruptibly(); promise = channel.newPromise(); return promise; }
public void write( ActiveMQBuffer buffer, final boolean flush, final boolean batched, final ChannelFutureListener futureListener) { try { writeLock.acquire(); try { if (batchBuffer == null && batchingEnabled && batched && !flush) { // Lazily create batch buffer batchBuffer = ActiveMQBuffers.dynamicBuffer(BATCHING_BUFFER_SIZE); } if (batchBuffer != null) { batchBuffer.writeBytes(buffer, 0, buffer.writerIndex()); if (batchBuffer.writerIndex() >= BATCHING_BUFFER_SIZE || !batched || flush) { // If the batch buffer is full or it's flush param or not batched then flush the buffer buffer = batchBuffer; } else { return; } if (!batched || flush) { batchBuffer = null; } else { // Create a new buffer batchBuffer = ActiveMQBuffers.dynamicBuffer(BATCHING_BUFFER_SIZE); } } // depending on if we need to flush or not we can use a voidPromise or // use a normal promise final ByteBuf buf = buffer.byteBuf(); final ChannelPromise promise; if (flush || futureListener != null) { promise = channel.newPromise(); } else { promise = channel.voidPromise(); } EventLoop eventLoop = channel.eventLoop(); boolean inEventLoop = eventLoop.inEventLoop(); if (!inEventLoop) { if (futureListener != null) { channel.writeAndFlush(buf, promise).addListener(futureListener); } else { channel.writeAndFlush(buf, promise); } } else { // create a task which will be picked up by the eventloop and trigger the write. // This is mainly needed as this method is triggered by different threads for the same // channel. // if we not do this we may produce out of order writes. final Runnable task = new Runnable() { @Override public void run() { if (futureListener != null) { channel.writeAndFlush(buf, promise).addListener(futureListener); } else { channel.writeAndFlush(buf, promise); } } }; // execute the task on the eventloop eventLoop.execute(task); } // only try to wait if not in the eventloop otherwise we will produce a deadlock if (flush && !inEventLoop) { while (true) { try { boolean ok = promise.await(10000); if (!ok) { ActiveMQClientLogger.LOGGER.timeoutFlushingPacket(); } break; } catch (InterruptedException e) { throw new ActiveMQInterruptedException(e); } } } } finally { writeLock.release(); } } catch (InterruptedException e) { throw new ActiveMQInterruptedException(e); } }