blob: 9da6fbd2b6d949b40638a7228831d9e7e80ad36c [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.util.verify;
import static com.sleepycat.je.config.EnvironmentParams.VERIFY_BTREE_BATCH_DELAY;
import static com.sleepycat.je.config.EnvironmentParams.VERIFY_BTREE_BATCH_SIZE;
import java.util.concurrent.TimeUnit;
import java.util.logging.Logger;
import com.sleepycat.je.VerifyConfig;
import com.sleepycat.je.config.EnvironmentParams;
import com.sleepycat.je.dbi.DbConfigManager;
import com.sleepycat.je.dbi.DbTree;
import com.sleepycat.je.dbi.EnvironmentImpl;
import com.sleepycat.je.utilint.LoggerUtils;
import com.sleepycat.je.utilint.PollCondition;
import com.sleepycat.je.utilint.StoppableThread;
/**
* See {@link EnvironmentParams#AUTO_RESERVED_FILE_REPAIR}.
*/
public class ReservedFilesAutoRepair extends StoppableThread {
/* Is non-final so it can be set by tests. */
public static int START_DELAY_S = 90;
private final BtreeVerifier verifier;
private boolean enabled;
private boolean noThrottle;
private boolean initialized;
private volatile boolean running;
private volatile boolean completed;
public ReservedFilesAutoRepair(final EnvironmentImpl envImpl) {
super(envImpl, "RepairReservedFiles-" + envImpl.getName());
verifier = new BtreeVerifier(envImpl);
}
/**
* Returns true if this thread will perform auto-repair, or false if the
* background DataVerifier should be configured as usual.
*
* <p>If true is returned, the background DataVerifier will be configured
* as usual by this thread after the auto-repair is complete.</p>
*
* <p>This method's implementation is fairly simple due to the
* following:
* <ul>
* <li>The caller, {@link EnvironmentImpl#doSetMutableConfig}, is
* synchronized.</li>
*
* <li>The first call, when 'initialized' is false, is made before
* the Environment ctor returns.</li>
*
* <li>The repair param is immutable.</li>
* </ul></p>
*
* @throws IllegalArgumentException if the repair param is invalid.
*/
public boolean startOrCheck(final DbConfigManager configMgr) {
if (initialized) {
/*
* We were previously initialized and now we're being called via
* Environment.setMutableConfig. If 'running' is false then either
* repair was unnecessary or has been completed, and we should
* return false to allow the background DataVerifier to run.
*/
return running;
}
initialized = true;
final DbTree dbTree = envImpl.getDbTree();
parseConfig(configMgr);
if (!enabled) {
/*
* When auto-repair is disabled, clear the repair-done flag if it
* was previously set. This allows repeating repair after it was
* previously completed.
*/
if (dbTree.isAutoRepairReservedFilesDone()) {
dbTree.clearAutoRepairReservedFilesDone();
dirtyDbTree(dbTree);
}
return false;
}
/*
* Auto-repair is enabled. Return false if auto-repair was previously
* completed.
*/
if (dbTree.isAutoRepairReservedFilesDone()) {
return false;
}
/*
* Auto-repair is enabled and should be run. Configure verify to
* repair reserved files and start the thread.
*/
final VerifyConfig verifyConfig = new VerifyConfig();
verifyConfig.setRepairReservedFiles(true);
verifyConfig.setBatchSize(configMgr.getInt(VERIFY_BTREE_BATCH_SIZE));
if (noThrottle) {
verifyConfig.setBatchDelay(0, TimeUnit.MILLISECONDS);
} else {
verifyConfig.setBatchDelay(
configMgr.getDuration(VERIFY_BTREE_BATCH_DELAY),
TimeUnit.MILLISECONDS);
}
verifier.setBtreeVerifyConfig(verifyConfig);
running = true;
start();
return true;
}
public boolean isCompleted() {
return completed;
}
public boolean isRunning() {
return running;
}
@Override
public Logger getLogger() {
return envImpl.getLogger();
}
public void requestShutdown() {
verifier.setStopVerifyFlag(true);
}
@Override
protected int initiateSoftShutdown() {
requestShutdown();
return (int) TimeUnit.SECONDS.toMillis(5);
}
@Override
public void run() {
try {
/*
* Give the app time to initialize its metadata to avoid a
* MAYBE_EXTINCT return from the extinction filter.
*/
if (PollCondition.await(
50, TimeUnit.SECONDS.toMillis(START_DELAY_S),
verifier::getStopVerifyFlag)) {
return;
}
/*
* Call verifyAll. The repair is incomplete if an exception is
* thrown or hasRepairWarnings returns true.
*/
int exCount = 0;
boolean success;
try {
verifier.verifyAll();
success = !verifier.hasRepairWarnings();
} catch (RuntimeException e) {
success = false;
exCount = 1;
/* Exception is not logged by the verifier, log it here. */
LoggerUtils.severe(
getLogger(), envImpl, LoggerUtils.getStackTrace(e));
}
/*
* If the repair is complete, log an info message and set the
* repair-done flag, else log a warning.
*/
if (success) {
LoggerUtils.info(
getLogger(), envImpl, "Reserved file repair complete");
final DbTree dbTree = envImpl.getDbTree();
dbTree.setAutoRepairReservedFilesDone();
dirtyDbTree(dbTree);
} else {
LoggerUtils.warning(
getLogger(), envImpl,
"Reserved file repair not complete." +
" lockConflicts=" + verifier.getRepairLockConflicts() +
" maybeExtinct=" + verifier.getRepairMaybeExtinct() +
" runtimeExceptions=" +
(verifier.getRepairRuntimeExceptions() + exCount));
}
/* Resume normal background verifier operation. */
envImpl.getDataVerifier().configVerifyTask(
envImpl.getConfigManager());
completed = true;
} finally {
running = false;
}
}
/**
* Dirtying either the ID or the Name DB will cause the next checkpoint to
* log the DbRoot, which will make the repair-done flag durable.
*/
private void dirtyDbTree(final DbTree dbTree) {
dbTree.getIdDatabaseImpl().setDirty();
}
/**
* Use repair param to set enabled and noThrottle fields.
*
* @throws IllegalArgumentException if the param is invalid.
*/
private void parseConfig(final DbConfigManager configMgr) {
final String paramName =
EnvironmentParams.AUTO_RESERVED_FILE_REPAIR.getName();
final String param = configMgr.get(
EnvironmentParams.AUTO_RESERVED_FILE_REPAIR);
if (param == null) {
throw new IllegalArgumentException(paramName + " is null");
}
enabled = false;
noThrottle = false;
switch (param) {
case "off":
break;
case "on":
enabled = true;
break;
case "on.noThrottle":
enabled = true;
noThrottle = true;
break;
default:
throw new IllegalArgumentException(
paramName + " is not 'off', 'on' or 'on.noThrottle': " +
param);
}
}
}