use of com.linkedin.databus2.test.ConditionCheck in project databus by linkedin.
the class MockRemoteExceptionHandler method testShutdownRace.
@Test
public /**
* DDSDBUS-1904: test a race condition between shutting down a connection and a reconnect to the
* relay which may cause a Netty connection to be left open after the RelayPuller shutdown.
* The idea is to trigger a shutdown exactly while we are trying to connect to a relay. We achieve
* that by rigging MockRelayConnection to call RelayPuller.shutdown() right before responding
* to requestSources();
* */
void testShutdownRace() throws Exception {
final Logger log = Logger.getLogger("TestRelayPullThread.testShutdownRace");
log.setLevel(Level.INFO);
log.info("start");
//elaborate test setup
List<String> sources = Arrays.asList("source1");
Properties clientProps = new Properties();
clientProps.setProperty("client.container.httpPort", "0");
clientProps.setProperty("client.runtime.bootstrap.enabled", "false");
clientProps.setProperty("client.runtime.relay(1).name", "relay1");
clientProps.setProperty("client.runtime.relay(1).port", "10001");
clientProps.setProperty("client.runtime.relay(1).sources", "source1");
clientProps.setProperty("client.connectionDefaults.eventBuffer.maxSize", "100000");
clientProps.setProperty("client.connectionDefaults.pullerRetries.maxRetryNum", "9");
clientProps.setProperty("client.connectionDefaults.pullerRetries.sleepIncFactor", "1.0");
clientProps.setProperty("client.connectionDefaults.pullerRetries.sleepIncDelta", "1");
clientProps.setProperty("client.connectionDefaults.pullerRetries.initSleep", "1");
DatabusHttpClientImpl client = new DatabusHttpClientImpl("client.", clientProps);
Assert.assertNotNull(client, "client instantiation ok");
final DatabusHttpClientImpl.StaticConfig clientConf = client.getClientStaticConfig();
final DatabusSourcesConnection.StaticConfig srcConnConf = clientConf.getConnectionDefaults();
DatabusHttpClientImpl.RuntimeConfig clientRtConf = clientConf.getRuntime().build();
DbusEventBuffer.StaticConfig bufferConf = clientConf.getConnectionDefaults().getEventBuffer();
DbusEventBuffer relayBuffer = new DbusEventBuffer(bufferConf);
DbusEventBuffer bootstrapBuffer = new DbusEventBuffer(bufferConf);
//we keep the index of the next server we expect to see
AtomicInteger serverIdx = new AtomicInteger(-1);
Set<ServerInfo> relays = clientRtConf.getRelaysSet();
//generate the order in which we should see the servers
List<ServerInfo> relayOrder = new ArrayList<ServerInfo>(relays);
if (LOG.isInfoEnabled()) {
StringBuilder sb = new StringBuilder();
for (ServerInfo serverInfo : relayOrder) {
sb.append(serverInfo.getName());
sb.append(" ");
}
LOG.info("Relay order:" + sb.toString());
}
List<IdNamePair> sourcesResponse = new ArrayList<IdNamePair>();
sourcesResponse.add(new IdNamePair(1L, "source1"));
RegisterResponseEntry rre1 = new RegisterResponseEntry(1L, (short) 1, SCHEMA$.toString());
final HashMap<Long, List<RegisterResponseEntry>> registerResponse = new HashMap<Long, List<RegisterResponseEntry>>();
registerResponse.put(1L, Arrays.asList(rre1));
//This guy succeeds on both /sources and /register
final MockRelayConnection relayConn1 = new MockRelayConnection(sourcesResponse, registerResponse, null, serverIdx);
//This guy will succeed on /sources but will force a shutdown so that
//the success message is ignored
final MockRelayConnection relayConn2 = new MockRelayConnectionForTestShutdownRace(sourcesResponse, null, null, serverIdx, log);
DatabusRelayConnectionFactory mockConnFactory = EasyMock.createMock("mockRelayFactory", DatabusRelayConnectionFactory.class);
// expected scenario: create relayConn1 -> /sources success -> /register success ->
// /stream error -> create relayConn2 -> call /sources -> shut down puller -> return
// success for /sources
EasyMock.expect(mockConnFactory.createRelayConnection(serverNameMatcher(serverIdx, relayOrder), EasyMock.<ActorMessageQueue>notNull(), EasyMock.<RemoteExceptionHandler>notNull())).andReturn(relayConn1);
EasyMock.expect(mockConnFactory.createRelayConnection(serverNameMatcher(serverIdx, relayOrder), EasyMock.<ActorMessageQueue>notNull(), EasyMock.<RemoteExceptionHandler>notNull())).andReturn(relayConn2);
EasyMock.replay(mockConnFactory);
List<DatabusSubscription> sourcesSubList = DatabusSubscription.createSubscriptionList(sources);
//Dummy connection object as expected by the puller thread
// Note that in this case, it is ok to pass Set<relays> as all the relays serve the same source "source1"
ConnectionStateFactory connStateFactory = new ConnectionStateFactory(sources);
DatabusSourcesConnection sourcesConn = new DatabusSourcesConnection(srcConnConf, sourcesSubList, relays, null, null, null, relayBuffer, bootstrapBuffer, Executors.newCachedThreadPool(), null, null, null, null, null, null, null, mockConnFactory, null, null, null, null, new DbusEventV1Factory(), connStateFactory);
final RelayPullThread relayPuller = new RelayPullThread("RelayPuller", sourcesConn, relayBuffer, connStateFactory, relays, new ArrayList<DbusKeyCompositeFilterConfig>(), !clientConf.getRuntime().getBootstrap().isEnabled(), clientConf.isReadLatestScnOnErrorEnabled(), clientConf.getPullerBufferUtilizationPct(), Integer.MAX_VALUE, ManagementFactory.getPlatformMBeanServer(), new DbusEventV1Factory(), null);
//relayPuller.getLog().setLevel(Level.INFO);
RemoteExceptionHandler mockRemoteExceptionHandler = new MockRemoteExceptionHandler(sourcesConn, relayBuffer, relayPuller);
Field field = relayPuller.getClass().getDeclaredField("_remoteExceptionHandler");
field.setAccessible(true);
field.set(relayPuller, mockRemoteExceptionHandler);
relayConn1.setCallback(relayPuller);
relayConn2.setCallback(relayPuller);
//Let the show begin
final Thread relayPullerThread = new Thread(relayPuller, "testShutdownRace.RelayPuller");
relayPullerThread.setDaemon(true);
relayPullerThread.start();
relayPuller.enqueueMessage(LifecycleMessage.createStartMessage());
//wait for the puller to go the STREAM_REQUEST_SUCCESS state
TestUtil.assertWithBackoff(new ConditionCheck() {
@Override
public boolean check() {
return null != relayConn1.getLastStateMsg();
}
}, "wait for call from the puller to the relay connection", 500, log);
//wait for puller thread to shutdown
TestUtil.assertWithBackoff(new ConditionCheck() {
@Override
public boolean check() {
log.debug(relayPuller.getMessageHistoryLog());
return !relayPullerThread.isAlive();
}
}, "wait for puller to shutdown", 1000, log);
EasyMock.verify(mockConnFactory);
Assert.assertEquals(relayPuller.getLastOpenConnection(), null);
log.info("done");
}
use of com.linkedin.databus2.test.ConditionCheck in project databus by linkedin.
the class TestHttpResponseProcessor method testHappyPathNoChunking.
@Test
public void testHappyPathNoChunking() throws DatabusException {
Logger log = Logger.getLogger("GenericHttpResponseHandler.testHappyPathNoChunking");
final GenericHttpResponseHandler responseHandler = new GenericHttpResponseHandler(KeepAliveType.KEEP_ALIVE);
responseHandler.getLog().setLevel(_logLevel);
TestHttpResponseProcessor respProcessor = new TestHttpResponseProcessor(log);
TestConnectListener connectListener = new TestConnectListener(log);
TestSendRequestListener requestListener = new TestSendRequestListener(log);
TestCloseListener closeListener = new TestCloseListener(log);
responseHandler.setConnectionListener(connectListener);
Channel channel = createClientBootstrap(responseHandler);
SocketAddress clientAddr = channel.getLocalAddress();
try {
setListeners(responseHandler, respProcessor, requestListener, closeListener);
channel.write(new DefaultHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.GET, "/test"));
//It seems that there is a race condition between the writeFuture succeeding
//and the writeComplete message getting to the handler. Make sure that the
//writeComplete has got to the handler before we do anything else with
//the channel.
final GenericHttpResponseHandler handler = getResponseHandler(channel);
TestUtil.assertWithBackoff(new ConditionCheck() {
@Override
public boolean check() {
return handler._messageState.hasSentRequest();
}
}, "request sent", 1000, log);
HttpResponse resp = new DefaultHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK);
resp.setContent(null);
resp.setHeader(HttpHeaders.Names.CONTENT_LENGTH, 0);
sendServerResponse(clientAddr, resp, 1000);
final List<String> callbacks = respProcessor.getCallbacks();
TestUtil.assertWithBackoff(new ConditionCheck() {
@Override
public boolean check() {
return 2 == callbacks.size();
}
}, "waiting for response processed", 1000, null);
final List<String> connectCallbacks = connectListener.getCallbacks();
final List<String> requestCallbacks = requestListener.getCallbacks();
final List<String> closeCallbacks = closeListener.getCallbacks();
stateSanityCheck(connectCallbacks, requestCallbacks, callbacks, closeCallbacks);
Assert.assertEquals(callbacks.get(0), "startResponse");
Assert.assertEquals(callbacks.get(1), "finishResponse");
} finally {
channel.close();
}
}
use of com.linkedin.databus2.test.ConditionCheck in project databus by linkedin.
the class TestHttpResponseProcessor method testHappyPathChunking.
@Test
public void testHappyPathChunking() throws DatabusException {
Logger log = Logger.getLogger("GenericHttpResponseHandler.testHappyPathChunking");
final GenericHttpResponseHandler responseHandler = new GenericHttpResponseHandler(KeepAliveType.KEEP_ALIVE);
responseHandler.getLog().setLevel(_logLevel);
TestHttpResponseProcessor respProcessor = new TestHttpResponseProcessor(log);
TestConnectListener connectListener = new TestConnectListener(log);
TestSendRequestListener requestListener = new TestSendRequestListener(log);
TestCloseListener closeListener = new TestCloseListener(log);
responseHandler.setConnectionListener(connectListener);
Channel channel = createClientBootstrap(responseHandler);
SocketAddress clientAddr = channel.getLocalAddress();
try {
setListeners(responseHandler, respProcessor, requestListener, closeListener);
channel.write(new DefaultHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.GET, "/test"));
//It seems that there is a race condition between the writeFuture succeeding
//and the writeComplete message getting to the handler. Make sure that the
//writeComplete has got to the handler before we do anything else with
//the channel.
final GenericHttpResponseHandler handler = getResponseHandler(channel);
TestUtil.assertWithBackoff(new ConditionCheck() {
@Override
public boolean check() {
return handler._messageState.hasSentRequest();
}
}, "request sent", 1000, log);
HttpResponse resp = new DefaultHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK);
resp.setHeader(HttpHeaders.Names.TRANSFER_ENCODING, HttpHeaders.Values.CHUNKED);
sendServerResponse(clientAddr, resp, 1000);
HttpChunk chunk1 = new DefaultHttpChunk(ChannelBuffers.wrappedBuffer("chunk1".getBytes(Charset.defaultCharset())));
sendServerResponse(clientAddr, chunk1, 1000);
HttpChunk chunk2 = new DefaultHttpChunk(ChannelBuffers.wrappedBuffer("chunk2".getBytes(Charset.defaultCharset())));
sendServerResponse(clientAddr, chunk2, 1000);
sendServerResponse(clientAddr, new DefaultHttpChunkTrailer(), 1000);
final List<String> callbacks = respProcessor.getCallbacks();
TestUtil.assertWithBackoff(new ConditionCheck() {
@Override
public boolean check() {
return callbacks.size() == 5;
}
}, "waiting for response processed", 1000, null);
final List<String> connectCallbacks = connectListener.getCallbacks();
final List<String> requestCallbacks = requestListener.getCallbacks();
final List<String> closeCallbacks = closeListener.getCallbacks();
stateSanityCheck(connectCallbacks, requestCallbacks, callbacks, closeCallbacks);
Assert.assertEquals(callbacks.get(0), "startResponse");
Assert.assertEquals(callbacks.get(1), "addChunk");
Assert.assertEquals(callbacks.get(2), "addChunk");
Assert.assertEquals(callbacks.get(3), "addTrailer");
Assert.assertEquals(callbacks.get(4), "finishResponse");
//make sure that no new callbacks have showed up
Assert.assertEquals(callbacks.size(), 5);
} finally {
channel.close();
}
}
use of com.linkedin.databus2.test.ConditionCheck in project databus by linkedin.
the class TestHttpResponseProcessor method testErrorServerResponseChunking.
@Test
public void testErrorServerResponseChunking() throws DatabusException {
Logger log = Logger.getLogger("GenericHttpResponseHandler.testErrorServerResponseChunking");
final GenericHttpResponseHandler responseHandler = new GenericHttpResponseHandler(KeepAliveType.KEEP_ALIVE);
responseHandler.getLog().setLevel(_logLevel);
TestHttpResponseProcessor respProcessor = new TestHttpResponseProcessor(log);
TestConnectListener connectListener = new TestConnectListener(log);
TestSendRequestListener requestListener = new TestSendRequestListener(log);
TestCloseListener closeListener = new TestCloseListener(log);
responseHandler.setConnectionListener(connectListener);
Channel channel = createClientBootstrap(responseHandler);
SocketAddress clientAddr = channel.getLocalAddress();
try {
setListeners(responseHandler, respProcessor, requestListener, closeListener);
channel.write(new DefaultHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.GET, "/test"));
//It seems that there is a race condition between the writeFuture succeeding
//and the writeComplete message getting to the handler. Make sure that the
//writeComplete has got to the handler before we do anything else with
//the channel.
final GenericHttpResponseHandler handler = getResponseHandler(channel);
TestUtil.assertWithBackoff(new ConditionCheck() {
@Override
public boolean check() {
return handler._messageState.hasSentRequest();
}
}, "request sent", 1000, log);
HttpResponse resp = new DefaultHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.SERVICE_UNAVAILABLE);
resp.setHeader(HttpHeaders.Names.TRANSFER_ENCODING, HttpHeaders.Values.CHUNKED);
sendServerResponse(clientAddr, resp, 2000);
HttpChunk chunk1 = new DefaultHttpChunk(ChannelBuffers.wrappedBuffer("chunk1".getBytes(Charset.defaultCharset())));
sendServerResponse(clientAddr, chunk1, 1000);
sendServerResponse(clientAddr, new DefaultHttpChunkTrailer(), 1000);
final List<String> callbacks = respProcessor.getCallbacks();
TestUtil.assertWithBackoff(new ConditionCheck() {
@Override
public boolean check() {
return 4 == callbacks.size();
}
}, "waiting for response processed", 1000, null);
final List<String> connectCallbacks = connectListener.getCallbacks();
final List<String> requestCallbacks = requestListener.getCallbacks();
final List<String> closeCallbacks = closeListener.getCallbacks();
stateSanityCheck(connectCallbacks, requestCallbacks, callbacks, closeCallbacks);
Assert.assertEquals(callbacks.get(0), "startResponse");
Assert.assertEquals(callbacks.get(1), "addChunk");
Assert.assertEquals(callbacks.get(2), "addTrailer");
Assert.assertEquals(callbacks.get(3), "finishResponse");
TestUtil.sleep(500);
//make sure that no new callbacks have showed up
Assert.assertEquals(callbacks.size(), 4);
} finally {
channel.close();
}
}
use of com.linkedin.databus2.test.ConditionCheck in project databus by linkedin.
the class TestHttpResponseProcessor method testEarlyServerCloseChunking.
@Test
public void testEarlyServerCloseChunking() throws DatabusException {
Logger log = Logger.getLogger("GenericHttpResponseHandler.testEarlyServerCloseChunking");
final GenericHttpResponseHandler responseHandler = new GenericHttpResponseHandler(KeepAliveType.KEEP_ALIVE);
responseHandler.getLog().setLevel(_logLevel);
TestHttpResponseProcessor respProcessor = new TestHttpResponseProcessor(log);
TestConnectListener connectListener = new TestConnectListener(log);
TestSendRequestListener requestListener = new TestSendRequestListener(log);
TestCloseListener closeListener = new TestCloseListener(log);
responseHandler.setConnectionListener(connectListener);
Channel channel = createClientBootstrap(responseHandler);
Assert.assertTrue(channel.isConnected());
final SocketAddress clientAddr = channel.getLocalAddress();
try {
setListeners(responseHandler, respProcessor, requestListener, closeListener);
channel.write(new DefaultHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.GET, "/test"));
//It seems that there is a race condition between the writeFuture succeeding
//and the writeComplete message getting to the handler. Make sure that the
//writeComplete has got to the handler before we do anything else with
//the channel.
final GenericHttpResponseHandler handler = getResponseHandler(channel);
TestUtil.assertWithBackoff(new ConditionCheck() {
@Override
public boolean check() {
return handler._messageState.hasSentRequest();
}
}, "request sent", 1000, log);
HttpResponse resp = new DefaultHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK);
resp.setHeader(HttpHeaders.Names.TRANSFER_ENCODING, HttpHeaders.Values.CHUNKED);
TestUtil.assertWithBackoff(new ConditionCheck() {
@Override
public boolean check() {
return null != _dummyServer.getChildChannel(clientAddr);
}
}, "make sure we have all tracking populated for client connection", 1000, log);
sendServerResponse(clientAddr, resp, 1000);
HttpChunk chunk1 = new DefaultHttpChunk(ChannelBuffers.wrappedBuffer("chunk1".getBytes(Charset.defaultCharset())));
sendServerResponse(clientAddr, chunk1, 1000);
TestUtil.sleep(200);
sendServerClose(clientAddr, -1);
final List<String> callbacks = respProcessor.getCallbacks();
final List<String> closeCallbacks = closeListener.getCallbacks();
TestUtil.assertWithBackoff(new ConditionCheck() {
@Override
public boolean check() {
return 3 == callbacks.size() && 1 == closeCallbacks.size();
}
}, "waiting for response processed", 1000, null);
final List<String> connectCallbacks = connectListener.getCallbacks();
final List<String> requestCallbacks = requestListener.getCallbacks();
stateSanityCheck(connectCallbacks, requestCallbacks, callbacks, closeCallbacks);
Assert.assertEquals(callbacks.get(0), "startResponse");
Assert.assertEquals(callbacks.get(1), "addChunk");
// we get Exception, no ChannelClose
Assert.assertTrue(callbacks.get(2).startsWith("channelException"));
//make sure that no new callbacks have showed up
Assert.assertEquals(callbacks.size(), 3);
} finally {
channel.close();
}
}
Aggregations