package com.zendesk.maxwell.schema;
import static org.hamcrest.CoreMatchers.containsString;
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.CoreMatchers.not;
import static org.hamcrest.CoreMatchers.nullValue;
import static org.junit.Assert.*;
import com.zendesk.maxwell.*;
import java.sql.ResultSet;
import java.util.List;
import com.zendesk.maxwell.recovery.RecoveryInfo;
import com.zendesk.maxwell.replication.BinlogPosition;
import com.zendesk.maxwell.errors.DuplicateProcessException;
import com.zendesk.maxwell.replication.Position;
import org.apache.commons.lang.StringUtils;
import org.junit.Test;
public class MysqlPositionStoreTest extends MaxwellTestWithIsolatedServer {
private MysqlPositionStore buildStore() throws Exception {
return buildStore(buildContext());
}
private MysqlPositionStore buildStore(MaxwellContext context) throws Exception {
return buildStore(context, context.getServerID());
}
private MysqlPositionStore buildStore(MaxwellContext context, Long serverID) throws Exception {
return new MysqlPositionStore(context.getMaxwellConnectionPool(), serverID, "maxwell", MaxwellTestSupport.inGtidMode());
}
@Test
public void testSetBinlogPosition() throws Exception {
MysqlPositionStore store = buildStore();
long lastHeartbeatRead = 100L;
BinlogPosition binlogPosition;
if (MaxwellTestSupport.inGtidMode()) {
String gtid = "123:1-100";
binlogPosition = new BinlogPosition(gtid, null, 12345, "foo");
} else {
binlogPosition = new BinlogPosition(12345, "foo");
}
Position position = new Position(binlogPosition, 100L);
store.set(position);
assertThat(buildStore().get(), is(position));
}
@Test
public void testHeartbeat() throws Exception {
MysqlPositionStore store = buildStore();
store.set(new Position(new BinlogPosition(12345, "foo"), 0L));
Long preHeartbeat = System.currentTimeMillis();
store.heartbeat();
ResultSet rs = server.getConnection().createStatement().executeQuery("select * from maxwell.heartbeats");
rs.next();
assertThat(rs.getLong("heartbeat") >= preHeartbeat, is(true));
}
@Test
public void testHeartbeatDuplicate() throws Exception {
MysqlPositionStore store = buildStore();
store.set(new Position(new BinlogPosition(12345, "foo"), 0L));
store.heartbeat();
buildStore().heartbeat();
Exception exception = null;
try {
store.heartbeat();
} catch (DuplicateProcessException d) {
exception = d;
}
assertThat(exception, is(not(nullValue())));
}
@Test
public void testEmptyPositionRecovery() throws Exception {
MaxwellContext context = buildContext();
MysqlPositionStore store = buildStore(context);
List<RecoveryInfo> recoveries = store.getAllRecoveryInfos();
assertThat(recoveries.size(), is(0));
String errorMessage = StringUtils.join(store.formatRecoveryFailure(context.getConfig(), recoveries), "\n");
assertThat(errorMessage, is("Unable to find any binlog positions in `positions` table"));
assertThat(store.getRecoveryInfo(context.getConfig()), is(nullValue()));
}
@Test
public void testMultiplePositionRecovery() throws Exception {
MaxwellContext context = buildContext();
Long activeServerID = context.getServerID();
Long newestServerID = activeServerID + 1;
Long intermediateServerID = activeServerID + 2;
Long oldestServerID = activeServerID + 3;
Long newestHeartbeat = 123L;
Long intermediateHeartbeat = newestHeartbeat - 10;
Long oldestHeartbeat = newestHeartbeat - 20;
String binlogFile = "bin.log";
buildStore(context, oldestServerID).set(new Position(new BinlogPosition(0L, binlogFile), oldestHeartbeat));
buildStore(context, intermediateServerID).set(new Position(new BinlogPosition(0L, binlogFile), intermediateHeartbeat));
buildStore(context, newestServerID).set(new Position(new BinlogPosition(0L, binlogFile), newestHeartbeat));
MysqlPositionStore store = buildStore(context);
List<RecoveryInfo> recoveries = store.getAllRecoveryInfos();
if (MaxwellTestSupport.inGtidMode()) {
assertThat(recoveries.size(), is(1));
// gtid mode can't get into a multiple recovery state
return;
}
assertThat(recoveries.size(), is(3));
assertThat(store.getRecoveryInfo(context.getConfig()), is(nullValue()));
String errorMessage = StringUtils.join(store.formatRecoveryFailure(context.getConfig(), recoveries), "\n");
assertThat(errorMessage, containsString("Found multiple binlog positions for cluster in `positions` table."));
for (RecoveryInfo recovery: recoveries) {
assertThat(errorMessage, containsString(" - " + recovery));
}
assertThat(errorMessage, containsString("execute: DELETE FROM maxwell.positions WHERE server_id <> " + newestServerID + ";"));
}
}