diff --git a/CHANGES.txt b/CHANGES.txt
index 0068c6ccdf5..16fc5d8c8ce 100644
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -16,6 +16,8 @@ Trunk (unreleased changes)
HADOOP-1729 Recent renaming or META tables breaks hbase shell
HADOOP-1730 unexpected null value causes META scanner to exit (silently)
HADOOP-1747 On a cluster, on restart, regions multiply assigned
+ HADOOP-1776 Fix for sporadic compaction failures closing and moving
+ compaction result
IMPROVEMENTS
HADOOP-1737 Make HColumnDescriptor data publically members settable
diff --git a/src/java/org/apache/hadoop/hbase/HConnectionManager.java b/src/java/org/apache/hadoop/hbase/HConnectionManager.java
index 089f5395c57..1f7172a4b68 100644
--- a/src/java/org/apache/hadoop/hbase/HConnectionManager.java
+++ b/src/java/org/apache/hadoop/hbase/HConnectionManager.java
@@ -679,7 +679,7 @@ public class HConnectionManager implements HConstants {
// We found at least one server for the table and now we're done.
if (LOG.isDebugEnabled()) {
- LOG.debug("Found " + servers.size() + " server(s) for " +
+ LOG.debug("Found " + servers.size() + " region(s) for " +
tableName + " at " + t);
}
break;
diff --git a/src/java/org/apache/hadoop/hbase/HLog.java b/src/java/org/apache/hadoop/hbase/HLog.java
index ae9bf513d07..0b96f66083d 100644
--- a/src/java/org/apache/hadoop/hbase/HLog.java
+++ b/src/java/org/apache/hadoop/hbase/HLog.java
@@ -40,8 +40,8 @@ import java.util.concurrent.atomic.AtomicInteger;
*
*
Each HRegion is identified by a unique long int
. HRegions do
* not need to declare themselves before using the HLog; they simply include
- * their HRegion-id in the {@link #append(Text, Text, Text, TreeMap, long)} or
- * {@link #completeCacheFlush(Text, Text, long)} calls.
+ * their HRegion-id in the append
or
+ * completeCacheFlush
calls.
*
*
An HLog consists of multiple on-disk files, which have a chronological
* order. As data is flushed to other (better) on-disk structures, the log
@@ -107,6 +107,12 @@ public class HLog implements HConstants {
if (LOG.isDebugEnabled()) {
LOG.debug("Splitting " + logfiles[i]);
}
+ // Check for empty file.
+ if (fs.getFileStatus(logfiles[i]).getLen() <= 0) {
+ LOG.warn("Skipping " + logfiles[i].toString() +
+ " because zero length");
+ continue;
+ }
SequenceFile.Reader in =
new SequenceFile.Reader(fs, logfiles[i], conf);
try {
diff --git a/src/java/org/apache/hadoop/hbase/HMaster.java b/src/java/org/apache/hadoop/hbase/HMaster.java
index f91e25f2abc..58cf4f981fe 100644
--- a/src/java/org/apache/hadoop/hbase/HMaster.java
+++ b/src/java/org/apache/hadoop/hbase/HMaster.java
@@ -305,10 +305,6 @@ HMasterRegionInterface, Runnable {
SortedMap rowContent)
throws IOException {
boolean result = false;
- if (LOG.isDebugEnabled()) {
- LOG.debug("Checking " + parent.getRegionName() +
- " to see if daughter splits still hold references");
- }
boolean hasReferencesA = hasReferences(metaRegionName, srvr,
parent.getRegionName(), rowContent, COL_SPLITA);
@@ -318,7 +314,6 @@ HMasterRegionInterface, Runnable {
if (!hasReferencesA && !hasReferencesB) {
LOG.info("Deleting region " + parent.getRegionName() +
" because daughter splits no longer hold references");
-
if (!HRegion.deleteRegion(fs, dir, parent.getRegionName())) {
LOG.warn("Deletion of " + parent.getRegionName() + " failed");
}
@@ -330,11 +325,11 @@ HMasterRegionInterface, Runnable {
b.delete(lockid, COL_STARTCODE);
srvr.batchUpdate(metaRegionName, System.currentTimeMillis(), b);
result = true;
- }
-
- if (LOG.isDebugEnabled()) {
- LOG.debug("Done checking " + parent.getRegionName() + ": splitA: " +
- hasReferencesA + ", splitB: "+ hasReferencesB);
+ } else if (LOG.isDebugEnabled()) {
+ // If debug, note we checked and current state of daughters.
+ LOG.debug("Checked " + parent.getRegionName() +
+ " for references: splitA: " + hasReferencesA + ", splitB: "+
+ hasReferencesB);
}
return result;
}
diff --git a/src/java/org/apache/hadoop/hbase/HRegion.java b/src/java/org/apache/hadoop/hbase/HRegion.java
index 16d500c1c24..0e98a11a090 100644
--- a/src/java/org/apache/hadoop/hbase/HRegion.java
+++ b/src/java/org/apache/hadoop/hbase/HRegion.java
@@ -649,7 +649,7 @@ public class HRegion implements HConstants {
for (HStore store: stores.values()) {
if (store.needsCompaction()) {
needsCompaction = true;
- LOG.info(store.toString() + " needs compaction");
+ LOG.debug(store.toString() + " needs compaction");
break;
}
}
@@ -1628,7 +1628,7 @@ public class HRegion implements HConstants {
/**
* Computes the Path of the HRegion
*
- * @param dir parent directory
+ * @param dir hbase home directory
* @param regionName name of the region
* @return Path of HRegion directory
*/
diff --git a/src/java/org/apache/hadoop/hbase/HStore.java b/src/java/org/apache/hadoop/hbase/HStore.java
index 1fc087430b8..d76b0ba46b8 100644
--- a/src/java/org/apache/hadoop/hbase/HStore.java
+++ b/src/java/org/apache/hadoop/hbase/HStore.java
@@ -62,8 +62,6 @@ import org.onelab.filter.RetouchedBloomFilter;
class HStore implements HConstants {
static final Log LOG = LogFactory.getLog(HStore.class);
- static final String COMPACTION_DIR = "compaction.tmp";
- static final String WORKING_COMPACTION = "compaction.inprogress";
static final String COMPACTION_TO_REPLACE = "toreplace";
static final String COMPACTION_DONE = "done";
@@ -77,11 +75,11 @@ class HStore implements HConstants {
FileSystem fs;
Configuration conf;
Path mapdir;
- Path compactdir;
Path loginfodir;
Path filterDir;
Filter bloomFilter;
private String storeName;
+ private final Path compactionDir;
Integer compactLock = new Integer(0);
Integer flushLock = new Integer(0);
@@ -133,6 +131,7 @@ class HStore implements HConstants {
FileSystem fs, Path reconstructionLog, Configuration conf)
throws IOException {
this.dir = dir;
+ this.compactionDir = new Path(dir, "compaction.dir");
this.regionName = regionName;
this.family = family;
this.familyName = HStoreKey.extractFamily(this.family.getName());
@@ -172,17 +171,6 @@ class HStore implements HConstants {
" (no reconstruction log)": " with reconstruction log: " +
reconstructionLog.toString()));
}
-
- // Either restart or get rid of any leftover compaction work. Either way,
- // by the time processReadyCompaction() returns, we can get rid of the
- // existing compaction-dir.
- this.compactdir = new Path(dir, COMPACTION_DIR);
- Path curCompactStore =
- HStoreFile.getHStoreDir(compactdir, regionName, familyName);
- if(fs.exists(curCompactStore)) {
- processReadyCompaction();
- fs.delete(curCompactStore);
- }
// Go through the 'mapdir' and 'loginfodir' together, make sure that all
// MapFiles are in a reliable state. Every entry in 'mapdir' must have a
@@ -409,7 +397,7 @@ class HStore implements HConstants {
this.readers.clear();
result = new Vector(storefiles.values());
this.storefiles.clear();
- LOG.info("closed " + this.storeName);
+ LOG.debug("closed " + this.storeName);
return result;
} finally {
this.lock.releaseWriteLock();
@@ -563,7 +551,7 @@ class HStore implements HConstants {
throws IOException {
compactHelper(deleteSequenceInfo, -1);
}
-
+
/*
* @param deleteSequenceInfo True if we are to set the sequence number to -1
* on compacted file.
@@ -577,23 +565,22 @@ class HStore implements HConstants {
long maxId = maxSeenSeqID;
synchronized(compactLock) {
Path curCompactStore =
- HStoreFile.getHStoreDir(compactdir, regionName, familyName);
+ HStoreFile.getHStoreDir(this.compactionDir, regionName, familyName);
if(LOG.isDebugEnabled()) {
LOG.debug("started compaction of " + storefiles.size() + " files in " +
curCompactStore.toString());
}
- try {
- // Grab a list of files to compact.
- Vector toCompactFiles = null;
- this.lock.obtainWriteLock();
- try {
- toCompactFiles = new Vector(storefiles.values());
- } finally {
- this.lock.releaseWriteLock();
+ if (this.fs.exists(curCompactStore)) {
+ LOG.warn("Cleaning up a previous incomplete compaction at " +
+ curCompactStore.toString());
+ if (!this.fs.delete(curCompactStore)) {
+ LOG.warn("Deleted returned false on " + curCompactStore.toString());
}
-
+ }
+ try {
+ Vector toCompactFiles = getFilesToCompact();
HStoreFile compactedOutputFile =
- new HStoreFile(conf, compactdir, regionName, familyName, -1);
+ new HStoreFile(conf, this.compactionDir, regionName, familyName, -1);
if (toCompactFiles.size() < 1 ||
(toCompactFiles.size() == 1 &&
!toCompactFiles.get(0).isReference())) {
@@ -606,7 +593,9 @@ class HStore implements HConstants {
return;
}
- fs.mkdirs(curCompactStore);
+ if (!fs.mkdirs(curCompactStore)) {
+ LOG.warn("Mkdir on " + curCompactStore.toString() + " failed");
+ }
// Compute the max-sequenceID seen in any of the to-be-compacted
// TreeMaps if it hasn't been passed in to us.
@@ -657,13 +646,31 @@ class HStore implements HConstants {
// Move the compaction into place.
processReadyCompaction();
} finally {
- if (fs.exists(compactdir)) {
- fs.delete(compactdir);
+ // Clean up the parent -- the region dir in the compactions directory.
+ if (this.fs.exists(curCompactStore.getParent())) {
+ if (!this.fs.delete(curCompactStore.getParent())) {
+ LOG.warn("Delete returned false deleting " +
+ curCompactStore.getParent().toString());
+ }
}
}
}
}
+ /*
+ * @return list of files to compact
+ */
+ private Vector getFilesToCompact() {
+ Vector toCompactFiles = null;
+ this.lock.obtainWriteLock();
+ try {
+ toCompactFiles = new Vector(storefiles.values());
+ } finally {
+ this.lock.releaseWriteLock();
+ }
+ return toCompactFiles;
+ }
+
/*
* Compact passed toCompactFiles
into compactedOut
.
* We create a new set of MapFile.Reader objects so we don't screw up
@@ -886,33 +893,34 @@ class HStore implements HConstants {
}
}
- /**
+ /*
* It's assumed that the compactLock will be acquired prior to calling this
* method! Otherwise, it is not thread-safe!
*
* It works by processing a compaction that's been written to disk.
*
- * It is usually invoked at the end of a compaction, but might also be
+ * It is usually invoked at the end of a compaction, but might also be
* invoked at HStore startup, if the prior execution died midway through.
+ *
+ *
Moving the compacted TreeMap into place means:
+ *
+ * 1) Acquiring the write-lock
+ * 2) Figuring out what MapFiles are going to be replaced
+ * 3) Moving the new compacted MapFile into place
+ * 4) Unloading all the replaced MapFiles.
+ * 5) Deleting all the old MapFile files.
+ * 6) Loading the new TreeMap.
+ * 7) Releasing the write-lock
+ *
*/
void processReadyCompaction() throws IOException {
- // Move the compacted TreeMap into place.
- // That means:
- // 1) Acquiring the write-lock
- // 2) Figuring out what MapFiles are going to be replaced
- // 3) Unloading all the replaced MapFiles.
- // 4) Deleting all the old MapFile files.
- // 5) Moving the new MapFile into place
- // 6) Loading the new TreeMap.
- // 7) Releasing the write-lock
-
// 1. Acquiring the write-lock
Path curCompactStore =
- HStoreFile.getHStoreDir(compactdir, regionName, familyName);
+ HStoreFile.getHStoreDir(this.compactionDir, regionName, familyName);
this.lock.obtainWriteLock();
try {
Path doneFile = new Path(curCompactStore, COMPACTION_DONE);
- if(!fs.exists(doneFile)) {
+ if (!fs.exists(doneFile)) {
// The last execution didn't finish the compaction, so there's nothing
// we can do. We'll just have to redo it. Abandon it and return.
LOG.warn("Redoing a failed compaction");
@@ -920,7 +928,6 @@ class HStore implements HConstants {
}
// 2. Load in the files to be deleted.
- // (Figuring out what MapFiles are going to be replaced)
Vector toCompactFiles = new Vector();
Path filesToReplace = new Path(curCompactStore, COMPACTION_TO_REPLACE);
DataInputStream in = new DataInputStream(fs.open(filesToReplace));
@@ -936,41 +943,16 @@ class HStore implements HConstants {
in.close();
}
- // 3. Unload all the replaced MapFiles. Do it by getting keys of all
- // to remove. Then cycling on keys, removing, closing and deleting.
-
- // What if we crash at this point? No big deal; we will restart
- // processReadyCompaction(), and nothing has been lost.
- Vector keys = new Vector(toCompactFiles.size());
- for(Map.Entry e: storefiles.entrySet()) {
- if(toCompactFiles.contains(e.getValue())) {
- keys.add(e.getKey());
- }
- }
-
- Vector toDelete = new Vector(keys.size());
- for (Long key: keys) {
- MapFile.Reader reader = this.readers.remove(key);
- if (reader != null) {
- reader.close();
- }
- HStoreFile hsf = this.storefiles.remove(key);
- // 4. Add to the toDelete files all old files, no longer needed
- toDelete.add(hsf);
- }
-
- // What if we fail now? The above deletes will fail silently. We'd
- // better make sure not to write out any new files with the same names as
- // something we delete, though.
-
- // 5. Moving the new MapFile into place
+ // 3. Moving the new MapFile into place.
HStoreFile compactedFile
- = new HStoreFile(conf, compactdir, regionName, familyName, -1);
+ = new HStoreFile(conf, this.compactionDir, regionName, familyName, -1);
+ // obtainNewHStoreFile does its best to generate a filename that does not
+ // currently exist.
HStoreFile finalCompactedFile
= HStoreFile.obtainNewHStoreFile(conf, dir, regionName, familyName, fs);
if(LOG.isDebugEnabled()) {
LOG.debug("moving " + compactedFile.toString() + " in " +
- compactdir.toString() +
+ this.compactionDir.toString() +
" to " + finalCompactedFile.toString() + " in " + dir.toString());
}
if (!compactedFile.rename(this.fs, finalCompactedFile)) {
@@ -978,24 +960,37 @@ class HStore implements HConstants {
finalCompactedFile.toString());
return;
}
-
- // Safe to delete now compaction has been moved into place.
- for (HStoreFile hsf: toDelete) {
- if (hsf.getFileId() == finalCompactedFile.getFileId()) {
- // Be careful we do not delte the just compacted file.
- LOG.warn("Weird. File to delete has same name as one we are " +
- "about to delete (skipping): " + hsf.getFileId());
+
+ // 4. and 5. Unload all the replaced MapFiles, close and delete.
+ Vector toDelete = new Vector(toCompactFiles.size());
+ for (Map.Entry e: this.storefiles.entrySet()) {
+ if (!toCompactFiles.contains(e.getValue())) {
continue;
}
- hsf.delete();
+ Long key = e.getKey();
+ MapFile.Reader reader = this.readers.remove(key);
+ if (reader != null) {
+ reader.close();
+ }
+ toDelete.add(key);
}
+
+ try {
+ for (Long key: toDelete) {
+ HStoreFile hsf = this.storefiles.remove(key);
+ hsf.delete();
+ }
- Long orderVal = Long.valueOf(finalCompactedFile.loadInfo(fs));
-
- // 6. Loading the new TreeMap.
- this.readers.put(orderVal,
- finalCompactedFile.getReader(this.fs, this.bloomFilter));
- this.storefiles.put(orderVal, finalCompactedFile);
+ // 6. Loading the new TreeMap.
+ Long orderVal = Long.valueOf(finalCompactedFile.loadInfo(fs));
+ this.readers.put(orderVal,
+ finalCompactedFile.getReader(this.fs, this.bloomFilter));
+ this.storefiles.put(orderVal, finalCompactedFile);
+ } finally {
+ LOG.warn("Failed replacing compacted files. Compacted fle is " +
+ finalCompactedFile.toString() + ". Files replaced are " +
+ toCompactFiles.toString() + " some of which may have been removed");
+ }
} finally {
// 7. Releasing the write-lock
this.lock.releaseWriteLock();
diff --git a/src/java/org/apache/hadoop/hbase/HStoreFile.java b/src/java/org/apache/hadoop/hbase/HStoreFile.java
index a9e3c15c6f0..4ed76690ef1 100644
--- a/src/java/org/apache/hadoop/hbase/HStoreFile.java
+++ b/src/java/org/apache/hadoop/hbase/HStoreFile.java
@@ -23,6 +23,7 @@ import java.io.DataInput;
import java.io.DataInputStream;
import java.io.DataOutput;
import java.io.DataOutputStream;
+import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.util.Random;
@@ -608,7 +609,6 @@ public class HStoreFile implements HConstants, WritableComparable {
try {
out.writeByte(INFO_SEQ_NUM);
out.writeLong(infonum);
-
} finally {
out.close();
}
@@ -637,16 +637,22 @@ public class HStoreFile implements HConstants, WritableComparable {
*/
public boolean rename(final FileSystem fs, final HStoreFile hsf)
throws IOException {
- boolean success = fs.rename(getMapFilePath(), hsf.getMapFilePath());
+ Path src = getMapFilePath();
+ if (!fs.exists(src)) {
+ throw new FileNotFoundException(src.toString());
+ }
+ boolean success = fs.rename(src, hsf.getMapFilePath());
if (!success) {
- LOG.warn("Failed rename of " + getMapFilePath() + " to " +
- hsf.getMapFilePath());
+ LOG.warn("Failed rename of " + src + " to " + hsf.getMapFilePath());
return success;
}
- success = fs.rename(getInfoFilePath(), hsf.getInfoFilePath());
+ src = getInfoFilePath();
+ if (!fs.exists(src)) {
+ throw new FileNotFoundException(src.toString());
+ }
+ success = fs.rename(src, hsf.getInfoFilePath());
if (!success) {
- LOG.warn("Failed rename of " + getInfoFilePath() + " to " +
- hsf.getInfoFilePath());
+ LOG.warn("Failed rename of " + src + " to " + hsf.getInfoFilePath());
}
return success;
}