/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you 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. */ package org.apache.flume.channel.file; import com.google.common.collect.Lists; import com.google.common.collect.Maps; import com.google.common.collect.Sets; import com.google.common.io.Files; import org.apache.commons.io.FileUtils; import org.apache.flume.Transaction; import org.apache.flume.channel.file.proto.ProtosFactory; import org.fest.reflect.exception.ReflectionError; import org.junit.After; import org.junit.Assert; import org.junit.Before; import org.junit.Test; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.FilenameFilter; import java.io.IOException; import java.io.RandomAccessFile; import java.util.Map; import java.util.Random; import java.util.Set; import java.util.concurrent.Executors; import static org.apache.flume.channel.file.TestUtils.compareInputAndOut; import static org.apache.flume.channel.file.TestUtils.consumeChannel; import static org.apache.flume.channel.file.TestUtils.fillChannel; import static org.apache.flume.channel.file.TestUtils.forceCheckpoint; import static org.apache.flume.channel.file.TestUtils.putEvents; import static org.apache.flume.channel.file.TestUtils.putWithoutCommit; import static org.apache.flume.channel.file.TestUtils.takeEvents; import static org.apache.flume.channel.file.TestUtils.takeWithoutCommit; import static org.fest.reflect.core.Reflection.*; public class TestFileChannelRestart extends TestFileChannelBase { protected static final Logger LOG = LoggerFactory.getLogger(TestFileChannelRestart.class); @Before public void setup() throws Exception { super.setup(); } @After public void teardown() { super.teardown(); } @Override protected FileChannel createFileChannel(Map<String, String> overrides) { // FLUME-2482, making sure scheduled checkpoint never gets called overrides.put(FileChannelConfiguration.CHECKPOINT_INTERVAL, "6000000"); return TestUtils.createFileChannel(checkpointDir.getAbsolutePath(), dataDir, backupDir.getAbsolutePath(), overrides); } @Test public void testRestartLogReplayV1() throws Exception { doTestRestart(true, false, false, false); } @Test public void testRestartLogReplayV2() throws Exception { doTestRestart(false, false, false, false); } @Test public void testFastReplayV1() throws Exception { doTestRestart(true, true, true, true); } @Test public void testFastReplayV2() throws Exception { doTestRestart(false, true, true, true); } @Test public void testFastReplayNegativeTestV1() throws Exception { doTestRestart(true, true, false, true); } @Test public void testFastReplayNegativeTestV2() throws Exception { doTestRestart(false, true, false, true); } @Test public void testNormalReplayV1() throws Exception { doTestRestart(true, true, true, false); } @Test public void testNormalReplayV2() throws Exception { doTestRestart(false, true, true, false); } public void doTestRestart(boolean useLogReplayV1, boolean forceCheckpoint, boolean deleteCheckpoint, boolean useFastReplay) throws Exception { Map<String, String> overrides = Maps.newHashMap(); overrides.put(FileChannelConfiguration.USE_LOG_REPLAY_V1, String.valueOf(useLogReplayV1)); overrides.put( FileChannelConfiguration.USE_FAST_REPLAY, String.valueOf(useFastReplay)); channel = createFileChannel(overrides); channel.start(); Assert.assertTrue(channel.isOpen()); Set<String> in = fillChannel(channel, "restart"); if (forceCheckpoint) { forceCheckpoint(channel); } channel.stop(); if (deleteCheckpoint) { File checkpoint = new File(checkpointDir, "checkpoint"); Assert.assertTrue(checkpoint.delete()); File checkpointMetaData = Serialization.getMetaDataFile(checkpoint); Assert.assertTrue(checkpointMetaData.delete()); } channel = createFileChannel(overrides); channel.start(); Assert.assertTrue(channel.isOpen()); Set<String> out = consumeChannel(channel); compareInputAndOut(in, out); } @Test public void testRestartWhenMetaDataExistsButCheckpointDoesNot() throws Exception { doTestRestartWhenMetaDataExistsButCheckpointDoesNot(false); } @Test public void testRestartWhenMetaDataExistsButCheckpointDoesNotWithBackup() throws Exception { doTestRestartWhenMetaDataExistsButCheckpointDoesNot(true); } private void doTestRestartWhenMetaDataExistsButCheckpointDoesNot(boolean backup) throws Exception { Map<String, String> overrides = Maps.newHashMap(); overrides.put(FileChannelConfiguration.USE_DUAL_CHECKPOINTS, String.valueOf(backup)); channel = createFileChannel(overrides); channel.start(); Assert.assertTrue(channel.isOpen()); Set<String> in = putEvents(channel, "restart", 10, 100); Assert.assertEquals(100, in.size()); forceCheckpoint(channel); if (backup) { Thread.sleep(2000); } channel.stop(); File checkpoint = new File(checkpointDir, "checkpoint"); Assert.assertTrue(checkpoint.delete()); File checkpointMetaData = Serialization.getMetaDataFile(checkpoint); Assert.assertTrue(checkpointMetaData.exists()); channel = createFileChannel(overrides); channel.start(); Assert.assertTrue(channel.isOpen()); Assert.assertTrue(checkpoint.exists()); Assert.assertTrue(checkpointMetaData.exists()); Assert.assertTrue(!backup || channel.checkpointBackupRestored()); Set<String> out = consumeChannel(channel); compareInputAndOut(in, out); } @Test public void testRestartWhenCheckpointExistsButMetaDoesNot() throws Exception { doTestRestartWhenCheckpointExistsButMetaDoesNot(false); } @Test public void testRestartWhenCheckpointExistsButMetaDoesNotWithBackup() throws Exception { doTestRestartWhenCheckpointExistsButMetaDoesNot(true); } private void doTestRestartWhenCheckpointExistsButMetaDoesNot(boolean backup) throws Exception { Map<String, String> overrides = Maps.newHashMap(); overrides.put(FileChannelConfiguration.USE_DUAL_CHECKPOINTS, String.valueOf(backup)); channel = createFileChannel(overrides); channel.start(); Assert.assertTrue(channel.isOpen()); Set<String> in = putEvents(channel, "restart", 10, 100); Assert.assertEquals(100, in.size()); forceCheckpoint(channel); if (backup) { Thread.sleep(2000); } channel.stop(); File checkpoint = new File(checkpointDir, "checkpoint"); File checkpointMetaData = Serialization.getMetaDataFile(checkpoint); Assert.assertTrue(checkpointMetaData.delete()); Assert.assertTrue(checkpoint.exists()); channel = createFileChannel(overrides); channel.start(); Assert.assertTrue(channel.isOpen()); Assert.assertTrue(checkpoint.exists()); Assert.assertTrue(checkpointMetaData.exists()); Assert.assertTrue(!backup || channel.checkpointBackupRestored()); Set<String> out = consumeChannel(channel); compareInputAndOut(in, out); } @Test public void testRestartWhenNoCheckpointExists() throws Exception { doTestRestartWhenNoCheckpointExists(false); } @Test public void testRestartWhenNoCheckpointExistsWithBackup() throws Exception { doTestRestartWhenNoCheckpointExists(true); } private void doTestRestartWhenNoCheckpointExists(boolean backup) throws Exception { Map<String, String> overrides = Maps.newHashMap(); overrides.put(FileChannelConfiguration.USE_DUAL_CHECKPOINTS, String.valueOf(backup)); channel = createFileChannel(overrides); channel.start(); Assert.assertTrue(channel.isOpen()); Set<String> in = putEvents(channel, "restart", 10, 100); Assert.assertEquals(100, in.size()); forceCheckpoint(channel); if (backup) { Thread.sleep(2000); } channel.stop(); File checkpoint = new File(checkpointDir, "checkpoint"); File checkpointMetaData = Serialization.getMetaDataFile(checkpoint); Assert.assertTrue(checkpointMetaData.delete()); Assert.assertTrue(checkpoint.delete()); channel = createFileChannel(overrides); channel.start(); Assert.assertTrue(channel.isOpen()); Assert.assertTrue(checkpoint.exists()); Assert.assertTrue(checkpointMetaData.exists()); Assert.assertTrue(!backup || channel.checkpointBackupRestored()); Set<String> out = consumeChannel(channel); compareInputAndOut(in, out); } @Test public void testBadCheckpointVersion() throws Exception { doTestBadCheckpointVersion(false); } @Test public void testBadCheckpointVersionWithBackup() throws Exception { doTestBadCheckpointVersion(true); } private void doTestBadCheckpointVersion(boolean backup) throws Exception { Map<String, String> overrides = Maps.newHashMap(); overrides.put(FileChannelConfiguration.USE_DUAL_CHECKPOINTS, String.valueOf(backup)); channel = createFileChannel(overrides); channel.start(); Assert.assertTrue(channel.isOpen()); Set<String> in = putEvents(channel, "restart", 10, 100); Assert.assertEquals(100, in.size()); forceCheckpoint(channel); if (backup) { Thread.sleep(2000); } channel.stop(); File checkpoint = new File(checkpointDir, "checkpoint"); RandomAccessFile writer = new RandomAccessFile(checkpoint, "rw"); writer.seek(EventQueueBackingStoreFile.INDEX_VERSION * Serialization.SIZE_OF_LONG); writer.writeLong(2L); writer.getFD().sync(); writer.close(); channel = createFileChannel(overrides); channel.start(); Assert.assertTrue(channel.isOpen()); Assert.assertTrue(!backup || channel.checkpointBackupRestored()); Set<String> out = consumeChannel(channel); compareInputAndOut(in, out); } @Test public void testBadCheckpointMetaVersion() throws Exception { doTestBadCheckpointMetaVersion(false); } @Test public void testBadCheckpointMetaVersionWithBackup() throws Exception { doTestBadCheckpointMetaVersion(true); } private void doTestBadCheckpointMetaVersion(boolean backup) throws Exception { Map<String, String> overrides = Maps.newHashMap(); overrides.put(FileChannelConfiguration.USE_DUAL_CHECKPOINTS, String.valueOf(backup)); channel = createFileChannel(overrides); channel.start(); Assert.assertTrue(channel.isOpen()); Set<String> in = putEvents(channel, "restart", 10, 100); Assert.assertEquals(100, in.size()); forceCheckpoint(channel); if (backup) { Thread.sleep(2000); } channel.stop(); File checkpoint = new File(checkpointDir, "checkpoint"); FileInputStream is = new FileInputStream(Serialization.getMetaDataFile(checkpoint)); ProtosFactory.Checkpoint meta = ProtosFactory.Checkpoint.parseDelimitedFrom(is); Assert.assertNotNull(meta); is.close(); FileOutputStream os = new FileOutputStream( Serialization.getMetaDataFile(checkpoint)); meta.toBuilder().setVersion(2).build().writeDelimitedTo(os); os.flush(); channel = createFileChannel(overrides); channel.start(); Assert.assertTrue(channel.isOpen()); Assert.assertTrue(!backup || channel.checkpointBackupRestored()); Set<String> out = consumeChannel(channel); compareInputAndOut(in, out); } @Test public void testDifferingOrderIDCheckpointAndMetaVersion() throws Exception { doTestDifferingOrderIDCheckpointAndMetaVersion(false); } @Test public void testDifferingOrderIDCheckpointAndMetaVersionWithBackup() throws Exception { doTestDifferingOrderIDCheckpointAndMetaVersion(true); } private void doTestDifferingOrderIDCheckpointAndMetaVersion(boolean backup) throws Exception { Map<String, String> overrides = Maps.newHashMap(); overrides.put(FileChannelConfiguration.USE_DUAL_CHECKPOINTS, String.valueOf(backup)); channel = createFileChannel(overrides); channel.start(); Assert.assertTrue(channel.isOpen()); Set<String> in = putEvents(channel, "restart", 10, 100); Assert.assertEquals(100, in.size()); forceCheckpoint(channel); if (backup) { Thread.sleep(2000); } channel.stop(); File checkpoint = new File(checkpointDir, "checkpoint"); FileInputStream is = new FileInputStream(Serialization.getMetaDataFile(checkpoint)); ProtosFactory.Checkpoint meta = ProtosFactory.Checkpoint.parseDelimitedFrom(is); Assert.assertNotNull(meta); is.close(); FileOutputStream os = new FileOutputStream( Serialization.getMetaDataFile(checkpoint)); meta.toBuilder().setWriteOrderID(12).build().writeDelimitedTo(os); os.flush(); channel = createFileChannel(overrides); channel.start(); Assert.assertTrue(channel.isOpen()); Assert.assertTrue(!backup || channel.checkpointBackupRestored()); Set<String> out = consumeChannel(channel); compareInputAndOut(in, out); } @Test public void testIncompleteCheckpoint() throws Exception { doTestIncompleteCheckpoint(false); } @Test public void testIncompleteCheckpointWithCheckpoint() throws Exception { doTestIncompleteCheckpoint(true); } private void doTestIncompleteCheckpoint(boolean backup) throws Exception { Map<String, String> overrides = Maps.newHashMap(); overrides.put(FileChannelConfiguration.USE_DUAL_CHECKPOINTS, String.valueOf(backup)); channel = createFileChannel(overrides); channel.start(); Assert.assertTrue(channel.isOpen()); Set<String> in = putEvents(channel, "restart", 10, 100); Assert.assertEquals(100, in.size()); forceCheckpoint(channel); if (backup) { Thread.sleep(2000); } channel.stop(); File checkpoint = new File(checkpointDir, "checkpoint"); RandomAccessFile writer = new RandomAccessFile(checkpoint, "rw"); writer.seek(EventQueueBackingStoreFile.INDEX_CHECKPOINT_MARKER * Serialization.SIZE_OF_LONG); writer.writeLong(EventQueueBackingStoreFile.CHECKPOINT_INCOMPLETE); writer.getFD().sync(); writer.close(); channel = createFileChannel(overrides); channel.start(); Assert.assertTrue(channel.isOpen()); Assert.assertTrue(!backup || channel.checkpointBackupRestored()); Set<String> out = consumeChannel(channel); compareInputAndOut(in, out); } @Test public void testCorruptInflightPuts() throws Exception { doTestCorruptInflights("inflightputs", false); } @Test public void testCorruptInflightPutsWithBackup() throws Exception { doTestCorruptInflights("inflightputs", true); } @Test public void testCorruptInflightTakes() throws Exception { doTestCorruptInflights("inflighttakes", false); } @Test public void testCorruptInflightTakesWithBackup() throws Exception { doTestCorruptInflights("inflighttakes", true); } @Test public void testFastReplayWithCheckpoint() throws Exception { testFastReplay(false, true); } @Test public void testFastReplayWithBadCheckpoint() throws Exception { testFastReplay(true, true); } @Test public void testNoFastReplayWithCheckpoint() throws Exception { testFastReplay(false, false); } @Test public void testNoFastReplayWithBadCheckpoint() throws Exception { testFastReplay(true, false); } private void testFastReplay(boolean shouldCorruptCheckpoint, boolean useFastReplay) throws Exception { Map<String, String> overrides = Maps.newHashMap(); overrides.put(FileChannelConfiguration.USE_FAST_REPLAY, String.valueOf(useFastReplay)); channel = createFileChannel(overrides); channel.start(); Assert.assertTrue(channel.isOpen()); Set<String> in = putEvents(channel, "restart", 10, 100); Assert.assertEquals(100, in.size()); forceCheckpoint(channel); channel.stop(); if (shouldCorruptCheckpoint) { File checkpoint = new File(checkpointDir, "checkpoint"); RandomAccessFile writer = new RandomAccessFile( Serialization.getMetaDataFile(checkpoint), "rw"); writer.seek(10); writer.writeLong(new Random().nextLong()); writer.getFD().sync(); writer.close(); } channel = createFileChannel(overrides); channel.start(); Assert.assertTrue(channel.isOpen()); Set<String> out = consumeChannel(channel); if (useFastReplay && shouldCorruptCheckpoint) { Assert.assertTrue(channel.didFastReplay()); } else { Assert.assertFalse(channel.didFastReplay()); } compareInputAndOut(in, out); } private void doTestCorruptInflights(String name, boolean backup) throws Exception { Map<String, String> overrides = Maps.newHashMap(); overrides.put(FileChannelConfiguration.USE_DUAL_CHECKPOINTS, String.valueOf(backup)); channel = createFileChannel(overrides); channel.start(); Assert.assertTrue(channel.isOpen()); final Set<String> in1 = putEvents(channel, "restart-", 10, 100); Assert.assertEquals(100, in1.size()); Executors.newSingleThreadScheduledExecutor().submit(new Runnable() { @Override public void run() { Transaction tx = channel.getTransaction(); Set<String> out1 = takeWithoutCommit(channel, tx, 100); Assert.assertEquals(100, out1.size()); } }); Transaction tx = channel.getTransaction(); Set<String> in2 = putWithoutCommit(channel, tx, "restart", 100); Assert.assertEquals(100, in2.size()); forceCheckpoint(channel); if (backup) { Thread.sleep(2000); } tx.commit(); tx.close(); channel.stop(); File inflight = new File(checkpointDir, name); RandomAccessFile writer = new RandomAccessFile(inflight, "rw"); writer.write(new Random().nextInt()); writer.close(); channel = createFileChannel(overrides); channel.start(); Assert.assertTrue(channel.isOpen()); Assert.assertTrue(!backup || channel.checkpointBackupRestored()); Set<String> out = consumeChannel(channel); in1.addAll(in2); compareInputAndOut(in1, out); } @Test public void testTruncatedCheckpointMeta() throws Exception { doTestTruncatedCheckpointMeta(false); } @Test public void testTruncatedCheckpointMetaWithBackup() throws Exception { doTestTruncatedCheckpointMeta(true); } private void doTestTruncatedCheckpointMeta(boolean backup) throws Exception { Map<String, String> overrides = Maps.newHashMap(); overrides.put(FileChannelConfiguration.USE_DUAL_CHECKPOINTS, String.valueOf(backup)); channel = createFileChannel(overrides); channel.start(); Assert.assertTrue(channel.isOpen()); Set<String> in = putEvents(channel, "restart", 10, 100); Assert.assertEquals(100, in.size()); forceCheckpoint(channel); if (backup) { Thread.sleep(2000); } channel.stop(); File checkpoint = new File(checkpointDir, "checkpoint"); RandomAccessFile writer = new RandomAccessFile(Serialization.getMetaDataFile(checkpoint), "rw"); writer.setLength(0); writer.getFD().sync(); writer.close(); channel = createFileChannel(overrides); channel.start(); Assert.assertTrue(channel.isOpen()); Assert.assertTrue(!backup || channel.checkpointBackupRestored()); Set<String> out = consumeChannel(channel); compareInputAndOut(in, out); } @Test public void testCorruptCheckpointMeta() throws Exception { doTestCorruptCheckpointMeta(false); } @Test public void testCorruptCheckpointMetaWithBackup() throws Exception { doTestCorruptCheckpointMeta(true); } private void doTestCorruptCheckpointMeta(boolean backup) throws Exception { Map<String, String> overrides = Maps.newHashMap(); overrides.put(FileChannelConfiguration.USE_DUAL_CHECKPOINTS, String.valueOf(backup)); channel = createFileChannel(overrides); channel.start(); Assert.assertTrue(channel.isOpen()); Set<String> in = putEvents(channel, "restart", 10, 100); Assert.assertEquals(100, in.size()); forceCheckpoint(channel); if (backup) { Thread.sleep(2000); } channel.stop(); File checkpoint = new File(checkpointDir, "checkpoint"); RandomAccessFile writer = new RandomAccessFile(Serialization.getMetaDataFile(checkpoint), "rw"); writer.seek(10); writer.writeLong(new Random().nextLong()); writer.getFD().sync(); writer.close(); channel = createFileChannel(overrides); channel.start(); Assert.assertTrue(channel.isOpen()); Assert.assertTrue(!backup || channel.checkpointBackupRestored()); Set<String> out = consumeChannel(channel); compareInputAndOut(in, out); } private void checkIfBackupUsed(boolean backup) { boolean backupRestored = channel.checkpointBackupRestored(); if (backup) { Assert.assertTrue(backupRestored); } else { Assert.assertFalse(backupRestored); } } //This test will fail without FLUME-1893 @Test public void testCorruptCheckpointVersionMostSignificant4Bytes() throws Exception { Map<String, String> overrides = Maps.newHashMap(); channel = createFileChannel(overrides); channel.start(); Assert.assertTrue(channel.isOpen()); Set<String> in = putEvents(channel, "restart", 10, 100); Assert.assertEquals(100, in.size()); forceCheckpoint(channel); channel.stop(); File checkpoint = new File(checkpointDir, "checkpoint"); RandomAccessFile writer = new RandomAccessFile(checkpoint, "rw"); writer.seek(EventQueueBackingStoreFile.INDEX_VERSION * Serialization.SIZE_OF_LONG); writer.write(new byte[] { (byte) 1, (byte) 5 }); writer.getFD().sync(); writer.close(); channel = createFileChannel(overrides); channel.start(); Assert.assertTrue(channel.isOpen()); Set<String> out = consumeChannel(channel); Assert.assertTrue(channel.didFullReplayDueToBadCheckpointException()); compareInputAndOut(in, out); } //This test will fail without FLUME-1893 @Test public void testCorruptCheckpointCompleteMarkerMostSignificant4Bytes() throws Exception { Map<String, String> overrides = Maps.newHashMap(); channel = createFileChannel(overrides); channel.start(); Assert.assertTrue(channel.isOpen()); Set<String> in = putEvents(channel, "restart", 10, 100); Assert.assertEquals(100, in.size()); forceCheckpoint(channel); channel.stop(); File checkpoint = new File(checkpointDir, "checkpoint"); RandomAccessFile writer = new RandomAccessFile(checkpoint, "rw"); writer.seek(EventQueueBackingStoreFile.INDEX_CHECKPOINT_MARKER * Serialization.SIZE_OF_LONG); writer.write(new byte[] { (byte) 1, (byte) 5 }); writer.getFD().sync(); writer.close(); channel = createFileChannel(overrides); channel.start(); Assert.assertTrue(channel.isOpen()); Set<String> out = consumeChannel(channel); Assert.assertTrue(channel.didFullReplayDueToBadCheckpointException()); compareInputAndOut(in, out); } @Test public void testWithExtraLogs() throws Exception { Map<String, String> overrides = Maps.newHashMap(); overrides.put(FileChannelConfiguration.CAPACITY, "10"); overrides.put(FileChannelConfiguration.TRANSACTION_CAPACITY, "10"); channel = createFileChannel(overrides); channel.start(); Assert.assertTrue(channel.isOpen()); Set<String> in = fillChannel(channel, "extralogs"); for (int i = 0; i < dataDirs.length; i++) { File file = new File(dataDirs[i], Log.PREFIX + (1000 + i)); Assert.assertTrue(file.createNewFile()); Assert.assertTrue(file.length() == 0); File metaDataFile = Serialization.getMetaDataFile(file); File metaDataTempFile = Serialization.getMetaDataTempFile(metaDataFile); Assert.assertTrue(metaDataTempFile.createNewFile()); } channel.stop(); channel = createFileChannel(overrides); channel.start(); Assert.assertTrue(channel.isOpen()); Set<String> out = consumeChannel(channel); compareInputAndOut(in, out); } // Make sure the entire channel was not replayed, only the events from the // backup. @Test public void testBackupUsedEnsureNoFullReplayWithoutCompression() throws Exception { testBackupUsedEnsureNoFullReplay(false); } @Test public void testBackupUsedEnsureNoFullReplayWithCompression() throws Exception { testBackupUsedEnsureNoFullReplay(true); } private void testBackupUsedEnsureNoFullReplay(boolean compressedBackup) throws Exception { File dataDir = Files.createTempDir(); File tempBackup = Files.createTempDir(); Map<String, String> overrides = Maps.newHashMap(); overrides.put(FileChannelConfiguration.DATA_DIRS, dataDir.getAbsolutePath()); overrides.put(FileChannelConfiguration.USE_DUAL_CHECKPOINTS, "true"); overrides.put(FileChannelConfiguration.COMPRESS_BACKUP_CHECKPOINT, String.valueOf(compressedBackup)); channel = createFileChannel(overrides); channel.start(); Assert.assertTrue(channel.isOpen()); Set<String> in = putEvents(channel, "restart", 10, 100); Assert.assertEquals(100, in.size()); Thread.sleep(5000); forceCheckpoint(channel); Thread.sleep(5000); in = putEvents(channel, "restart", 10, 100); takeEvents(channel, 10, 100); Assert.assertEquals(100, in.size()); for (File file : backupDir.listFiles()) { if (file.getName().equals(Log.FILE_LOCK)) { continue; } Files.copy(file, new File(tempBackup, file.getName())); } forceCheckpoint(channel); channel.stop(); Serialization.deleteAllFiles(checkpointDir, Log.EXCLUDES); // The last checkpoint may have been already backed up (it did while I // was running this test, since the checkpoint itself is tiny in unit // tests), so throw away the backup and force the use of an older backup by // bringing in the copy of the last backup before the checkpoint. Serialization.deleteAllFiles(backupDir, Log.EXCLUDES); for (File file : tempBackup.listFiles()) { if (file.getName().equals(Log.FILE_LOCK)) { continue; } Files.copy(file, new File(backupDir, file.getName())); } channel = createFileChannel(overrides); channel.start(); Assert.assertTrue(channel.isOpen()); checkIfBackupUsed(true); Assert.assertEquals(100, channel.getLog().getPutCount()); Assert.assertEquals(20, channel.getLog().getCommittedCount()); Assert.assertEquals(100, channel.getLog().getTakeCount()); Assert.assertEquals(0, channel.getLog().getRollbackCount()); //Read Count = 100 puts + 10 commits + 100 takes + 10 commits Assert.assertEquals(220, channel.getLog().getReadCount()); consumeChannel(channel); FileUtils.deleteQuietly(dataDir); FileUtils.deleteQuietly(tempBackup); } //Make sure data files required by the backup checkpoint are not deleted. @Test public void testDataFilesRequiredByBackupNotDeleted() throws Exception { Map<String, String> overrides = Maps.newHashMap(); overrides.put(FileChannelConfiguration.USE_DUAL_CHECKPOINTS, "true"); overrides.put(FileChannelConfiguration.MAX_FILE_SIZE, "1000"); channel = createFileChannel(overrides); channel.start(); String prefix = "abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz"; Assert.assertTrue(channel.isOpen()); putEvents(channel, prefix, 10, 100); Set<String> origFiles = Sets.newHashSet(); for (File dir : dataDirs) { origFiles.addAll(Lists.newArrayList(dir.list())); } forceCheckpoint(channel); takeEvents(channel, 10, 50); long beforeSecondCheckpoint = System.currentTimeMillis(); forceCheckpoint(channel); Set<String> newFiles = Sets.newHashSet(); int olderThanCheckpoint = 0; int totalMetaFiles = 0; for (File dir : dataDirs) { File[] metadataFiles = dir.listFiles(new FilenameFilter() { @Override public boolean accept(File dir, String name) { if (name.endsWith(".meta")) { return true; } return false; } }); totalMetaFiles = metadataFiles.length; for (File metadataFile : metadataFiles) { if (metadataFile.lastModified() < beforeSecondCheckpoint) { olderThanCheckpoint++; } } newFiles.addAll(Lists.newArrayList(dir.list())); } /* * Files which are not required by the new checkpoint should not have been * modified by the checkpoint. */ Assert.assertTrue(olderThanCheckpoint > 0); Assert.assertTrue(totalMetaFiles != olderThanCheckpoint); /* * All files needed by original checkpoint should still be there. */ Assert.assertTrue(newFiles.containsAll(origFiles)); takeEvents(channel, 10, 50); forceCheckpoint(channel); newFiles = Sets.newHashSet(); for (File dir : dataDirs) { newFiles.addAll(Lists.newArrayList(dir.list())); } Assert.assertTrue(!newFiles.containsAll(origFiles)); } @Test(expected = IOException.class) public void testSlowBackup() throws Throwable { Map<String, String> overrides = Maps.newHashMap(); overrides.put(FileChannelConfiguration.USE_DUAL_CHECKPOINTS, "true"); overrides.put(FileChannelConfiguration.MAX_FILE_SIZE, "1000"); channel = createFileChannel(overrides); channel.start(); Assert.assertTrue(channel.isOpen()); Set<String> in = putEvents(channel, "restart", 10, 100); Assert.assertEquals(100, in.size()); slowdownBackup(channel); forceCheckpoint(channel); in = putEvents(channel, "restart", 10, 100); takeEvents(channel, 10, 100); Assert.assertEquals(100, in.size()); try { forceCheckpoint(channel); } catch (ReflectionError ex) { throw ex.getCause(); } finally { channel.stop(); } } @Test public void testCompressBackup() throws Throwable { Map<String, String> overrides = Maps.newHashMap(); overrides.put(FileChannelConfiguration.USE_DUAL_CHECKPOINTS, "true"); overrides.put(FileChannelConfiguration.MAX_FILE_SIZE, "1000"); overrides.put(FileChannelConfiguration.COMPRESS_BACKUP_CHECKPOINT, "true"); channel = createFileChannel(overrides); channel.start(); Assert.assertTrue(channel.isOpen()); putEvents(channel, "restart", 10, 100); forceCheckpoint(channel); //Wait for the backup checkpoint Thread.sleep(2000); Assert.assertTrue(compressedBackupCheckpoint.exists()); Serialization.decompressFile(compressedBackupCheckpoint, uncompressedBackupCheckpoint); File checkpoint = new File(checkpointDir, "checkpoint"); Assert.assertTrue(FileUtils.contentEquals(checkpoint, uncompressedBackupCheckpoint)); channel.stop(); } @Test public void testToggleCheckpointCompressionFromTrueToFalse() throws Exception { restartToggleCompression(true); } @Test public void testToggleCheckpointCompressionFromFalseToTrue() throws Exception { restartToggleCompression(false); } public void restartToggleCompression(boolean originalCheckpointCompressed) throws Exception { Map<String, String> overrides = Maps.newHashMap(); overrides.put(FileChannelConfiguration.USE_DUAL_CHECKPOINTS, "true"); overrides.put(FileChannelConfiguration.MAX_FILE_SIZE, "1000"); overrides.put(FileChannelConfiguration.TRANSACTION_CAPACITY, "1000"); overrides.put(FileChannelConfiguration.CAPACITY, "1000"); overrides.put(FileChannelConfiguration.COMPRESS_BACKUP_CHECKPOINT, String.valueOf(originalCheckpointCompressed)); channel = createFileChannel(overrides); channel.start(); Assert.assertTrue(channel.isOpen()); Set<String> in = fillChannel(channel, "restart"); forceCheckpoint(channel); Thread.sleep(2000); Assert.assertEquals(compressedBackupCheckpoint.exists(), originalCheckpointCompressed); Assert.assertEquals(uncompressedBackupCheckpoint.exists(), !originalCheckpointCompressed); channel.stop(); File checkpoint = new File(checkpointDir, "checkpoint"); Assert.assertTrue(checkpoint.delete()); File checkpointMetaData = Serialization.getMetaDataFile( checkpoint); Assert.assertTrue(checkpointMetaData.delete()); overrides.put(FileChannelConfiguration.COMPRESS_BACKUP_CHECKPOINT, String.valueOf(!originalCheckpointCompressed)); channel = createFileChannel(overrides); channel.start(); Assert.assertTrue(channel.isOpen()); Set<String> out = consumeChannel(channel); compareInputAndOut(in, out); forceCheckpoint(channel); Thread.sleep(2000); Assert.assertEquals(compressedBackupCheckpoint.exists(), !originalCheckpointCompressed); Assert.assertEquals(uncompressedBackupCheckpoint.exists(), originalCheckpointCompressed); } private static void slowdownBackup(FileChannel channel) { Log log = field("log").ofType(Log.class).in(channel).get(); FlumeEventQueue queue = field("queue") .ofType(FlumeEventQueue.class) .in(log).get(); EventQueueBackingStore backingStore = field("backingStore") .ofType(EventQueueBackingStore.class) .in(queue).get(); field("slowdownBackup").ofType(Boolean.class).in(backingStore).set(true); } }