use of com.linkedin.databus.core.BootstrapCheckpointHandler in project databus by linkedin.
the class TestNettyHttpDatabusBootstrapConnection method testServerBootstrapDisconnect.
@Test
public /**
* This is a unit test for DDSDBUS-3537. There is a lag between a network channel disconnect and the
* state change in AbstractNettyHttpConnection. This can cause a race condition in various requestXXX objects which
* check the state of the connection using the network channel. As a result, they may attempt to reconnect while
* AbstractNettyHttpConnection is still in CONNECTED state which causes an error for an incorrect transition
* CONNECTED -> CONNECTING.
*
* The test simulates the above condition by injecting a handler in the client pipeline which artificially holds up
* the channelClosed message. As a result we can inject a request while the netty channel is disconnected but the
* AbstractNettyHttpConnection object has not detected this yet.
*/
void testServerBootstrapDisconnect() throws Exception {
final Logger log = Logger.getLogger("TestNettyHttpDatabusBootstrapConnection.testServerBootstrapDisconnect");
log.info("starting");
log.info("setup the client");
TestingConnectionCallback callback = TestingConnectionCallback.createAndStart("testServerSourcesDisconnect");
DummyRemoteExceptionHandler remoteExceptionHandler = new DummyRemoteExceptionHandler();
final NettyHttpDatabusBootstrapConnection conn = (NettyHttpDatabusBootstrapConnection) CONN_FACTORY.createConnection(BOOTSTRAP_SERVER_INFO, callback, remoteExceptionHandler);
try {
log.info("initial setup");
final List<String> sourceNamesList = Arrays.asList(SOURCE1_NAME);
final Checkpoint cp = Checkpoint.createOnlineConsumptionCheckpoint(0);
BootstrapCheckpointHandler cpHandler = new BootstrapCheckpointHandler(sourceNamesList);
cpHandler.createInitialBootstrapCheckpoint(cp, 0L);
final DummyDatabusBootstrapConnectionStateMessage bstCallback = new DummyDatabusBootstrapConnectionStateMessage(log);
log.info("process a normal startSCN which should establish the connection");
sendStartScnHappyPath(conn, cp, bstCallback, SOURCE1_NAME, 100L, log);
Assert.assertTrue(conn.isConnected());
// wait for the response
TestUtil.assertWithBackoff(new ConditionCheck() {
@Override
public boolean check() {
return null != bstCallback.getCheckpoint();
}
}, "wait for /startSCN response", 100, log);
log.info("verify /startSCN response");
final Checkpoint startScnCp = bstCallback.getCheckpoint();
Assert.assertNotNull(startScnCp);
Assert.assertEquals(100L, startScnCp.getBootstrapStartScn().longValue());
log.info("instrument the client pipeline so that we can intercept and delay the channelClosed message");
final Semaphore passMessage = new Semaphore(1);
final CountDownLatch closeSent = new CountDownLatch(1);
passMessage.acquire();
conn._channel.getPipeline().addBefore("handler", "closeChannelDelay", new SimpleChannelHandler() {
@Override
public void channelClosed(ChannelHandlerContext ctx, ChannelStateEvent e) throws Exception {
closeSent.countDown();
passMessage.acquire();
try {
super.channelClosed(ctx, e);
} finally {
passMessage.release();
}
}
});
final Channel serverChannel = getServerChannelForClientConn(conn);
Thread asyncChannelClose = new Thread(new Runnable() {
@Override
public void run() {
log.info("closing server channel");
serverChannel.close();
log.info("server channel: closed");
closeSent.countDown();
}
}, "asyncChannelCloseThread");
asyncChannelClose.setDaemon(true);
Thread asyncBootstrapReq = new Thread(new Runnable() {
@Override
public void run() {
conn.requestStream("1", null, 10000, startScnCp, bstCallback);
}
}, "asyncBootstrapReqThread");
asyncBootstrapReq.setDaemon(true);
log.info("simultaneously closing connection and sending /bootstrap request");
bstCallback.reset();
asyncChannelClose.start();
Assert.assertTrue(closeSent.await(1000, TimeUnit.MILLISECONDS));
TestUtil.assertWithBackoff(new ConditionCheck() {
@Override
public boolean check() {
return !conn._channel.isConnected();
}
}, "waiting for disconnect on the client side", 1000, log);
Assert.assertEquals(AbstractNettyHttpConnection.State.CONNECTED, conn.getNetworkState());
log.info("asynchronously sending /bootstrap");
asyncBootstrapReq.start();
log.info("letting channelClose get through");
TestUtil.assertWithBackoff(new ConditionCheck() {
@Override
public boolean check() {
return bstCallback.isStreamRequestError();
}
}, "wait for streamRequestError callback", 1000, log);
passMessage.release();
log.info("finished");
} finally {
conn.close();
callback.shutdown();
log.info("cleaned");
}
}
use of com.linkedin.databus.core.BootstrapCheckpointHandler in project databus by linkedin.
the class CheckpointSerializerMain method updateCheckpoint.
private static Checkpoint updateCheckpoint(Checkpoint cpOld) throws JsonParseException, JsonMappingException, IOException {
Checkpoint cpNew = null != cpOld ? new Checkpoint(cpOld.toString()) : new Checkpoint();
if (null != _scn) {
if (-1L != _scn) {
cpNew.setWindowScn(_scn);
cpNew.setWindowOffset(0);
} else {
cpNew.setFlexible();
}
}
if (null != _startScn) {
cpNew.setBootstrapStartScn(_startScn);
}
if (null != _targetScn) {
cpNew.setBootstrapTargetScn(_targetScn);
}
if (null != _cpType) {
cpNew.setConsumptionMode(_cpType);
switch(_cpType) {
case ONLINE_CONSUMPTION:
cpNew.setWindowOffset(0);
break;
/*
* TODO Disabling as the bootstrap checkpoint creation leaves out important
* information (e.g. catchup/snashot source index) out of the checkpoint
* and thus is incorrect. We have to figure out what types of bootstrap
* checkpoints it makes sense to create.
case BOOTSTRAP_CATCHUP:
{
if (null != _bootstrapSource) cpNew.setCatchupSource(_bootstrapSource);
cpNew.setCatchupOffset(-1);
break;
}*/
case BOOTSTRAP_SNAPSHOT:
{
BootstrapCheckpointHandler handler = new BootstrapCheckpointHandler(_sources);
cpNew = handler.createInitialBootstrapCheckpoint(cpNew, _sinceScn);
// if (null != _bootstrapSource) cpNew.setSnapshotSource(_bootstrapSource);
cpNew.setSnapshotOffset(-1);
break;
}
default:
throw new DatabusRuntimeException("unsupported checkpoint type: " + _cpType);
}
}
return cpNew;
}
use of com.linkedin.databus.core.BootstrapCheckpointHandler in project databus by linkedin.
the class BootstrapPullThread method determineNextStateFromCheckpoint.
/**
* Determines the next state based on the checkpoint. The idea is to determine where we are in the bootstrap flow and
* move to the next state.
*
* <pre>
* 1. Request startSCN (State=REQUEST_START_SCN, SNAPSHOT, !cp.isBootstrapStartScnSet())
* 2. For each snapshot source:
* 2.1. Start snapshot (State=REQUEST_STREAM, SNAPSHOT, cp.isBootstrapStartScnSet() &&
* 0 == cp.getSnapshotOffset())
* 2.2. While (! cp.isSnapShotSourceCompleted())
* 2.2.1. Continue snapshot (State=REQUEST_STREAM, SNAPSHOT, cp.isBootstrapStartScnSet() &&
* 0 < cp.getSnapshotOffset())
* 2.3. Request targetSCN (State=REQUEST_TARGET_SCN, SNAPSHOT, cp.isBootstrapStartScnSet() &&
* cp.isSnapShotSourceCompleted() && 0 == cp.getWindowOffset())
* 2.4. For each catchup source <= the snapshot source:
* 2.4.1. Start catchup (State=REQUEST_STREAM, CATCHUP, 0==cp.getWindowOffset() && handler.needsMoreCatchup())
* 2.4.2. While (! cp.isCatchupSourceCompleted())
* 2.4.2.1. Continue catchup (State=REQUEST_STREAM, CATCHUP, ! cp.isCatchupSourceCompleted())
* </pre>
* @param curState the bootstrap checkpoint
*/
private void determineNextStateFromCheckpoint(ConnectionState curState) {
try {
final Checkpoint cp = curState.getCheckpoint();
final BootstrapCheckpointHandler cpHandler = curState.getBstCheckpointHandler();
cpHandler.assertBootstrapCheckpoint(cp);
switch(cp.getConsumptionMode()) {
case BOOTSTRAP_SNAPSHOT:
determineNextStateFromSnapshotCheckpoint(cp, cpHandler, curState);
break;
case BOOTSTRAP_CATCHUP:
determineNextStateFromCatchupCheckpoint(cp, cpHandler, curState);
break;
default:
_log.fatal("unexpected bootstrap checkpoint type: " + cp + "; shutting down");
curState.switchToClosed();
}
} catch (InvalidCheckpointException e) {
_log.fatal("invalid bootstrap checkpoint:", e);
curState.switchToClosed();
}
}
use of com.linkedin.databus.core.BootstrapCheckpointHandler in project databus by linkedin.
the class BootstrapPullThread method doReadBootstrapEvents.
protected void doReadBootstrapEvents(ConnectionState curState) {
boolean success = true;
boolean debugEnabled = _log.isDebugEnabled();
boolean enqueueMessage = true;
try {
Checkpoint cp = curState.getCheckpoint();
DbusEventBuffer eventBuffer = curState.getDataEventsBuffer();
if (debugEnabled)
_log.debug("Sending bootstrap events to buffer");
// eventBuffer.startEvents();
DbusEventInternalReadable cpEvent = getEventFactory().createCheckpointEvent(cp);
byte[] cpEventBytes = new byte[cpEvent.size()];
if (debugEnabled) {
_log.debug("checkpoint event size: " + cpEventBytes.length);
_log.debug("checkpoint event:" + cpEvent.toString());
}
cpEvent.getRawBytes().get(cpEventBytes);
ByteArrayInputStream cpIs = new ByteArrayInputStream(cpEventBytes);
ReadableByteChannel cpRbc = Channels.newChannel(cpIs);
UnifiedClientStats unifiedClientStats = _sourcesConn.getUnifiedClientStats();
sendHeartbeat(unifiedClientStats);
int ecnt = eventBuffer.readEvents(cpRbc);
success = (ecnt > 0);
if (!success) {
_log.error("Unable to write bootstrap phase marker");
} else {
ChunkedBodyReadableByteChannel readChannel = curState.getReadChannel();
String remoteErrorName = RemoteExceptionHandler.getExceptionName(readChannel);
Throwable remoteError = _remoteExceptionHandler.getException(readChannel);
if (null != remoteError && remoteError instanceof BootstrapDatabaseTooOldException) {
_log.error("Bootstrap database is too old!");
_remoteExceptionHandler.handleException(remoteError);
curState.switchToStreamResponseError();
} else if (null != remoteErrorName) {
// remote processing error
_log.error("read events error: " + RemoteExceptionHandler.getExceptionMessage(readChannel));
curState.switchToStreamResponseError();
} else {
sendHeartbeat(unifiedClientStats);
int eventsNum = eventBuffer.readEvents(readChannel, curState.getListeners(), _sourcesConn.getBootstrapEventsStatsCollector());
if (eventsNum == 0 && _remoteExceptionHandler.getPendingEventSize(readChannel) > eventBuffer.getMaxReadBufferCapacity()) {
String err = "ReadBuffer max capacity(" + eventBuffer.getMaxReadBufferCapacity() + ") is less than event size(" + _remoteExceptionHandler.getPendingEventSize(readChannel) + "). Increase databus.client.connectionDefaults.bstEventBuffer.maxEventSize and restart.";
_log.fatal(err);
enqueueMessage(LifecycleMessage.createSuspendOnErroMessage(new PendingEventTooLargeException(err)));
return;
} else {
resetServerRetries();
if (debugEnabled)
_log.debug("Sending events to buffer");
numEventsInCurrentState += eventsNum;
_log.info("Bootstrap events read so far: " + numEventsInCurrentState);
String status = readChannel.getMetadata("PhaseCompleted");
final BootstrapCheckpointHandler ckptHandler = curState.getBstCheckpointHandler();
if (status != null) {
// set status in checkpoint to indicate that we are done with the current source
if (cp.getConsumptionMode() == DbusClientMode.BOOTSTRAP_CATCHUP) {
ckptHandler.finalizeCatchupPhase(cp);
} else if (cp.getConsumptionMode() == DbusClientMode.BOOTSTRAP_SNAPSHOT) {
ckptHandler.finalizeSnapshotPhase(cp);
} else {
throw new RuntimeException("Invalid bootstrap phase: " + cp.getConsumptionMode());
}
_log.info("Bootstrap events read :" + numEventsInCurrentState + " during phase:" + cp.getConsumptionMode() + " [" + cp.getBootstrapSnapshotSourceIndex() + "," + cp.getBootstrapCatchupSourceIndex() + "]");
numEventsInCurrentState = 0;
} else {
// question: how is snapshotOffset maintained in ckpt
if (eventsNum > 0) {
cp.bootstrapCheckPoint();
}
}
curState.switchToStreamResponseDone();
}
}
}
} catch (InterruptedException ie) {
_log.error("interupted", ie);
success = false;
} catch (InvalidEventException e) {
_log.error("error reading events from server: " + e.getMessage(), e);
success = false;
} catch (RuntimeException e) {
_log.error("runtime error reading events from server: " + e.getMessage(), e);
success = false;
}
if (toTearConnAfterHandlingResponse()) {
tearConnectionAndEnqueuePickServer();
enqueueMessage = false;
} else if (!success) {
curState.switchToPickServer();
}
if (enqueueMessage)
enqueueMessage(curState);
}
use of com.linkedin.databus.core.BootstrapCheckpointHandler in project databus by linkedin.
the class TestBootstrap method testBootstrapProcessor.
@Test
public void testBootstrapProcessor() throws InstantiationException, IllegalAccessException, ClassNotFoundException, SQLException, IOException, BootstrapProcessingException, DatabusException, BootstrapDatabaseTooOldException, BootstrapDBException {
EventProcessor processorCallback = new EventProcessor();
BootstrapConfig config = new BootstrapConfig();
BootstrapReadOnlyConfig staticConfig = config.build();
BootstrapServerConfig configBuilder = new BootstrapServerConfig();
configBuilder.setEnableMinScnCheck(false);
BootstrapServerStaticConfig staticServerConfig = configBuilder.build();
BootstrapProcessor processor = new BootstrapProcessor(staticServerConfig, null);
String sourceName = "TestBootstrap.testBootstrapProcessor.events";
// Create the tables for all the sources before starting up the threads
BootstrapConn _bootstrapConn = new BootstrapConn();
boolean autoCommit = true;
_bootstrapConn.initBootstrapConn(autoCommit, staticConfig.getBootstrapDBUsername(), staticConfig.getBootstrapDBPassword(), staticConfig.getBootstrapDBHostname(), staticConfig.getBootstrapDBName());
BootstrapDBMetaDataDAO dao = new BootstrapDBMetaDataDAO(_bootstrapConn, staticConfig.getBootstrapDBHostname(), staticConfig.getBootstrapDBUsername(), staticConfig.getBootstrapDBPassword(), staticConfig.getBootstrapDBName(), autoCommit);
SourceStatusInfo srcStatusInfo = dao.getSrcIdStatusFromDB(sourceName, false);
if (srcStatusInfo.getSrcId() >= 0) {
dao.dropSourceInDB(srcStatusInfo.getSrcId());
}
dao.addNewSourceInDB(sourceName, BootstrapProducerStatus.ACTIVE);
srcStatusInfo = dao.getSrcIdStatusFromDB(sourceName, false);
setBootstrapLoginfoTable(_bootstrapConn, 0, Long.MAX_VALUE);
// insert one row
insertOneSnapshotEvent(dao, srcStatusInfo.getSrcId(), Long.MAX_VALUE - 10, Long.MAX_VALUE - 100, "check", "test_payload_data");
BootstrapCheckpointHandler bstCheckpointHandler = new BootstrapCheckpointHandler(sourceName);
Checkpoint c = bstCheckpointHandler.createInitialBootstrapCheckpoint(null, 0L);
c.setConsumptionMode(DbusClientMode.BOOTSTRAP_SNAPSHOT);
c.setBootstrapStartScn(Long.MAX_VALUE - 10);
c.setSnapshotOffset(Long.MAX_VALUE - 1000);
// System.out.println("Snapshot");
processor.streamSnapShotRows(c, processorCallback);
Assert.assertEquals(processorCallback.getrIds().size(), 1);
Assert.assertEquals(Long.MAX_VALUE - 10, processorCallback.getrIds().get(0).longValue());
Assert.assertEquals(Long.MAX_VALUE - 100, processorCallback.getSequences().get(0).longValue());
Assert.assertEquals("check", processorCallback.getSrcKeys().get(0));
Assert.assertEquals("test_payload_data", new String(processorCallback.getValues().get(0)));
processorCallback.reset();
c.setSnapshotOffset(Checkpoint.FULLY_CONSUMED_WINDOW_OFFSET);
c.setBootstrapTargetScn(c.getBootstrapStartScn().longValue());
bstCheckpointHandler.advanceAfterTargetScn(c);
bstCheckpointHandler.advanceAfterSnapshotPhase(c);
// c.setConsumptionMode(DbusClientMode.BOOTSTRAP_CATCHUP);
// c.setCatchupSource(sourceName);
// c.setWindowScn(0L);
// c.setWindowOffset(0);
// System.out.println("Catchup");
boolean phaseCompleted = processor.streamCatchupRows(c, processorCallback);
Assert.assertEquals(true, phaseCompleted);
}
Aggregations