Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@

package org.apache.hadoop.yarn.server.timeline;

import org.apache.commons.io.FileUtils;

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would you move the import between

import org.apache.commons.collections.map.LRUMap;

and

import org.apache.commons.lang3.StringUtils;

?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

import org.apache.hadoop.classification.VisibleForTesting;
import org.apache.hadoop.util.Preconditions;

Expand Down Expand Up @@ -48,6 +49,7 @@
import org.apache.hadoop.io.IOUtils;
import org.apache.hadoop.io.WritableComparator;
import org.apache.hadoop.service.AbstractService;
import org.apache.hadoop.util.Time;
import org.apache.hadoop.yarn.api.records.timeline.TimelineDomain;
import org.apache.hadoop.yarn.api.records.timeline.TimelineDomains;
import org.apache.hadoop.yarn.api.records.timeline.TimelineEntities;
Expand Down Expand Up @@ -199,6 +201,11 @@ public class RollingLevelDBTimelineStore extends AbstractService implements
static final String STARTTIME = "starttime-ldb";
static final String OWNER = "owner-ldb";

@VisibleForTesting
//Extension to FILENAME where backup will be stored in case we need to
//call LevelDb recovery
static final String BACKUP_EXT = ".backup-";

private static final byte[] DOMAIN_ID_COLUMN = "d".getBytes(UTF_8);
private static final byte[] EVENTS_COLUMN = "e".getBytes(UTF_8);
private static final byte[] PRIMARY_FILTERS_COLUMN = "f".getBytes(UTF_8);
Expand Down Expand Up @@ -240,6 +247,12 @@ public RollingLevelDBTimelineStore() {
super(RollingLevelDBTimelineStore.class.getName());
}

private JniDBFactory factory;
@VisibleForTesting
void setFactory(JniDBFactory fact) {
this.factory = fact;
}

@Override
@SuppressWarnings("unchecked")
protected void serviceInit(Configuration conf) throws Exception {
Expand Down Expand Up @@ -284,7 +297,9 @@ protected void serviceInit(Configuration conf) throws Exception {
options.cacheSize(conf.getLong(
TIMELINE_SERVICE_LEVELDB_READ_CACHE_SIZE,
DEFAULT_TIMELINE_SERVICE_LEVELDB_READ_CACHE_SIZE));
JniDBFactory factory = new JniDBFactory();
if(factory == null) {
factory = new JniDBFactory();
}
Path dbPath = new Path(
conf.get(TIMELINE_SERVICE_LEVELDB_PATH), FILENAME);
Path domainDBPath = new Path(dbPath, DOMAIN);
Expand Down Expand Up @@ -327,13 +342,46 @@ protected void serviceInit(Configuration conf) throws Exception {
TIMELINE_SERVICE_LEVELDB_WRITE_BUFFER_SIZE,
DEFAULT_TIMELINE_SERVICE_LEVELDB_WRITE_BUFFER_SIZE));
LOG.info("Using leveldb path " + dbPath);
domaindb = factory.open(new File(domainDBPath.toString()), options);
try{
domaindb = factory.open(new File(domainDBPath.toString()), options);
} catch (IOException ioe){
File domaindbFile = new File(domainDBPath.toString());
File domaindbBackupPath = new File(
domainDBPath.toString() + BACKUP_EXT + Time.monotonicNow());
LOG.warn("Incurred exception while loading LevelDb database. Backing " +
"up at "+ domaindbBackupPath, ioe);
FileUtils.copyDirectory(domaindbFile, domaindbBackupPath);
factory.repair(domaindbFile, options);
domaindb = factory.open(domaindbFile, options);
}
entitydb = new RollingLevelDB(ENTITY);
entitydb.init(conf);
indexdb = new RollingLevelDB(INDEX);
indexdb.init(conf);
starttimedb = factory.open(new File(starttimeDBPath.toString()), options);
ownerdb = factory.open(new File(ownerDBPath.toString()), options);
try{
starttimedb = factory.open(new File(starttimeDBPath.toString()), options);
} catch (IOException ioe){
File starttimedbFile = new File(starttimeDBPath.toString());
File starttimeBackupPath = new File(
starttimeDBPath.toString() + BACKUP_EXT + Time.monotonicNow());
LOG.warn("Incurred exception while loading LevelDb database. Backing " +
"up at "+ starttimeBackupPath, ioe);
FileUtils.copyDirectory(starttimedbFile, starttimeBackupPath);
factory.repair(starttimedbFile, options);
starttimedb = factory.open(starttimedbFile, options);
}
try{
ownerdb = factory.open(new File(ownerDBPath.toString()), options);
} catch (IOException ioe){
File ownerdbFile = new File(ownerDBPath.toString());
File ownerdbBackupPath = new File(
ownerDBPath.toString() + BACKUP_EXT + Time.monotonicNow());
LOG.warn("Incurred exception while loading LevelDb database. Backing " +
"up at "+ ownerdbBackupPath, ioe);
FileUtils.copyDirectory(ownerdbFile, ownerdbBackupPath);
factory.repair(ownerdbFile, options);
ownerdb = factory.open(ownerdbFile, options);
}
checkVersion();
startTimeWriteCache = Collections.synchronizedMap(new LRUMap(
getStartTimeWriteCacheSize(conf)));
Expand All @@ -346,7 +394,7 @@ protected void serviceInit(Configuration conf) throws Exception {

super.serviceInit(conf);
}

@Override
protected void serviceStart() throws Exception {
if (getConfig().getBoolean(TIMELINE_SERVICE_TTL_ENABLE, true)) {
Expand Down Expand Up @@ -1816,4 +1864,4 @@ private static TimelineDomain getTimelineDomain(DBIterator iterator,
return domain;
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,10 @@
import static org.junit.Assert.assertNotNull;

import java.io.File;
import java.io.FilenameFilter;
import java.io.IOException;

import org.apache.commons.io.filefilter.WildcardFileFilter;
import org.apache.hadoop.classification.InterfaceAudience;
import org.apache.hadoop.classification.InterfaceStability;
import org.apache.hadoop.conf.Configuration;
Expand All @@ -38,11 +40,15 @@
import org.apache.hadoop.yarn.api.records.timeline.TimelinePutResponse.TimelinePutError;
import org.apache.hadoop.yarn.conf.YarnConfiguration;
import org.apache.hadoop.yarn.server.records.Version;

import org.fusesource.leveldbjni.JniDBFactory;
import org.iq80.leveldb.Options;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.eclipse.jetty.util.log.Log;
import org.mockito.Mockito;

/** Test class to verify RollingLevelDBTimelineStore. */
@InterfaceAudience.Private
Expand Down Expand Up @@ -417,11 +423,41 @@ public void testStorePerformance() throws IOException {
Log.getLog().info("Duration for " + num + ": " + duration);
}

@Test
/**
* Test that RollingLevelDb repair is attempted at least once during
* serviceInit for RollingLeveldbTimelineStore in case open fails the
* first time.
*/ public void testLevelDbRepair() throws IOException {
RollingLevelDBTimelineStore store = new RollingLevelDBTimelineStore();
JniDBFactory factory = Mockito.mock(JniDBFactory.class);
Mockito.when(factory.open(Mockito.any(File.class), Mockito.any(Options.class)))
.thenThrow(new IOException()).thenCallRealMethod();
store.setFactory(factory);

//Create the LevelDb in a different location
File path = new File("target", this.getClass().getSimpleName() + "-tmpDir2").getAbsoluteFile();
Configuration conf = new Configuration(this.config);
conf.set(YarnConfiguration.TIMELINE_SERVICE_LEVELDB_PATH, path.getAbsolutePath());
try {
store.init(conf);
Mockito.verify(factory, Mockito.times(1))
.repair(Mockito.any(File.class), Mockito.any(Options.class));
FilenameFilter fileFilter =
new WildcardFileFilter("*" + RollingLevelDBTimelineStore.BACKUP_EXT + "*");
Assert.assertTrue(new File(path.getAbsolutePath(), RollingLevelDBTimelineStore.FILENAME)
.list(fileFilter).length > 0);
} finally {
store.close();
fsContext.delete(new Path(path.getAbsolutePath()), true);
}
}

public static void main(String[] args) throws Exception {
TestRollingLevelDBTimelineStore store =
new TestRollingLevelDBTimelineStore();
store.setup();
store.testStorePerformance();
store.tearDown();
}
}
}