HBase-2729
The problem lies in the method internalFlushCache, at HRegion. It writes directly to the target spot of the flushed data, regardless of the success of the data write.
Blocker.
IOException. Not for the failure itself (the flush). An exception could be thrown when the faulty file is written, but that is not part of the bug itself.
No.
No.
Large number of region files.
yes.
0.89.20100621
One cluster with one Region Server and an HMaster with a faulty file system.
1. Start writing something into the temporary file system (file write)
2. Throw any IOException and interrupt the procedure (e.g. disconnect the writing client), creating a corrupted file (disconnect)
3. Flush the data using the method internalFlushCache. (feature start)
In this order.
Yes.
No (they show the corrupted file, but do not show it was wrongly written to the disk, which is the wrong behavior).
One.
Simple.
An internalFlushCache was ran and completed successfully for a file that was corrupted.
HRegion.internalFlushCache writes directly to the target spot of the flushed data. The finally() block appends the metadata and closes the file as if nothing bad went wrong in case of an exception. This is really bad, since it means that an IOException in the middle of flushing cache could easily write a valid looking file with only half the data, which would then prevent us from recovering those edits during log replay.
No.
No.
No.
No.
Simple.
HRegion.internalFlushCache() writes directly to the memory and does not check the status of the write and also does not check for exceptions thrown. The correct logic is to first write the data to a temporary directory, and only if there is no exception thrown and writes succeed, write them to the actual directory.
Incorrect Error Handling (an IOException during the write would expose the failure!) But to test this, you need to thrown an error (and statement coverage is not enough)
The method internalFlushCash should write the data to a temporary directory first and check if the write was successful before moving to the permanent directory.
try{
- writer = createWriter(this.regionCompactionDir, maxKeyCount);
+ writer = createWriterInTmp(maxKeyCount,
+ this.compactionCompression);
} finally {
// Doesn’t care about if an exception is thrown...
if (writer != null) {
writer.appendMetadata(maxId, majorCompaction);
writer.close();
}
}