package io.eguan.nrs; /* * #%L * Project eguan * %% * Copyright (C) 2012 - 2017 Oodrive * %% * 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. * #L% */ import io.eguan.configuration.MetaConfiguration; import io.eguan.hash.HashAlgorithm; import io.eguan.net.MsgClientStartpoint; import io.eguan.net.MsgNode; import io.eguan.net.MsgServerEndpoint; import io.eguan.net.MsgServerHandler; import io.eguan.net.TestMessagingService; import io.eguan.nrs.NrsClusterSizeConfigKey; import io.eguan.nrs.NrsException; import io.eguan.nrs.NrsFile; import io.eguan.nrs.NrsFileFlag; import io.eguan.nrs.NrsFileHeader; import io.eguan.nrs.NrsFileJanitor; import io.eguan.nrs.NrsMsgEnhancer; import io.eguan.nrs.NrsMsgPostOffice; import io.eguan.proto.Common.OpCode; import io.eguan.proto.Common.ProtocolVersion; import io.eguan.proto.Common.Type; import io.eguan.proto.Common.Uuid; import io.eguan.proto.nrs.NrsRemote.NrsFileMapping; import io.eguan.proto.vvr.VvrRemote.RemoteOperation; import io.eguan.utils.ByteArrays; import io.eguan.utils.SimpleIdentifierProvider; import io.eguan.utils.UuidT; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.net.InetSocketAddress; import java.util.Arrays; import java.util.Collection; import java.util.Queue; import java.util.Random; import java.util.UUID; import java.util.concurrent.ConcurrentLinkedQueue; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicLong; import javax.annotation.Nonnull; import org.junit.After; import org.junit.Assert; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; import org.junit.runners.Parameterized.Parameters; import org.junit.runners.model.InitializationError; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.google.common.math.LongMath; import com.google.protobuf.GeneratedMessageLite; import com.google.protobuf.MessageLite; /** * Test {@link NrsFile} write and update on several pseudo nodes. * * @author oodrive * @author llambert * @author jmcaba * @author ebredzinski * */ @RunWith(value = Parameterized.class) public class TestNrsFileRemoteL { private static final Logger LOGGER = LoggerFactory.getLogger(TestNrsFileRemoteL.class); // Sometimes, the restore of the file is longer than the background close timeout, so it may not be restored unless // if the file is locked private static final int RETRY_COUNT_LOCK = 5; final class NrsInstance implements MsgServerHandler { private final MsgNode node; private final NrsFileJanitor janitor; private final NrsInstallationHelper helper; private MsgServerEndpoint serverEndpoint; private boolean serverEndpointStarted; private final AtomicLong timeLastUpdate = new AtomicLong(); NrsInstance(final MsgNode node) throws InitializationError { super(); this.node = node; this.helper = new NrsInstallationHelper(); helper.setUpNrs(); final MetaConfiguration config = helper.getConfiguration(); final NrsFileJanitor janitor = new NrsFileJanitor(config); janitor.init(); this.janitor = janitor; } final int getClusterSize() { return janitor.newNrsFileHeaderBuilder().clusterSize(); } final long getTimeLastUpdate() { return timeLastUpdate.get(); } final void setServerEndpoint(final MsgServerEndpoint serverEndpoint) { this.serverEndpoint = serverEndpoint; this.serverEndpointStarted = true; } final void startServerEndpoint() { assert !serverEndpointStarted; serverEndpoint.start(); serverEndpointStarted = true; } final void stopServerEndpoint() { assert serverEndpointStarted; serverEndpoint.stop(); serverEndpointStarted = false; } final void setMsgClientStartpoint(final MsgClientStartpoint clientStartpoint) { this.janitor.setClientStartpoint(clientStartpoint, new NrsMsgEnhancer() { @Override public final void enhance(final GeneratedMessageLite.Builder<?, ?> genericBuilder) { final RemoteOperation.Builder builder = (RemoteOperation.Builder) genericBuilder; builder.setSource(NrsMsgPostOffice.newUuid(node.getNodeId())); } }); } final void fini() throws InitializationError { if (serverEndpoint != null) { try { serverEndpoint.stop(); } catch (final Throwable t) { errors.add(t); } serverEndpoint = null; } try { janitor.fini(); } catch (final Throwable t) { errors.add(t); } try { helper.tearDownNrs(); } catch (final Throwable t) { errors.add(t); } } final void createTestNrsFile(final UuidT<NrsFile> parent, final UUID device, final UuidT<NrsFile> file, final UUID nodeCreator, final long timestamp) throws NrsException { TestNrsFileRemoteL.createTestNrsFile(janitor, helper.getConfiguration(), parent, device, file, nodeCreator, blockCount.longValue(), hashSize, timestamp); } final NrsFile getNrsFile(final UuidT<NrsFile> file) throws NrsException { return janitor.loadNrsFile(file); } final NrsFile openNrsFile(final UuidT<NrsFile> file, final boolean readOnly) throws IOException { return janitor.openNrsFile(file, readOnly); } final void unlockNrsFile(final NrsFile file) throws IOException { janitor.unlockNrsFile(file); } final void closeNrsFile(final NrsFile file) throws IOException { janitor.closeNrsFile(file, false); } @Override public final MessageLite handleMessage(final MessageLite message) { timeLastUpdate.set(System.currentTimeMillis()); try { if (!(message instanceof RemoteOperation)) { final AssertionError ae = new AssertionError("Not an operation: " + message.getClass()); errors.add(ae); return null; } final RemoteOperation op = (RemoteOperation) message; if (op.getType() != Type.NRS) { final AssertionError ae = new AssertionError("type=" + op.getType()); errors.add(ae); return null; } if (op.getOp() != OpCode.SET) { final AssertionError ae = new AssertionError("type=" + op.getOp()); errors.add(ae); return null; } if (!op.hasNrsFileUpdate()) { final AssertionError ae = new AssertionError("No file update"); errors.add(ae); return null; } final UuidT<NrsFile> fileUuid = fromUuidT(op.getUuid()); try { // Have received an update message (for testNrsFileHotRepare1Xxx()) final boolean broadcast = op.getNrsFileUpdate().getBroadcast(); if (!broadcast) messageUpdateReceived.set(true); final NrsFile nrsFile = janitor.openNrsFile(fileUuid, false); try { // final String log = node.getNodeId() + ": received " + op.getNrsFileUpdate().getUpdatesCount() // + " updates, broadcast=" + broadcast + ", version=" + nrsFile.getVersion() + ", size=" // + message.getSerializedSize(); // LOGGER.info(log); nrsFile.handleNrsFileUpdate(op.getNrsFileUpdate()); // LOGGER.info(log + " DONE version=" + nrsFile.getVersion()); } finally { janitor.unlockNrsFile(nrsFile); } } catch (final Throwable t) { final AssertionError ae = new AssertionError("File update failed " + node.getNodeId(), t); errors.add(ae); return null; } // No reply return null; } finally { // Update time once again timeLastUpdate.set(System.currentTimeMillis()); } } } private final int hashSize; private final MsgNode SERVER_1 = new MsgNode(UUID.randomUUID(), new InetSocketAddress("127.0.0.1", 35205)); private final MsgNode SERVER_2 = new MsgNode(UUID.randomUUID(), new InetSocketAddress("127.0.0.1", 35206)); private final MsgNode SERVER_3 = new MsgNode(UUID.randomUUID(), new InetSocketAddress("127.0.0.1", 35207)); private NrsInstance nrsInstance1; private NrsInstance nrsInstance2; private NrsInstance nrsInstance3; private MsgClientStartpoint clientStartpoint1; private MsgClientStartpoint clientStartpoint2; private MsgClientStartpoint clientStartpoint3; // Test file private UuidT<NrsFile> fileUuid; // Comparison read size: set to the cluster size private int readSize; // Stores errors happening in background threads private final Queue<Throwable> errors = new ConcurrentLinkedQueue<>(); // Set when a NrsInstance receives non-broadcast messages private final AtomicBoolean messageUpdateReceived = new AtomicBoolean(); private final Long blockCount; /** * Select the block count so that the H1 table of the {@link NrsFile} have one block or more than one block. * * @return initial parameters. */ @Parameters public static Collection<Object[]> getBlockCountConfig() { final Object[][] blockCounts = new Object[][] { { /* _________ L1 table one cluster */Long.valueOf(50L), Integer.valueOf(24) }, { /* L1 table more than one cluster */Long.valueOf(563700L), Integer.valueOf(24) }, // FIXME { /* ______________ Very large file */Long.valueOf(268435456L), Integer.valueOf(24) }, { /* _________ L1 table one cluster */Long.valueOf(50L), Integer.valueOf(123) }, { /* L1 table more than one cluster */Long.valueOf(563700L), Integer.valueOf(123) }, // FIXME { /* ______________ Very large file */Long.valueOf(268435456L), Integer.valueOf(123) }, }; return Arrays.asList(blockCounts); } public TestNrsFileRemoteL(final Long blockCount, final Integer hashSize) { super(); this.blockCount = blockCount; this.hashSize = hashSize.intValue(); LOGGER.info("Test " + TestNrsFileRemoteL.class.getSimpleName() + ": " + blockCount + " blocks, hashSize=" + this.hashSize); } @Before public void setupTest() throws InitializationError, InterruptedException, NrsException { { nrsInstance1 = new NrsInstance(SERVER_1); final MsgServerEndpoint serverEndpoint1 = new MsgServerEndpoint(SERVER_1, nrsInstance1, RemoteOperation.getDefaultInstance()); serverEndpoint1.start(); nrsInstance1.setServerEndpoint(serverEndpoint1); } { nrsInstance2 = new NrsInstance(SERVER_2); final MsgServerEndpoint serverEndpoint2 = new MsgServerEndpoint(SERVER_2, nrsInstance2, RemoteOperation.getDefaultInstance()); serverEndpoint2.start(); nrsInstance2.setServerEndpoint(serverEndpoint2); } { nrsInstance3 = new NrsInstance(SERVER_3); final MsgServerEndpoint serverEndpoint3 = new MsgServerEndpoint(SERVER_3, nrsInstance3, RemoteOperation.getDefaultInstance()); serverEndpoint3.start(); nrsInstance3.setServerEndpoint(serverEndpoint3); } { clientStartpoint1 = new MsgClientStartpoint(SERVER_1.getNodeId(), null); clientStartpoint1.addPeer(SERVER_2); clientStartpoint1.addPeer(SERVER_3); clientStartpoint1.setTimeout(30 * 1000); clientStartpoint1.start(); nrsInstance1.setMsgClientStartpoint(clientStartpoint1); TestMessagingService.waitConnected(2, clientStartpoint1); } { clientStartpoint2 = new MsgClientStartpoint(SERVER_2.getNodeId(), null); clientStartpoint2.addPeer(SERVER_1); clientStartpoint2.addPeer(SERVER_3); clientStartpoint2.start(); nrsInstance2.setMsgClientStartpoint(clientStartpoint2); TestMessagingService.waitConnected(2, clientStartpoint2); } { clientStartpoint3 = new MsgClientStartpoint(SERVER_3.getNodeId(), null); clientStartpoint3.addPeer(SERVER_1); clientStartpoint3.addPeer(SERVER_2); clientStartpoint3.start(); nrsInstance3.setMsgClientStartpoint(clientStartpoint3); TestMessagingService.waitConnected(2, clientStartpoint3); } // Create a file on each node final UuidT<NrsFile> parent = SimpleIdentifierProvider.newId(); final UUID device = UUID.randomUUID(); fileUuid = SimpleIdentifierProvider.newId(); final UUID creatorNode = SERVER_1.getNodeId(); final long timestamp = System.currentTimeMillis(); nrsInstance1.createTestNrsFile(parent, device, fileUuid, creatorNode, timestamp); nrsInstance2.createTestNrsFile(parent, device, fileUuid, creatorNode, timestamp); nrsInstance3.createTestNrsFile(parent, device, fileUuid, creatorNode, timestamp); // File comparison: read cluster per cluster readSize = nrsInstance1.getClusterSize(); messageUpdateReceived.set(false); } @After public void tearDownTest() throws Throwable { if (clientStartpoint1 != null) { try { clientStartpoint1.stop(); } catch (final Throwable t) { errors.add(t); } clientStartpoint1 = null; } if (clientStartpoint2 != null) { try { clientStartpoint2.stop(); } catch (final Throwable t) { errors.add(t); } clientStartpoint2 = null; } if (clientStartpoint3 != null) { try { clientStartpoint3.stop(); } catch (final Throwable t) { errors.add(t); } clientStartpoint3 = null; } if (nrsInstance1 != null) { nrsInstance1.fini(); nrsInstance1 = null; } if (nrsInstance2 != null) { nrsInstance2.fini(); nrsInstance2 = null; } if (nrsInstance3 != null) { nrsInstance3.fini(); nrsInstance3 = null; } // Check Errors if (!errors.isEmpty()) { // Throw first one final Throwable t = errors.peek(); while (!errors.isEmpty()) { LOGGER.warn("Test error", errors.poll()); } throw t; } } /** * Test live update. * * @throws IOException * @throws InterruptedException */ @Test public void testNrsFileLiveUpdate() throws IOException, InterruptedException { // Check initial contents compareFiles(false); // Write on the three nodes for 12 seconds randomWrite(nrsInstance1, 12 * 1000, new AtomicBoolean()); // Restore file: should have nothing to do (and wait for the end of the live update of the files) Assert.assertFalse(restoreFile(nrsInstance1, nrsInstance2, SERVER_2, false)); Assert.assertFalse(restoreFile(nrsInstance2, nrsInstance3, SERVER_3, false)); // Check final contents compareFiles(false); } /** * Interrupt a server while writing in an NrsFile than repair the file after the end of writes (no write in * progress). * * @throws IOException * @throws InterruptedException */ @Test public void testNrsFileColdRepare() throws IOException, InterruptedException { // Check initial contents compareFiles(false); final Thread writer = new Thread(new Runnable() { @Override public final void run() { // Write on the nodes for 10 seconds try { randomWrite(nrsInstance1, 10 * 1000, new AtomicBoolean()); } catch (final Throwable t) { final AssertionError ae = new AssertionError("Write failed", t); errors.add(ae); } } }, "Writer"); writer.setDaemon(true); writer.start(); try { // Wait a little before stopping SERVER_3 Thread.sleep(6 * 1000); nrsInstance3.stopServerEndpoint(); } finally { writer.join(); } // Restart server and restore file nrsInstance3.startServerEndpoint(); TestMessagingService.waitConnected(2, clientStartpoint1); doRestoreFile(nrsInstance3, SERVER_3); // Check final contents compareFiles(false); } /** * Update a file while some writes are in progress at the beginning of the update. * * @throws IOException * @throws InterruptedException */ @Test public void testNrsFileHotRepare1OneNode() throws IOException, InterruptedException { // Check initial contents compareFiles(false); final Thread writer = new Thread(new Runnable() { @Override public final void run() { // Write on the nodes until the first update message is received try { randomWrite(nrsInstance1, 40 * 1000, messageUpdateReceived); } catch (final Throwable t) { final AssertionError ae = new AssertionError("Write failed", t); errors.add(ae); } } }, "Writer"); writer.setDaemon(true); writer.start(); try { suspendServerRestoreFile(nrsInstance3, SERVER_3); } finally { // The writer should have been stopped writer.join(); } // Check final contents compareFiles(true); } /** * Update a file while some writes are in progress during the update. * * @throws IOException * @throws InterruptedException */ @Test public void testNrsFileHotRepare2OneNode() throws IOException, InterruptedException { // Check initial contents compareFiles(false); // Stop write at the end final AtomicBoolean conditionStop = new AtomicBoolean(); final Thread writer = new Thread(new Runnable() { @Override public final void run() { // Write on the nodes until the first update message is received try { randomWrite(nrsInstance1, 40 * 1000, conditionStop); } catch (final Throwable t) { final AssertionError ae = new AssertionError("Write failed", t); errors.add(ae); } } }, "Writer"); writer.setDaemon(true); writer.start(); try { suspendServerRestoreFile(nrsInstance3, SERVER_3); } finally { // Stops the writer conditionStop.set(true); writer.join(); } // Check final contents compareFiles(true); } /** * Update a file in two nodes while some writes are in progress at the beginning of the update. * * @throws IOException * @throws InterruptedException */ @Test public void testNrsFileHotRepare1TwoNodes() throws IOException, InterruptedException { // Check initial contents compareFiles(false); final Thread writer = new Thread(new Runnable() { @Override public final void run() { // Write on the nodes until the first update message is received try { randomWrite(nrsInstance1, 40 * 1000, messageUpdateReceived); } catch (final Throwable t) { final AssertionError ae = new AssertionError("Write failed", t); errors.add(ae); } } }, "Writer"); writer.setDaemon(true); writer.start(); try { final Thread restore2 = new Thread(new Runnable() { @Override public final void run() { // Suspend and restore file in instance2 try { suspendServerRestoreFile(nrsInstance2, SERVER_2); } catch (final Throwable t) { final AssertionError ae = new AssertionError("Restore failed", t); errors.add(ae); } } }, "Restore2"); restore2.setDaemon(true); restore2.start(); try { suspendServerRestoreFile(nrsInstance3, SERVER_3); } finally { restore2.join(); } } finally { // The writer should have been stopped writer.join(); } // Check final contents compareFiles(true); } /** * Update a file in two nodes while some writes are in progress during the update. * * @throws IOException * @throws InterruptedException */ @Test public void testNrsFileHotRepare2TwoNodes() throws IOException, InterruptedException { // Check initial contents compareFiles(false); // Stop write at the end final AtomicBoolean conditionStop = new AtomicBoolean(); final Thread writer = new Thread(new Runnable() { @Override public final void run() { // Write on the nodes until the first update message is received try { randomWrite(nrsInstance1, 40 * 1000, conditionStop); } catch (final Throwable t) { final AssertionError ae = new AssertionError("Write failed", t); errors.add(ae); } } }, "Writer"); writer.setDaemon(true); writer.start(); try { final Thread restore2 = new Thread(new Runnable() { @Override public final void run() { // Suspend and restore file in instance2 try { suspendServerRestoreFile(nrsInstance2, SERVER_2); } catch (final Throwable t) { final AssertionError ae = new AssertionError("Restore failed", t); errors.add(ae); } } }, "Restore2"); restore2.setDaemon(true); restore2.start(); try { suspendServerRestoreFile(nrsInstance3, SERVER_3); } finally { restore2.join(); } } finally { // Stops the writer conditionStop.set(true); writer.join(); } // Check final contents compareFiles(true); } private final void randomWrite(final NrsInstance nrsInstance, final long duration, final AtomicBoolean conditionStop) throws IOException, InterruptedException { final NrsFile nrsFile = nrsInstance.openNrsFile(fileUuid, false); try { LOGGER.info("Start writing in '" + nrsFile + "'"); final NrsFileHeader<NrsFile> header = nrsFile.getDescriptor(); final long blockCount = header.getSize() / header.getBlockSize(); // Write some hashes final Random random = new Random(); final byte[] hash = new byte[hashSize]; long prevBlockIndex = 0L; final long end = System.currentTimeMillis() + duration; while (System.currentTimeMillis() < end && !conditionStop.get()) { random.nextBytes(hash); final long blockIndex = LongMath.mod(random.nextLong(), blockCount); nrsFile.write(blockIndex, hash); // Reset some blocks and wait a little to avoid unrealistic saturation final long version = nrsFile.getVersion(); if ((version % 10) == 0) { nrsFile.reset(prevBlockIndex); Thread.sleep(1); } if ((version % 12) == 0) { nrsFile.trim(prevBlockIndex); } prevBlockIndex = blockIndex; } } finally { nrsInstance.closeNrsFile(nrsFile); LOGGER.info("End writing in '" + nrsFile + "'"); } } private final void suspendServerRestoreFile(final NrsInstance nrsInstanceDst, final MsgNode nodeDst) throws InterruptedException, IllegalStateException, IOException { // Wait a little before stopping the server Thread.sleep(4 * 1000); nrsInstanceDst.stopServerEndpoint(); // Wait a little before performing update Thread.sleep(7 * 1000); // Restart server and restore file nrsInstanceDst.startServerEndpoint(); TestMessagingService.waitConnected(2, clientStartpoint1); doRestoreFile(nrsInstanceDst, nodeDst); } private final void doRestoreFile(final NrsInstance nrsInstanceDst, final MsgNode nodeDst) throws IllegalStateException, IOException { final NrsFile nrsFile = nrsInstanceDst.getNrsFile(fileUuid); LOGGER.info("Start restore '" + nrsFile + "'"); try { boolean lockDst = false; int retryCount = 0; while (restoreFile(nrsInstance1, nrsInstanceDst, nodeDst, lockDst)) { LOGGER.info("Retry file update"); retryCount++; if (retryCount > RETRY_COUNT_LOCK) lockDst = true; } } finally { LOGGER.info("End restore '" + nrsFile + "'"); } } private final boolean restoreFile(final NrsInstance nrsInstanceSrc, final NrsInstance nrsInstanceDst, final MsgNode nodeDst, final boolean lockDst) throws IllegalStateException, IOException { boolean dstOpened = true; final NrsFile nrsFileDst = nrsInstanceDst.openNrsFile(fileUuid, false); try { if (!lockDst) { nrsInstanceDst.unlockNrsFile(nrsFileDst); dstOpened = false; } final RemoteOperation.Builder builder = nrsFileDst.getFileMapping(HashAlgorithm.TIGER); // Complete builder builder.setVersion(ProtocolVersion.VERSION_1); builder.setType(Type.NRS); builder.setOp(OpCode.LIST); final NrsFileMapping mapping = builder.build().getNrsFileMapping(); final NrsFile nrsFileSrc = nrsInstanceSrc.openNrsFile(fileUuid, true); nrsInstanceSrc.unlockNrsFile(nrsFileSrc); nrsFileSrc.processNrsFileSync(mapping, nodeDst.getNodeId()); // Wait for the update end on the destination file final boolean inProgress = nrsFileDst.waitUpdateEnd(60, TimeUnit.SECONDS); if (inProgress) { nrsFileDst.resetUpdate(); } return nrsFileDst.isLastUpdateAborted(); } finally { if (dstOpened) { nrsInstanceDst.unlockNrsFile(nrsFileDst); } } } /** * Check that the {@link NrsFile} is identical on the three 'nodes'. * * @throws IOException * @throws InterruptedException */ private final void compareFiles(final boolean checkDone) throws IOException, InterruptedException { if (checkDone) { checkMessagesDone(); } LOGGER.info("Start compare files"); final long v1, v2, v3; final File file1, file2, file3; { final NrsFile nrsFile1 = nrsInstance1.getNrsFile(fileUuid); file1 = nrsFile1.getFile().toFile(); v1 = nrsFile1.getVersion(); } { final NrsFile nrsFile2 = nrsInstance2.getNrsFile(fileUuid); file2 = nrsFile2.getFile().toFile(); v2 = nrsFile2.getVersion(); } { final NrsFile nrsFile3 = nrsInstance3.getNrsFile(fileUuid); file3 = nrsFile3.getFile().toFile(); v3 = nrsFile3.getVersion(); } Assert.assertEquals(v1, v2); Assert.assertEquals(v1, v3); compareFiles(file1, file2); compareFiles(file1, file3); LOGGER.info("End compare files"); } private final long MSG_TIMEOUT = 200; // 200 ms private final int MSG_COUNT = 20; /** * Wait for the end of the processing of update messages. Check if some messages have been received for the * {@link NrsInstance}. * * @throws InterruptedException */ private final void checkMessagesDone() throws InterruptedException { final NrsInstance[] nrsInstances = new NrsInstance[] { nrsInstance2, nrsInstance3 }; final long[] prevTimeLastUpdates = new long[] { nrsInstance2.getTimeLastUpdate(), nrsInstance3.getTimeLastUpdate() }; final int[] counts = new int[] { 0, 0 }; int done; do { Thread.sleep(MSG_TIMEOUT); done = 0; for (int i = 0; i < nrsInstances.length; i++) { final NrsInstance nrsInstance = nrsInstances[i]; final long newTimeLastUpdate = nrsInstance.getTimeLastUpdate(); if (prevTimeLastUpdates[i] == newTimeLastUpdate) { counts[i]++; done += counts[i] >= MSG_COUNT ? 1 : 0; } else { // New message(s) received: reset counter counts[i] = 0; prevTimeLastUpdates[i] = newTimeLastUpdate; } } } while (done != nrsInstances.length); } private final void compareFiles(final File f1, final File f2) throws IOException { final byte[] bufFile1 = new byte[readSize]; final byte[] bufFile2 = new byte[readSize]; // Check file size Assert.assertEquals("size", f1.length(), f2.length()); Assert.assertEquals(0, f1.length() % readSize); // Read contents long offset = 0; try (FileInputStream fis1 = new FileInputStream(f1)) { try (FileInputStream fis2 = new FileInputStream(f2)) { int readLen; while ((readLen = fis1.read(bufFile1)) != -1) { // Should fill the buffer at every read Assert.assertEquals(readSize, readLen); Assert.assertEquals(readSize, fis2.read(bufFile2)); // Compare read bytes ByteArrays.assertEqualsByteArrays("offset=" + offset + " ", bufFile1, bufFile2); offset += readLen; } } } } private static final UuidT<NrsFile> fromUuidT(@Nonnull final Uuid uuid) { return new UuidT<>(uuid.getMsb(), uuid.getLsb()); } private static final void createTestNrsFile(final NrsFileJanitor janitor, final MetaConfiguration config, final UuidT<NrsFile> parent, final UUID device, final UuidT<NrsFile> file, final UUID node, final long blockCount, final int hashSize, final long timestamp) throws NrsException { // Check cluster size final int clusterSize = janitor.newNrsFileHeaderBuilder().clusterSize(); Assert.assertEquals(NrsClusterSizeConfigKey.getInstance().getTypedValue(config), Integer.valueOf(clusterSize)); // Create NrsFile final int blockSize = 4096; final long size = blockSize * blockCount; final NrsFileHeader.Builder<NrsFile> headerBuilder = janitor.newNrsFileHeaderBuilder(); headerBuilder.parent(parent); headerBuilder.device(device); headerBuilder.node(node); headerBuilder.file(file); headerBuilder.size(size); headerBuilder.blockSize(blockSize); headerBuilder.hashSize(hashSize); headerBuilder.timestamp(timestamp); headerBuilder.addFlags(NrsFileFlag.PARTIAL); final NrsFileHeader<NrsFile> header = headerBuilder.build(); janitor.createNrsFile(header); } }