blob: 71d19a93ab7715befd4c46a399e015aa5c60114b [file] [log] [blame]
/*-
* Copyright (C) 2002, 2018, Oracle and/or its affiliates. All rights reserved.
*
* This file was distributed by Oracle as part of a version of Oracle Berkeley
* DB Java Edition made available at:
*
* http://www.oracle.com/technetwork/database/database-technologies/berkeleydb/downloads/index.html
*
* Please see the LICENSE file included in the top-level directory of the
* appropriate version of Oracle Berkeley DB Java Edition for a copy of the
* license and additional information.
*/
package com.sleepycat.je.log;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.util.Properties;
import com.sleepycat.je.log.entry.FileHeaderEntry;
import com.sleepycat.je.log.entry.LogEntry;
import com.sleepycat.je.log.entry.RestoreRequired;
import com.sleepycat.je.log.entry.SingleItemEntry;
/**
* A RestoreMarker is a file that indicates that a normal recovery is not
* possible, because the log files are physically or semantically inconsistent
* in some way.
*
* One example is an interrupted, incomplete network restore. The network
* restore copies log files from a source to destination node. If it's halted,
* while the destination node may contain files that are readable,
* checksum-able and seem correct, the set as a whole may not have a complete
* and coherent copy of the log. In such a case, recovery should not be run on
* this environment's log until some action is taken to make it consistent. For
* a network restore, this curative action is to restart the copy process, and
* to complete the copy of a set of logs from some node to this node. The
* network restore creates a marker file just before it does any form of change
* to the log that would make the log inconsistent.
*
* The restore marker file is named <i>MAXINT</i>.jdb, and holds a normal log
* file header and a RestoreRequired log entry. The RestoreRequired entry
* indicates the type of error of the initial cause, and information needed to
* repair the environment. The mechanism depends on the fact that the very
* first step of recovery is to read backwards from the last file in the
* environment. Recovery will start at this file, and will fail when it reads
* the RestoreRequired log entry, throwing an exception that contains
* prescriptive information for how the environment can be repaired.
*
* Note that createMarkerFile() is idempotent and can be safely called multiple
* times. It's done that way to make it simpler for the caller.
*
* The handler that repairs the log must also delete the marker file so future
* recoveries can succeed.
*/
public class RestoreMarker {
/**
* Internal exception used to distinguish marker file creation from other
* IOErrors.
*/
public static class FileCreationException extends Exception {
FileCreationException(String msg, Throwable cause) {
super(msg, cause);
}
}
private final LogManager logManager;
private final File lastFile;
public RestoreMarker(FileManager fileManager,
LogManager logManager) {
this.logManager = logManager;
String lastFileName = fileManager.getFullFileName(Integer.MAX_VALUE);
lastFile = new File(lastFileName);
}
public static String getMarkerFileName() {
return FileManager.getFileName(Integer.MAX_VALUE);
}
/**
* Remove the marker file. Use FileManager.delete so this file works with
* the FileDeletionDetector.
* @throws IOException if the file won't delete.
*/
public void removeMarkerFile(FileManager fileManager)
throws IOException {
if (lastFile.exists()) {
fileManager.deleteFile(Integer.MAX_VALUE);
}
}
/**
* Create the restore marker file.
*
* The method may be called repeatedly, but will not re-create the marker
* file if there's already a non-zero length file.
*
* @param failureType the failure type that should be recorded in the
* RestoreRequired log entry.
* @param props will be serialized to store information about how to handle
* the failure type.
* @throws FileCreationException if the marker file can't be created.
*/
public void createMarkerFile(RestoreRequired.FailureType failureType,
Properties props)
throws FileCreationException {
/* Don't overwrite the file if it already exists. */
if (lastFile.exists() && lastFile.length() > 0) {
return;
}
try {
lastFile.createNewFile();
/*
* The file will have two log entries:
* - a manufactured file header. Note that the file header usually
* has a previous offset that points to the previous log entry. In
* this case, it's set to 0 because we will never scan backwards
* from this file.
* - a RestoreRequired log entry
*/
FileHeader header = new FileHeader(Integer.MAX_VALUE, 0);
LogEntry headerLogEntry =
new FileHeaderEntry(LogEntryType.LOG_FILE_HEADER, header);
ByteBuffer buf1 = logManager.putIntoBuffer(headerLogEntry,
0); // prevLogEntryOffset
RestoreRequired rr = new RestoreRequired(failureType, props);
LogEntry marker =
SingleItemEntry.create(LogEntryType.LOG_RESTORE_REQUIRED, rr);
ByteBuffer buf2 = logManager.putIntoBuffer(marker, 0);
try (FileOutputStream stream = new FileOutputStream(lastFile);
FileChannel channel = stream.getChannel()) {
channel.write(buf1);
channel.write(buf2);
} catch (IOException e) {
/* the stream and channel will be closed */
throw e;
}
} catch (IOException ioe) {
throw new FileCreationException(
"Marker file creation failed for: " + failureType +
" " + ioe.toString(), ioe);
}
}
}