/*
* 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);
}
}