private void updateClusterState( final ClusterServersConfig cfg, final RedisConnection connection) { Future<String> future = connection.async(RedisCommands.CLUSTER_NODES); future.addListener( new FutureListener<String>() { @Override public void operationComplete(Future<String> future) throws Exception { if (!future.isSuccess()) { log.error( "Can't execute CLUSTER_NODES with " + connection.getRedisClient().getAddr(), future.cause()); scheduleClusterChangeCheck(cfg); return; } String nodesValue = future.getNow(); log.debug( "cluster nodes state from {}:\n{}", connection.getRedisClient().getAddr(), nodesValue); Collection<ClusterPartition> newPartitions = parsePartitions(nodesValue); checkMasterNodesChange(newPartitions); checkSlaveNodesChange(newPartitions); checkSlotsChange(cfg, newPartitions); scheduleClusterChangeCheck(cfg); } }); }
@SuppressWarnings("unchecked") private static <T> Future<T> failedFuture() { Future<T> future = mockFuture(); when(future.isSuccess()).thenReturn(false); when(future.cause()).thenReturn(new Exception()); return future; }
private void addRemoveSlaves( final MasterSlaveEntry entry, final ClusterPartition currentPart, final ClusterPartition newPart) { Set<URI> removedSlaves = new HashSet<URI>(currentPart.getSlaveAddresses()); removedSlaves.removeAll(newPart.getSlaveAddresses()); for (URI uri : removedSlaves) { currentPart.removeSlaveAddress(uri); slaveDown(entry, uri.getHost(), uri.getPort(), FreezeReason.MANAGER); log.info("slave {} removed for slot ranges: {}", uri, currentPart.getSlotRanges()); } Set<URI> addedSlaves = new HashSet<URI>(newPart.getSlaveAddresses()); addedSlaves.removeAll(currentPart.getSlaveAddresses()); for (final URI uri : addedSlaves) { Future<Void> future = entry.addSlave(uri.getHost(), uri.getPort()); future.addListener( new FutureListener<Void>() { @Override public void operationComplete(Future<Void> future) throws Exception { if (!future.isSuccess()) { log.error("Can't add slave: " + uri, future.cause()); return; } currentPart.addSlaveAddress(uri); entry.slaveUp(uri.getHost(), uri.getPort(), FreezeReason.MANAGER); log.info("slave {} added for slot ranges: {}", uri, currentPart.getSlotRanges()); } }); } }
@Override public Future<Void> setupMasterEntry(String host, int port) { RedisClient masterClient = connectionManager.createClient(host, port); masterEntry = new ClientConnectionsEntry( masterClient, config.getMasterConnectionMinimumIdleSize(), config.getMasterConnectionPoolSize(), config.getSlaveConnectionMinimumIdleSize(), config.getSlaveSubscriptionConnectionPoolSize(), connectionManager, NodeType.MASTER, config); final Promise<Void> res = connectionManager.newPromise(); Future<Void> f = writeConnectionHolder.add(masterEntry); Future<Void> s = pubSubConnectionHolder.add(masterEntry); FutureListener<Void> listener = new FutureListener<Void>() { AtomicInteger counter = new AtomicInteger(2); @Override public void operationComplete(Future<Void> future) throws Exception { if (!future.isSuccess()) { res.setFailure(future.cause()); return; } if (counter.decrementAndGet() == 0) { res.setSuccess(null); } } }; f.addListener(listener); s.addListener(listener); return res; }
private void checkClusterState( final ClusterServersConfig cfg, final Iterator<URI> iterator, final AtomicReference<Throwable> lastException) { if (!iterator.hasNext()) { log.error("Can't update cluster state", lastException.get()); scheduleClusterChangeCheck(cfg); return; } URI uri = iterator.next(); Future<RedisConnection> connectionFuture = connect(cfg, uri); connectionFuture.addListener( new FutureListener<RedisConnection>() { @Override public void operationComplete(Future<RedisConnection> future) throws Exception { if (!future.isSuccess()) { lastException.set(future.cause()); checkClusterState(cfg, iterator, lastException); return; } RedisConnection connection = future.getNow(); updateClusterState(cfg, connection); } }); }
private Future<T> createConnection(final ClientConnectionsEntry entry) { final Promise<T> promise = connectionManager.newPromise(); Future<T> connFuture = connect(entry); connFuture.addListener( new FutureListener<T>() { @Override public void operationComplete(Future<T> future) throws Exception { if (!future.isSuccess()) { releaseConnection(entry); promiseFailure(entry, promise, future.cause()); return; } T conn = future.getNow(); if (!conn.isActive()) { promiseFailure(entry, promise, conn); return; } promiseSuccessful(entry, promise, conn); } }); return promise; }
@Override public final void operationComplete(Future<V> future) throws Exception { if (future.isSuccess()) { onSuccess(future.get()); } else { onFailure(future.cause()); } }
public Future<Void> add(final ClientConnectionsEntry entry) { Future<Void> f = entries.add(entry); f.addListener( new FutureListener<Void>() { @Override public void operationComplete(Future<Void> future) throws Exception { addr2Entry.put(entry.getClient().getAddr(), entry); pubSubEntries.add(entry); } }); return f; }
public void testSslRenegotiationRejected(ServerBootstrap sb, Bootstrap cb) throws Throwable { reset(); sb.childHandler( new ChannelInitializer<Channel>() { @Override @SuppressWarnings("deprecation") public void initChannel(Channel sch) throws Exception { serverChannel = sch; serverSslHandler = serverCtx.newHandler(sch.alloc()); sch.pipeline().addLast("ssl", serverSslHandler); sch.pipeline().addLast("handler", serverHandler); } }); cb.handler( new ChannelInitializer<Channel>() { @Override @SuppressWarnings("deprecation") public void initChannel(Channel sch) throws Exception { clientChannel = sch; clientSslHandler = clientCtx.newHandler(sch.alloc()); sch.pipeline().addLast("ssl", clientSslHandler); sch.pipeline().addLast("handler", clientHandler); } }); Channel sc = sb.bind().sync().channel(); cb.connect().sync(); Future<Channel> clientHandshakeFuture = clientSslHandler.handshakeFuture(); clientHandshakeFuture.sync(); String renegotiation = "SSL_RSA_WITH_RC4_128_SHA"; clientSslHandler.engine().setEnabledCipherSuites(new String[] {renegotiation}); clientSslHandler.renegotiate().await(); serverChannel.close().awaitUninterruptibly(); clientChannel.close().awaitUninterruptibly(); sc.close().awaitUninterruptibly(); try { if (serverException.get() != null) { throw serverException.get(); } fail(); } catch (DecoderException e) { assertTrue(e.getCause() instanceof SSLHandshakeException); } if (clientException.get() != null) { throw clientException.get(); } }
@SuppressWarnings("unchecked") private static <T> Future<T> mockFuture() { Future<T> future = (Future<T>) mock(Future.class); when(future.addListener(any())) .then( invoc -> { GenericFutureListener<Future<T>> listener = invoc.getArgumentAt(0, GenericFutureListener.class); listener.operationComplete(future); return future; }); return future; }
@Test public void testPutAsync() throws InterruptedException, ExecutionException { RMap<Integer, Integer> map = redisson.getMap("simple"); Future<Integer> future = map.putAsync(2, 3); Assert.assertNull(future.get()); Assert.assertEquals((Integer) 3, map.get(2)); Future<Integer> future1 = map.putAsync(2, 4); Assert.assertEquals((Integer) 3, future1.get()); Assert.assertEquals((Integer) 4, map.get(2)); }
@Test public void testConnectAsync() throws InterruptedException { RedisClient c = new RedisClient("localhost", 6379); Future<RedisConnection> f = c.connectAsync(); final CountDownLatch l = new CountDownLatch(1); f.addListener( (FutureListener<RedisConnection>) future -> { RedisConnection conn = future.get(); assertThat(conn.sync(RedisCommands.PING)).isEqualTo("PONG"); l.countDown(); }); l.await(10, TimeUnit.SECONDS); }
private Future<RedisConnection> connect(ClusterServersConfig cfg, final URI addr) { RedisConnection connection = nodeConnections.get(addr); if (connection != null) { return newSucceededFuture(connection); } RedisClient client = createClient(addr.getHost(), addr.getPort(), cfg.getConnectTimeout()); final Promise<RedisConnection> result = newPromise(); Future<RedisConnection> future = client.connectAsync(); future.addListener( new FutureListener<RedisConnection>() { @Override public void operationComplete(Future<RedisConnection> future) throws Exception { if (!future.isSuccess()) { result.setFailure(future.cause()); return; } RedisConnection connection = future.getNow(); Promise<RedisConnection> promise = newPromise(); connectListener.onConnect(promise, connection, null, config); promise.addListener( new FutureListener<RedisConnection>() { @Override public void operationComplete(Future<RedisConnection> future) throws Exception { if (!future.isSuccess()) { result.setFailure(future.cause()); return; } RedisConnection connection = future.getNow(); if (connection.isActive()) { nodeConnections.put(addr, connection); result.setSuccess(connection); } else { connection.closeAsync(); result.setFailure( new RedisException( "Connection to " + connection.getRedisClient().getAddr() + " is not active!")); } } }); } }); return result; }
@Test public void testScriptLoadAsync() { redisson.getBucket("foo").set("bar"); Future<String> r = redisson.getScript().scriptLoadAsync("return redis.call('get', 'foo')"); Assert.assertEquals( "282297a0228f48cd3fc6a55de6316f31422f5d17", r.awaitUninterruptibly().getNow()); String r1 = redisson .getScript() .evalSha( Mode.READ_ONLY, "282297a0228f48cd3fc6a55de6316f31422f5d17", RScript.ReturnType.VALUE, Collections.emptyList()); Assert.assertEquals("bar", r1); }
@Test public void testPerMethodScope() throws Exception { FakeTicker ticker = new FakeTicker(); int minimumRequestThreshold = 2; Duration circuitOpenWindow = Duration.ofSeconds(60); Duration counterSlidingWindow = Duration.ofSeconds(180); Duration counterUpdateInterval = Duration.ofMillis(1); Future<Object> successFuture = successFuture(); Future<Object> failedFuture = failedFuture(); Function<String, CircuitBreaker> factory = method -> new CircuitBreakerBuilder(remoteServiceName) .minimumRequestThreshold(minimumRequestThreshold) .circuitOpenWindow(circuitOpenWindow) .counterSlidingWindow(counterSlidingWindow) .counterUpdateInterval(counterUpdateInterval) .ticker(ticker) .build(); RemoteInvoker remoteInvoker = mock(RemoteInvoker.class); // Always return failed future for methodA when(remoteInvoker.invoke(any(), any(), any(), any(), eq(methodA()), any())) .thenReturn(failedFuture); // Always return success future for methodB when(remoteInvoker.invoke(any(), any(), any(), any(), eq(methodB()), any())) .thenReturn(successFuture); CircuitBreakerMapping mapping = new KeyedCircuitBreakerMapping<>(KeySelector.METHOD, factory::apply); CircuitBreakerRemoteInvoker stub = new CircuitBreakerRemoteInvoker(remoteInvoker, mapping); // CLOSED (methodA) for (int i = 0; i < minimumRequestThreshold + 1; i++) { stub.invoke(eventLoop, uri, options, codec, methodA(), args); ticker.advance(Duration.ofMillis(1).toNanos()); } // OPEN (methodA) Future<Object> future1 = stub.invoke(eventLoop, uri, options, codec, methodA(), args); assertThat(future1.isSuccess(), is(false)); assertThat(future1.cause(), instanceOf(FailFastException.class)); // CLOSED (methodB) Future<Object> future2 = stub.invoke(eventLoop, uri, options, codec, methodB(), args); assertThat(future2.isSuccess(), is(true)); }
public void runCleanTask(final String name, String timeoutSetName, long currentDate) { final Long lastExpired = lastExpiredTime.get(name); long now = System.currentTimeMillis(); if (lastExpired == null) { if (lastExpiredTime.putIfAbsent(name, now) != null) { return; } } else if (lastExpired + expireTaskExecutionDelay >= now) { if (!lastExpiredTime.replace(name, lastExpired, now)) { return; } } else { return; } Future<Integer> future = cleanupExpiredEntires(name, timeoutSetName, null, valuesAmountToClean, false); future.addListener( new FutureListener<Integer>() { @Override public void operationComplete(Future<Integer> future) throws Exception { executor .getConnectionManager() .getGroup() .schedule( new Runnable() { @Override public void run() { lastExpiredTime.remove(name, lastExpired); } }, expireTaskExecutionDelay * 3, TimeUnit.SECONDS); if (!future.isSuccess()) { log.warn( "Can't execute clean task for expired values. RSetCache name: " + name, future.cause()); return; } } }); }
@Override public void run() { Future<Integer> future = cleanupExpiredEntires(name, timeoutSetName, maxIdleSetName, keysLimit, multimap); future.addListener( new FutureListener<Integer>() { @Override public void operationComplete(Future<Integer> future) throws Exception { if (!future.isSuccess()) { schedule(); return; } Integer size = future.getNow(); if (sizeHistory.size() == 2) { if (sizeHistory.peekFirst() > sizeHistory.peekLast() && sizeHistory.peekLast() > size) { delay = Math.min(maxDelay, (int) (delay * 1.5)); } // if (sizeHistory.peekFirst() < sizeHistory.peekLast() // && sizeHistory.peekLast() < size) { // prevDelay = Math.max(minDelay, prevDelay/2); // } if (sizeHistory.peekFirst() == sizeHistory.peekLast() && sizeHistory.peekLast() == size) { if (size == keysLimit) { delay = Math.max(minDelay, delay / 4); } if (size == 0) { delay = Math.min(maxDelay, (int) (delay * 1.5)); } } sizeHistory.pollFirst(); } sizeHistory.add(size); schedule(); } }); }
private Publisher<Integer> addListener(final RedisPubSubListener<M> pubSubListener) { final Promise<Integer> promise = commandExecutor.getConnectionManager().newPromise(); Future<PubSubConnectionEntry> future = commandExecutor.getConnectionManager().subscribe(codec, name, pubSubListener); future.addListener( new FutureListener<PubSubConnectionEntry>() { @Override public void operationComplete(Future<PubSubConnectionEntry> future) throws Exception { if (!future.isSuccess()) { promise.setFailure(future.cause()); return; } promise.setSuccess(System.identityHashCode(pubSubListener)); } }); return new NettyFuturePublisher<Integer>(promise); }
@Test(timeout = 10000) public void testBindDeadLock() throws Exception { final Bootstrap bootstrapA = new Bootstrap(); bootstrapA.group(groupA); bootstrapA.channel(LocalChannel.class); bootstrapA.handler(dummyHandler); final Bootstrap bootstrapB = new Bootstrap(); bootstrapB.group(groupB); bootstrapB.channel(LocalChannel.class); bootstrapB.handler(dummyHandler); List<Future<?>> bindFutures = new ArrayList<Future<?>>(); // Try to bind from each other. for (int i = 0; i < 1024; i++) { bindFutures.add( groupA .next() .submit( new Runnable() { @Override public void run() { bootstrapB.bind(LocalAddress.ANY); } })); bindFutures.add( groupB .next() .submit( new Runnable() { @Override public void run() { bootstrapA.bind(LocalAddress.ANY); } })); } for (Future<?> f : bindFutures) { f.sync(); } }
@Override public <T> Future<T> invoke( EventLoop eventLoop, URI uri, ClientOptions options, ClientCodec codec, Method method, Object[] args) throws Exception { final CircuitBreaker circuitBreaker; try { circuitBreaker = mapping.get(eventLoop, uri, options, codec, method, args); } catch (Throwable t) { logger.warn("Failed to get a circuit breaker from mapping", t); return delegate().invoke(eventLoop, uri, options, codec, method, args); } if (circuitBreaker.canRequest()) { final Future<T> resultFut = delegate().invoke(eventLoop, uri, options, codec, method, args); resultFut.addListener( future -> { if (future.isSuccess()) { // reports success event circuitBreaker.onSuccess(); } else { circuitBreaker.onFailure(future.cause()); } }); return resultFut; } else { // the circuit is tripped // prepares a failed resultPromise final Promise<T> resultPromise = eventLoop.newPromise(); resultPromise.setFailure(new FailFastException(circuitBreaker)); codec.prepareRequest(method, args, resultPromise); // returns immediately without calling succeeding remote invokers return resultPromise; } }
public ClusterConnectionManager(ClusterServersConfig cfg, Config config) { super(config); connectListener = new ClusterConnectionListener(cfg.getReadMode() == ReadMode.SLAVE); this.config = create(cfg); init(this.config); Exception lastException = null; for (URI addr : cfg.getNodeAddresses()) { Future<RedisConnection> connectionFuture = connect(cfg, addr); try { RedisConnection connection = connectionFuture.syncUninterruptibly().getNow(); String nodesValue = connection.sync(RedisCommands.CLUSTER_NODES); Collection<ClusterPartition> partitions = parsePartitions(nodesValue); List<Future<Collection<Future<Void>>>> futures = new ArrayList<Future<Collection<Future<Void>>>>(); for (ClusterPartition partition : partitions) { Future<Collection<Future<Void>>> masterFuture = addMasterEntry(partition, cfg); futures.add(masterFuture); } for (Future<Collection<Future<Void>>> masterFuture : futures) { masterFuture.syncUninterruptibly(); for (Future<Void> future : masterFuture.getNow()) { future.syncUninterruptibly(); } } break; } catch (Exception e) { lastException = e; log.warn(e.getMessage()); } } if (lastPartitions.isEmpty()) { throw new RedisConnectionException("Can't connect to servers!", lastException); } scheduleClusterChangeCheck(cfg); }
@Test public void testEvalshaAsync() { RScript s = redisson.getScript(); String res = s.scriptLoad(null, "return redis.call('get', 'foo')"); Assert.assertEquals("282297a0228f48cd3fc6a55de6316f31422f5d17", res); redisson.getBucket("foo").set("bar"); String r = redisson .getScript() .eval(Mode.READ_ONLY, "return redis.call('get', 'foo')", RScript.ReturnType.VALUE); Assert.assertEquals("bar", r); Future<Object> r1 = redisson .getScript() .evalShaAsync( Mode.READ_ONLY, "282297a0228f48cd3fc6a55de6316f31422f5d17", RScript.ReturnType.VALUE, Collections.emptyList()); Assert.assertEquals("bar", r1.awaitUninterruptibly().getNow()); }
@After public void teardown() throws Exception { serverChannel.close().sync(); Future<?> serverGroup = sb.group().shutdownGracefully(0, 0, MILLISECONDS); Future<?> serverChildGroup = sb.childGroup().shutdownGracefully(0, 0, MILLISECONDS); Future<?> clientGroup = cb.group().shutdownGracefully(0, 0, MILLISECONDS); serverGroup.sync(); serverChildGroup.sync(); clientGroup.sync(); }
@After public void teardown() throws Exception { cleanupCapturedRequests(); cleanupCapturedResponses(); serverChannel.close().sync(); Future<?> serverGroup = sb.group().shutdownGracefully(0, 0, TimeUnit.MILLISECONDS); Future<?> serverChildGroup = sb.childGroup().shutdownGracefully(0, 0, TimeUnit.MILLISECONDS); Future<?> clientGroup = cb.group().shutdownGracefully(0, 0, TimeUnit.MILLISECONDS); serverGroup.sync(); serverChildGroup.sync(); clientGroup.sync(); clientDelegator = null; serverDelegator = null; clientChannel = null; serverChannel = null; serverConnectedChannel = null; }
@Test public void testExceptionFilter() throws Exception { FakeTicker ticker = new FakeTicker(); int minimumRequestThreshold = 2; Duration circuitOpenWindow = Duration.ofSeconds(60); Duration counterSlidingWindow = Duration.ofSeconds(180); Duration counterUpdateInterval = Duration.ofMillis(1); Future<Object> failedFuture = failedFuture(); // a filter that ignores all exception ExceptionFilter exceptionFilter = (cause) -> false; CircuitBreaker circuitBreaker = new CircuitBreakerBuilder(remoteServiceName) .minimumRequestThreshold(minimumRequestThreshold) .circuitOpenWindow(circuitOpenWindow) .counterSlidingWindow(counterSlidingWindow) .counterUpdateInterval(counterUpdateInterval) .exceptionFilter(exceptionFilter) .ticker(ticker) .build(); RemoteInvoker remoteInvoker = mock(RemoteInvoker.class); // return failed future when(remoteInvoker.invoke(any(), any(), any(), any(), any(), any())).thenReturn(failedFuture); CircuitBreakerMapping mapping = (eventLoop1, uri, options1, codec1, method, args1) -> circuitBreaker; CircuitBreakerRemoteInvoker stub = new CircuitBreakerRemoteInvoker(remoteInvoker, mapping); // CLOSED for (int i = 0; i < minimumRequestThreshold + 1; i++) { Future<Object> future = stub.invoke(eventLoop, uri, options, codec, methodA(), args); // The future is `failedFuture` itself assertThat(future.isSuccess(), is(false)); // This is not a CircuitBreakerException assertThat(future.cause(), is(not(instanceOf(FailFastException.class)))); ticker.advance(Duration.ofMillis(1).toNanos()); } // OPEN Future<Object> future1 = stub.invoke(eventLoop, uri, options, codec, methodA(), args); // The circuit is still CLOSED assertThat(future1.isSuccess(), is(false)); assertThat(future1.cause(), is(not(instanceOf(FailFastException.class)))); }
@Override public V pollLastAndOfferFirstTo(String queueName, long timeout, TimeUnit unit) throws InterruptedException { Future<V> res = pollLastAndOfferFirstToAsync(queueName, timeout, unit); return res.await().getNow(); }
@Override public V poll(long timeout, TimeUnit unit) throws InterruptedException { Future<V> res = pollAsync(timeout, unit); return res.await().getNow(); }
protected void cancelTask() { if (responseTask != null) { responseTask.cancel(true); } }
@Override public V take() throws InterruptedException { Future<V> res = takeAsync(); return res.await().getNow(); }
@SuppressWarnings("unchecked") private static <T> Future<T> successFuture() { Future<T> future = mockFuture(); when(future.isSuccess()).thenReturn(true); return future; }