/**
*
* 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.bookkeeper.bookie;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import org.apache.bookkeeper.bookie.LedgerDirsManager.LedgerDirsListener;
import org.apache.bookkeeper.bookie.LedgerDirsManager.NoWritableLedgerDirException;
import org.apache.bookkeeper.conf.ServerConfiguration;
import org.apache.bookkeeper.conf.TestBKConfiguration;
import org.apache.bookkeeper.stats.NullStatsLogger;
import org.apache.bookkeeper.util.DiskChecker;
import org.apache.bookkeeper.util.IOUtils;
import org.apache.commons.io.FileUtils;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class TestLedgerDirsManager {
private final static Logger LOG = LoggerFactory.getLogger(TestLedgerDirsManager.class);
ServerConfiguration conf = TestBKConfiguration.newServerConfiguration();
File curDir;
LedgerDirsManager dirsManager;
MockDiskChecker mockDiskChecker;
int diskCheckInterval = 1000;
float threshold = 0.5f;
float warnThreshold = 0.5f;
final List<File> tempDirs = new ArrayList<File>();
File createTempDir(String prefix, String suffix) throws IOException {
File dir = IOUtils.createTempDir(prefix, suffix);
tempDirs.add(dir);
return dir;
}
@Before
public void setUp() throws Exception {
File tmpDir = createTempDir("bkTest", ".dir");
curDir = Bookie.getCurrentDirectory(tmpDir);
Bookie.checkDirectoryStructure(curDir);
ServerConfiguration conf = TestBKConfiguration.newServerConfiguration();
conf.setLedgerDirNames(new String[] { tmpDir.toString() });
conf.setDiskCheckInterval(diskCheckInterval);
conf.setIsForceGCAllowWhenNoSpace(true);
mockDiskChecker = new MockDiskChecker(threshold, warnThreshold);
dirsManager = new LedgerDirsManager(conf, conf.getLedgerDirs(), NullStatsLogger.INSTANCE, mockDiskChecker);
dirsManager.init();
}
@After
public void tearDown() throws Exception {
dirsManager.shutdown();
for (File dir : tempDirs) {
FileUtils.deleteDirectory(dir);
}
tempDirs.clear();
}
@Test(timeout=60000)
public void testGetWritableDir() throws Exception {
try {
List<File> writeDirs = dirsManager.getWritableLedgerDirs();
assertTrue("Must have a writable ledgerDir", writeDirs.size() > 0);
} catch (NoWritableLedgerDirException nwlde) {
fail("We should have a writeble ledgerDir");
}
}
@Test(timeout=60000)
public void testPickWritableDirExclusive() throws Exception {
try {
dirsManager.pickRandomWritableDir(curDir);
fail("Should not reach here due to there is no writable ledger dir.");
} catch (NoWritableLedgerDirException nwlde) {
// expected to fail with no writable ledger dir
assertTrue(true);
}
}
@Test(timeout=60000)
public void testNoWritableDir() throws Exception {
try {
dirsManager.addToFilledDirs(curDir);
dirsManager.pickRandomWritableDir();
fail("Should not reach here due to there is no writable ledger dir.");
} catch (NoWritableLedgerDirException nwlde) {
// expected to fail with no writable ledger dir
assertEquals("Should got NoWritableLedgerDirException w/ 'All ledger directories are non writable'.",
"All ledger directories are non writable", nwlde.getMessage());
}
}
@Test(timeout=60000)
public void testGetWritableDirForLog() throws Exception {
List<File> writeDirs;
try {
dirsManager.addToFilledDirs(curDir);
writeDirs = dirsManager.getWritableLedgerDirs();
fail("Should not reach here due to there is no writable ledger dir.");
} catch (NoWritableLedgerDirException nwlde) {
// expected to fail with no writable ledger dir
// Now make sure we can get one for log
try {
writeDirs = dirsManager.getWritableLedgerDirsForNewLog();
assertTrue("Must have a writable ledgerDir", writeDirs.size() > 0);
} catch (NoWritableLedgerDirException e) {
fail("We should have a writeble ledgerDir");
}
}
}
@Test(timeout=60000)
public void testLedgerDirsMonitorDuringTransition() throws Exception {
MockLedgerDirsListener mockLedgerDirsListener = new MockLedgerDirsListener();
dirsManager.addLedgerDirsListener(mockLedgerDirsListener);
dirsManager.start();
assertFalse(mockLedgerDirsListener.readOnly);
mockDiskChecker.setUsage(threshold + 0.05f);
Thread.sleep((diskCheckInterval * 2) + 100);
assertTrue(mockLedgerDirsListener.readOnly);
mockDiskChecker.setUsage(threshold - 0.05f);
Thread.sleep(diskCheckInterval + 100);
assertFalse(mockLedgerDirsListener.readOnly);
}
private class MockDiskChecker extends DiskChecker {
private float used;
public MockDiskChecker(float threshold, float warnThreshold) {
super(threshold, warnThreshold);
used = 0f;
}
@Override
public float checkDir(File dir) throws DiskErrorException, DiskOutOfSpaceException, DiskWarnThresholdException {
if (used > getDiskUsageThreshold()) {
throw new DiskOutOfSpaceException("", used);
}
if (used > getDiskUsageWarnThreshold()) {
throw new DiskWarnThresholdException("", used);
}
return used;
}
public void setUsage(float usage) {
this.used = usage;
}
}
private class MockLedgerDirsListener implements LedgerDirsListener {
public boolean readOnly;
public MockLedgerDirsListener() {
reset();
}
@Override
public void diskFailed(File disk) {
}
@Override
public void diskAlmostFull(File disk) {
}
@Override
public void diskFull(File disk) {
}
@Override
public void diskWritable(File disk) {
readOnly = false;
}
@Override
public void diskJustWritable(File disk) {
readOnly = false;
}
@Override
public void allDisksFull() {
readOnly = true;
}
@Override
public void fatalError() {
}
public void reset() {
readOnly = false;
}
}
}