package com.lambdaworks.redis.cluster;
import static org.mockito.Matchers.any;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import java.util.concurrent.TimeUnit;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.runners.MockitoJUnitRunner;
import com.lambdaworks.redis.resource.ClientResources;
import io.netty.util.concurrent.EventExecutorGroup;
/**
* @author Mark Paluch
*/
@RunWith(MockitoJUnitRunner.class)
public class ClusterTopologyRefreshSchedulerTest {
private ClusterTopologyRefreshScheduler sut;
private ClusterTopologyRefreshOptions immediateRefresh = ClusterTopologyRefreshOptions.builder().enablePeriodicRefresh(1, TimeUnit.MILLISECONDS)
.enableAllAdaptiveRefreshTriggers().build();
private ClusterClientOptions clusterClientOptions = ClusterClientOptions.builder()
.topologyRefreshOptions(immediateRefresh).build();
@Mock
private ClientResources clientResources;
@Mock
private RedisClusterClient clusterClient;
@Mock
private EventExecutorGroup eventExecutors;
@Before
public void before() throws Exception {
when(clientResources.eventExecutorGroup()).thenReturn(eventExecutors);
sut = new ClusterTopologyRefreshScheduler(clusterClient, clientResources);
}
@Test
public void runShouldSubmitRefreshShouldTrigger() throws Exception {
when(clusterClient.getClusterClientOptions()).thenReturn(clusterClientOptions);
sut.run();
verify(eventExecutors).submit(any(Runnable.class));
}
@Test
public void runnableShouldCallPartitionRefresh() throws Exception {
when(clusterClient.getClusterClientOptions()).thenReturn(clusterClientOptions);
when(eventExecutors.submit(any(Runnable.class))).then(invocation -> {
((Runnable) invocation.getArguments()[0]).run();
return null;
});
sut.run();
verify(clusterClient).reloadPartitions();
}
@Test
public void shouldNotSubmitIfOptionsNotSet() throws Exception {
sut.run();
verify(eventExecutors, never()).submit(any(Runnable.class));
}
@Test
public void shouldNotSubmitIfExecutorIsShuttingDown() throws Exception {
when(clusterClient.getClusterClientOptions()).thenReturn(clusterClientOptions);
when(eventExecutors.isShuttingDown()).thenReturn(true);
sut.run();
verify(eventExecutors, never()).submit(any(Runnable.class));
}
@Test
public void shouldNotSubmitIfExecutorIsShutdown() throws Exception {
when(clusterClient.getClusterClientOptions()).thenReturn(clusterClientOptions);
when(eventExecutors.isShutdown()).thenReturn(true);
sut.run();
verify(eventExecutors, never()).submit(any(Runnable.class));
}
@Test
public void shouldNotSubmitIfExecutorIsTerminated() throws Exception {
when(clusterClient.getClusterClientOptions()).thenReturn(clusterClientOptions);
when(eventExecutors.isTerminated()).thenReturn(true);
sut.run();
verify(eventExecutors, never()).submit(any(Runnable.class));
}
@Test
public void shouldTriggerRefreshOnAskRedirection() throws Exception {
ClusterTopologyRefreshOptions clusterTopologyRefreshOptions = ClusterTopologyRefreshOptions.builder()
.enableAllAdaptiveRefreshTriggers().build();
ClusterClientOptions clusterClientOptions = ClusterClientOptions.builder()
.topologyRefreshOptions(clusterTopologyRefreshOptions).build();
when(clusterClient.getClusterClientOptions()).thenReturn(clusterClientOptions);
sut.onAskRedirection();
verify(eventExecutors).submit(any(Runnable.class));
}
@Test
public void shouldNotTriggerAdaptiveRefreshUsingDefaults() throws Exception {
ClusterTopologyRefreshOptions clusterTopologyRefreshOptions = ClusterTopologyRefreshOptions.create();
ClusterClientOptions clusterClientOptions = ClusterClientOptions.builder()
.topologyRefreshOptions(clusterTopologyRefreshOptions).build();
when(clusterClient.getClusterClientOptions()).thenReturn(clusterClientOptions);
sut.onAskRedirection();
verify(eventExecutors, never()).submit(any(Runnable.class));
}
@Test
public void shouldTriggerRefreshOnMovedRedirection() throws Exception {
ClusterClientOptions clusterClientOptions = ClusterClientOptions.builder().topologyRefreshOptions(immediateRefresh)
.build();
when(clusterClient.getClusterClientOptions()).thenReturn(clusterClientOptions);
sut.onMovedRedirection();
verify(eventExecutors).submit(any(Runnable.class));
}
@Test
public void shouldTriggerRefreshOnReconnect() throws Exception {
ClusterClientOptions clusterClientOptions = ClusterClientOptions.builder().topologyRefreshOptions(immediateRefresh)
.build();
when(clusterClient.getClusterClientOptions()).thenReturn(clusterClientOptions);
sut.onReconnection(10);
verify(eventExecutors).submit(any(Runnable.class));
}
@Test
public void shouldNotTriggerRefreshOnFirstReconnect() throws Exception {
ClusterClientOptions clusterClientOptions = ClusterClientOptions.builder().topologyRefreshOptions(immediateRefresh)
.build();
when(clusterClient.getClusterClientOptions()).thenReturn(clusterClientOptions);
sut.onReconnection(1);
verify(eventExecutors, never()).submit(any(Runnable.class));
}
@Test
public void shouldRateLimitAdaptiveRequests() throws Exception {
ClusterTopologyRefreshOptions adaptiveTimeout = ClusterTopologyRefreshOptions.builder().enablePeriodicRefresh(false)
.enableAllAdaptiveRefreshTriggers().adaptiveRefreshTriggersTimeout(50, TimeUnit.MILLISECONDS).build();
ClusterClientOptions clusterClientOptions = ClusterClientOptions.builder().topologyRefreshOptions(adaptiveTimeout)
.build();
when(clusterClient.getClusterClientOptions()).thenReturn(clusterClientOptions);
for (int i = 0; i < 10; i++) {
sut.onAskRedirection();
}
Thread.sleep(100);
sut.onAskRedirection();
verify(eventExecutors, times(2)).submit(any(Runnable.class));
}
}