package org.corfudb.runtime.view; import lombok.extern.slf4j.Slf4j; import org.corfudb.infrastructure.TestLayoutBuilder; import org.corfudb.runtime.CorfuRuntime; import org.corfudb.runtime.clients.TestRule; import org.corfudb.runtime.view.stream.IStreamView; import org.junit.Test; import java.util.concurrent.CountDownLatch; import static org.assertj.core.api.Assertions.assertThat; /** * Created by mwei on 1/6/16. */ @Slf4j public class LayoutViewTest extends AbstractViewTest { //@Test public void canGetLayout() { CorfuRuntime r = getDefaultRuntime().connect(); Layout l = r.getLayoutView().getCurrentLayout(); assertThat(l.asJSONString()) .isNotNull(); } @Test public void canSetLayout() throws Exception { CorfuRuntime r = getDefaultRuntime().connect(); Layout l = new TestLayoutBuilder() .setEpoch(1) .addLayoutServer(SERVERS.PORT_0) .addSequencer(SERVERS.PORT_0) .buildSegment() .buildStripe() .addLogUnit(SERVERS.PORT_0) .addToSegment() .addToLayout() .build(); l.setRuntime(r); l.moveServersToEpoch(); r.getLayoutView().updateLayout(l, 1L); r.invalidateLayout(); assertThat(r.getLayoutView().getLayout().epoch) .isEqualTo(1L); } @Test public void canTolerateLayoutServerFailure() throws Exception { addServer(SERVERS.PORT_0); addServer(SERVERS.PORT_1); bootstrapAllServers(new TestLayoutBuilder() .setEpoch(1L) .addLayoutServer(SERVERS.PORT_0) .addLayoutServer(SERVERS.PORT_1) .addSequencer(SERVERS.PORT_0) .buildSegment() .buildStripe() .addLogUnit(SERVERS.PORT_0) .addToSegment() .addToLayout() .build()); CorfuRuntime r = getRuntime().connect(); // Fail the network link between the client and test server addServerRule(SERVERS.PORT_1, new TestRule() .always() .drop()); r.invalidateLayout(); r.getStreamsView().get(CorfuRuntime.getStreamID("hi")).hasNext(); } /** * Fail a server and reconfigure * while data operations are going on. * Details: * Start with a configuration of 3 servers SERVERS.PORT_0, SERVERS.PORT_1, SERVERS.PORT_2. * Perform data operations. Fail SERVERS.PORT_1 and reconfigure to have only SERVERS.PORT_0 and SERVERS.PORT_2. * Perform data operations while the reconfiguration is going on. The operations should * be stuck till the new configuration is chosen and then complete after that. * FIXME: We cannot failover the server with the primary sequencer yet. * * @throws Exception */ @Test public void reconfigurationDuringDataOperations() throws Exception { addServer(SERVERS.PORT_0); addServer(SERVERS.PORT_1); addServer(SERVERS.PORT_2); Layout l = new TestLayoutBuilder() .setEpoch(1L) .addLayoutServer(SERVERS.PORT_0) .addLayoutServer(SERVERS.PORT_1) .addLayoutServer(SERVERS.PORT_2) .addSequencer(SERVERS.PORT_0) .addSequencer(SERVERS.PORT_1) .addSequencer(SERVERS.PORT_2) .buildSegment() .buildStripe() .addLogUnit(SERVERS.PORT_0) .addLogUnit(SERVERS.PORT_1) .addLogUnit(SERVERS.PORT_2) .addToSegment() .addToLayout() .build(); bootstrapAllServers(l); CorfuRuntime corfuRuntime = getRuntime(l).connect(); // Thread to reconfigure the layout CountDownLatch startReconfigurationLatch = new CountDownLatch(1); CountDownLatch layoutReconfiguredLatch = new CountDownLatch(1); Thread t = new Thread(() -> { try { startReconfigurationLatch.await(); corfuRuntime.invalidateLayout(); // Fail the network link between the client and test server addServerRule(SERVERS.PORT_1, new TestRule().always().drop()); // New layout removes the failed server SERVERS.PORT_0 Layout newLayout = new TestLayoutBuilder() .setEpoch(l.getEpoch() + 1) .addLayoutServer(SERVERS.PORT_0) .addLayoutServer(SERVERS.PORT_2) .addSequencer(SERVERS.PORT_0) .addSequencer(SERVERS.PORT_2) .buildSegment() .buildStripe() .addLogUnit(SERVERS.PORT_0) .addLogUnit(SERVERS.PORT_2) .addToSegment() .addToLayout() .build(); newLayout.setRuntime(corfuRuntime); //TODO need to figure out if we can move to //update layout newLayout.moveServersToEpoch(); corfuRuntime.getLayoutView().updateLayout(newLayout, newLayout.getEpoch()); corfuRuntime.invalidateLayout(); log.debug("layout updated new layout {}", corfuRuntime.getLayoutView().getLayout()); layoutReconfiguredLatch.countDown(); } catch (Exception e) { e.printStackTrace(); } }); t.start(); // verify writes and reads happen before and after the reconfiguration IStreamView sv = corfuRuntime.getStreamsView().get(CorfuRuntime.getStreamID("streamA")); // This append will happen before the reconfiguration while the read for this append // will happen after reconfiguration writeAndReadStream(corfuRuntime, sv, startReconfigurationLatch, layoutReconfiguredLatch); // Write and read after reconfiguration. writeAndReadStream(corfuRuntime, sv, startReconfigurationLatch, layoutReconfiguredLatch); t.join(); } private void writeAndReadStream(CorfuRuntime corfuRuntime, IStreamView sv, CountDownLatch startReconfigurationLatch, CountDownLatch layoutReconfiguredLatch) throws InterruptedException { byte[] testPayload = "hello world".getBytes(); sv.append(testPayload); startReconfigurationLatch.countDown(); layoutReconfiguredLatch.await(); assertThat(sv.next().getPayload(corfuRuntime)).isEqualTo("hello world".getBytes()); assertThat(sv.next()).isEqualTo(null); } }