提交 b9652f97 authored 作者: Thomas Mueller Graf's avatar Thomas Mueller Graf

MVStoreTool: add repair feature

上级 8f2e41d1
......@@ -35,6 +35,8 @@ Change Log
</li>
<li>Mac OS X: Console tool process did not stop on exit.
</li>
<li>MVStoreTool: add "repair" feature.
</li>
</ul>
<h2>Version 1.4.192 Beta (2016-05-26)</h2>
......
......@@ -6,6 +6,7 @@
package org.h2.mvstore;
import java.io.IOException;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.io.Writer;
import java.nio.ByteBuffer;
......@@ -60,6 +61,9 @@ public class MVStoreTool {
} else if ("-compress".equals(args[i])) {
String fileName = args[++i];
compact(fileName, true);
} else if ("-repair".equals(args[i])) {
String fileName = args[++i];
repair(fileName);
}
}
}
......@@ -325,12 +329,13 @@ public class MVStoreTool {
*
* @param fileName the name of the file
* @param writer the print writer
* @return null if successful (if there was no error), otherwise the error message
*/
public static void info(String fileName, Writer writer) {
public static String info(String fileName, Writer writer) {
PrintWriter pw = new PrintWriter(writer, true);
if (!FilePath.get(fileName).exists()) {
pw.println("File not found: " + fileName);
return;
return "File not found: " + fileName;
}
long fileLength = FileUtils.size(fileName);
MVStore store = new MVStore.Builder().
......@@ -389,10 +394,12 @@ public class MVStoreTool {
} catch (Exception e) {
pw.println("ERROR: " + e);
e.printStackTrace(pw);
return e.getMessage();
} finally {
store.close();
}
pw.flush();
return null;
}
private static String formatTimestamp(long t, long start) {
......@@ -518,6 +525,137 @@ public class MVStoreTool {
}
}
/**
* Repair a store by rolling back to the newest good version.
*
* @param fileName the file name
*/
public static void repair(String fileName) {
PrintWriter pw = new PrintWriter(System.out);
long version = Long.MAX_VALUE;
OutputStream ignore = new OutputStream() {
@Override
public void write(int b) throws IOException {
// ignore
}
};
while (version >= 0) {
pw.println(version == Long.MAX_VALUE ? "Trying latest version" : ("Trying version " + version));
pw.flush();
version = rollback(fileName, version, new PrintWriter(ignore));
try {
String error = info(fileName + ".temp", new PrintWriter(ignore));
if (error == null) {
FilePath.get(fileName).moveTo(FilePath.get(fileName + ".back"), true);
FilePath.get(fileName + ".temp").moveTo(FilePath.get(fileName), true);
pw.println("Success");
break;
}
pw.println(" ... failed: " + error);
} catch (Exception e) {
pw.println("Fail: " + e.getMessage());
pw.flush();
}
version--;
}
pw.flush();
}
/**
* Roll back to a given revision into a a file called *.temp.
*
* @param fileName the file name
* @param targetVersion the version to roll back to (Long.MAX_VALUE for the latest version)
* @return the version rolled back to (-1 if no version)
*/
public static long rollback(String fileName, long targetVersion, Writer writer) {
long newestVersion = -1;
PrintWriter pw = new PrintWriter(writer, true);
if (!FilePath.get(fileName).exists()) {
pw.println("File not found: " + fileName);
return newestVersion;
}
FileChannel file = null;
FileChannel target = null;
int blockSize = MVStore.BLOCK_SIZE;
try {
file = FilePath.get(fileName).open("r");
FilePath.get(fileName + ".temp").delete();
target = FilePath.get(fileName + ".temp").open("rw");
long fileSize = file.size();
ByteBuffer block = ByteBuffer.allocate(4096);
Chunk newestChunk = null;
for (long pos = 0; pos < fileSize;) {
block.rewind();
DataUtils.readFully(file, pos, block);
block.rewind();
int headerType = block.get();
if (headerType == 'H') {
block.rewind();
target.write(block, pos);
pos += blockSize;
continue;
}
if (headerType != 'c') {
pos += blockSize;
continue;
}
Chunk c = null;
try {
c = Chunk.readChunkHeader(block, pos);
} catch (IllegalStateException e) {
pos += blockSize;
continue;
}
if (c.len <= 0) {
// not a chunk
pos += blockSize;
continue;
}
int length = c.len * MVStore.BLOCK_SIZE;
ByteBuffer chunk = ByteBuffer.allocate(length);
DataUtils.readFully(file, pos, chunk);
if (c.version > targetVersion) {
// newer than the requested version
pos += length;
continue;
}
chunk.rewind();
target.write(chunk, pos);
if (newestChunk == null || c.version > newestChunk.version) {
newestChunk = c;
newestVersion = c.version;
}
pos += length;
}
int length = newestChunk.len * MVStore.BLOCK_SIZE;
ByteBuffer chunk = ByteBuffer.allocate(length);
DataUtils.readFully(file, newestChunk.block * MVStore.BLOCK_SIZE, chunk);
chunk.rewind();
target.write(chunk, fileSize);
} catch (IOException e) {
pw.println("ERROR: " + e);
e.printStackTrace(pw);
} finally {
if (file != null) {
try {
file.close();
} catch (IOException e) {
// ignore
}
}
if (target != null) {
try {
target.close();
} catch (IOException e) {
// ignore
}
}
}
pw.flush();
return newestVersion;
}
/**
* A data type that can read any data that is persisted, and converts it to
* a byte array.
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论