/*
* Copyright (C) 2012-2015 DataStax Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.datastax.driver.core;
import org.testng.annotations.Test;
import java.util.Collection;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.CyclicBarrier;
import java.util.concurrent.TimeUnit;
import static com.datastax.driver.core.CreateCCM.TestMode.PER_METHOD;
import static com.datastax.driver.core.TestUtils.ipOfNode;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Fail.fail;
import static org.mockito.Mockito.*;
@CreateCCM(PER_METHOD)
public class EventDebouncerIntegrationTest extends CCMTestsSupport {
/**
* Tests that DOWN, UP, REMOVE or ADD events will not be delivered to
* load balancing policy nor host state listeners
* before the cluster is fully initialized.
*
* @throws InterruptedException
* @jira_ticket JAVA-784
* @since 2.0.11
*/
@CCMConfig(numberOfNodes = 3, createCluster = false, dirtiesContext = true)
@Test(groups = "long")
public void should_wait_until_load_balancing_policy_is_fully_initialized() throws InterruptedException {
TestLoadBalancingPolicy policy = new TestLoadBalancingPolicy();
final Cluster cluster = register(createClusterBuilderNoDebouncing()
.addContactPoints(getContactPoints().get(0))
.withPort(ccm().getBinaryPort())
.withLoadBalancingPolicy(policy).build());
new Thread() {
@Override
public void run() {
cluster.init();
}
}.start();
// stop cluster initialization in the middle of LBP initialization
policy.stop();
// generate a DOWN event - will not be delivered immediately
// because the debouncers are not started
// note: a graceful stop notify other nodes which send a topology change event to the driver right away
// while forceStop kills the node so other nodes take much more time to detect node failure
ccm().stop(3);
ccm().waitForDown(3);
// finish cluster initialization and deliver the DOWN event
policy.proceed();
assertThat(policy.onDownCalledBeforeInit).isFalse();
assertThat(policy.onDownCalled()).isTrue();
assertThat(policy.hosts).doesNotContain(TestUtils.findHost(cluster, 3));
}
/**
* Tests that settings for a debouncer can be modified dynamically
* without requiring the cluster to be restarted.
*
* @throws InterruptedException
* @jira_ticket JAVA-1192
*/
@CCMConfig(numberOfNodes = 1)
@Test(groups = "short")
public void should_change_debouncer_settings_dynamically() throws InterruptedException {
// Create a spy of the Cluster's control connection and replace it with the spy.
ControlConnection controlConnection = spy(cluster().manager.controlConnection);
cluster().manager.controlConnection = controlConnection;
for (int i = 0; i < 10; i++) {
cluster().manager.submitNodeListRefresh();
Thread.sleep(100);
}
// all requests should be coalesced into a single one
verify(controlConnection, timeout(10000)).refreshNodeListAndTokenMap();
reset(controlConnection);
// disable debouncing
cluster().getConfiguration().getQueryOptions()
.setRefreshNodeListIntervalMillis(0);
for (int i = 0; i < 10; i++) {
cluster().manager.submitNodeListRefresh();
Thread.sleep(100);
}
// each request should have been handled separately
verify(controlConnection, timeout(10000).times(10)).refreshNodeListAndTokenMap();
}
private class TestLoadBalancingPolicy extends SortingLoadBalancingPolicy {
CyclicBarrier stop = new CyclicBarrier(2);
CyclicBarrier proceed = new CyclicBarrier(2);
CountDownLatch onDownCalled = new CountDownLatch(1);
volatile boolean init = false;
volatile boolean onDownCalledBeforeInit = false;
@Override
public void init(Cluster cluster, Collection<Host> hosts) {
try {
stop.await(1, TimeUnit.MINUTES);
proceed.await(1, TimeUnit.MINUTES);
} catch (Exception e) {
fail(e.getMessage());
}
super.init(cluster, hosts);
init = true;
}
@Override
public void onDown(Host host) {
if (!init)
onDownCalledBeforeInit = true;
super.onDown(host);
if (host.getAddress().toString().contains(ipOfNode(3)))
onDownCalled.countDown();
}
void stop() throws InterruptedException {
try {
stop.await(1, TimeUnit.MINUTES);
} catch (Exception e) {
fail(e.getMessage());
}
}
void proceed() throws InterruptedException {
try {
proceed.await(1, TimeUnit.MINUTES);
} catch (Exception e) {
fail(e.getMessage());
}
}
boolean onDownCalled() throws InterruptedException {
return onDownCalled.await(1, TimeUnit.MINUTES);
}
}
}