blob: d2d0130738507d3e0ea795af4e91261ae569b6b8 [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;
import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStreamReader;
import java.text.DecimalFormat;
import com.sleepycat.je.CheckpointConfig;
import com.sleepycat.je.Cursor;
import com.sleepycat.je.Database;
import com.sleepycat.je.DatabaseEntry;
import com.sleepycat.je.DatabaseException;
import com.sleepycat.je.DbInternal;
import com.sleepycat.je.Environment;
import com.sleepycat.je.EnvironmentConfig;
import com.sleepycat.je.EnvironmentMutableConfig;
import com.sleepycat.je.EnvironmentStats;
import com.sleepycat.je.LockMode;
import com.sleepycat.je.OperationStatus;
import com.sleepycat.je.StatsConfig;
import com.sleepycat.je.Transaction;
import com.sleepycat.je.cleaner.VerifyUtils;
import com.sleepycat.je.config.EnvironmentParams;
import com.sleepycat.je.dbi.EnvironmentImpl;
import com.sleepycat.je.utilint.CmdUtil;
/**
* @hidden
* For internal use only.
* DbRunAction is a debugging aid that can invoke a JE operation or background
* activity from the command line.
*
* batchClean calls Environment.cleanLog() in a loop
* checkpoint calls Environment.checkpoint() with force=true
* compress calls Environment.compress
* evict calls Environment.preload, then evictMemory
* removeDb calls Environment.removeDatabase, but doesn't do any cleaning
* removeDbAndClean calls removeDatabase, then cleanLog in a loop
* activateCleaner wakes up the cleaner, and then the main thread waits
* until you type "y" to the console before calling Environment.close().
* The control provided by the prompt is necessary for daemon activities
* because often threads check and bail out if the environment is closed.
* verifyUtilization calls CleanerTestUtils.verifyUtilization() to compare
* utilization as calculated by UtilizationProfile to utilization as
* calculated by UtilizationFileReader.
*/
public class DbRunAction {
private static final int BATCH_CLEAN = 1; // app-driven batch cleaning
private static final int COMPRESS = 2;
private static final int EVICT = 3;
private static final int CHECKPOINT = 4;
private static final int REMOVEDB = 5;
private static final int REMOVEDB_AND_CLEAN = 6;
private static final int ACTIVATE_CLEANER_THREADS = 7;
// wake up cleaner threads
private static final int VERIFY_UTILIZATION = 8;
public static void main(String[] argv) {
long recoveryStart = 0;
long actionStart = 0;
long actionEnd = 0;
try {
int whichArg = 0;
if (argv.length == 0) {
usage();
System.exit(1);
}
String dbName = null;
int doAction = 0;
String envHome = ".";
boolean readOnly = false;
boolean printStats = false;
while (whichArg < argv.length) {
String nextArg = argv[whichArg];
if (nextArg.equals("-h")) {
whichArg++;
envHome = CmdUtil.getArg(argv, whichArg);
} else if (nextArg.equals("-a")) {
whichArg++;
String action = CmdUtil.getArg(argv, whichArg);
if (action.equalsIgnoreCase("batchClean")) {
doAction = BATCH_CLEAN;
} else if (action.equalsIgnoreCase("compress")) {
doAction = COMPRESS;
} else if (action.equalsIgnoreCase("checkpoint")) {
doAction = CHECKPOINT;
} else if (action.equalsIgnoreCase("evict")) {
doAction = EVICT;
} else if (action.equalsIgnoreCase("removedb")) {
doAction = REMOVEDB;
} else if (action.equalsIgnoreCase("removedbAndClean")) {
doAction = REMOVEDB_AND_CLEAN;
} else if (action.equalsIgnoreCase("activateCleaner")) {
doAction = ACTIVATE_CLEANER_THREADS;
} else if (action.equalsIgnoreCase("verifyUtilization")) {
doAction = VERIFY_UTILIZATION;
} else {
usage();
System.exit(1);
}
} else if (nextArg.equals("-ro")) {
readOnly = true;
} else if (nextArg.equals("-s")) {
dbName = argv[++whichArg];
} else if (nextArg.equals("-stats")) {
printStats = true;
} else {
throw new IllegalArgumentException
(nextArg + " is not a supported option.");
}
whichArg++;
}
EnvironmentConfig envConfig = new EnvironmentConfig();
/* Don't debug log to the database log. */
if (readOnly) {
envConfig.setConfigParam
(EnvironmentParams.JE_LOGGING_DBLOG.getName(), "false");
envConfig.setReadOnly(true);
}
/*
* If evicting, scan the given database first and don't run the
* background evictor.
*/
if (doAction == EVICT) {
envConfig.setConfigParam
(EnvironmentParams.ENV_RUN_EVICTOR.getName(), "false");
envConfig.setConfigParam
(EnvironmentParams.EVICTOR_CRITICAL_PERCENTAGE.getName(),
"1000");
}
/*
* If cleaning, disable the daemon cleaner threads. The work being
* done by these threads is aborted when the environment is closed,
* which can result in incomplete log cleaning.
*/
if (doAction == BATCH_CLEAN) {
envConfig.setConfigParam
(EnvironmentParams.ENV_RUN_CLEANER.getName(), "false");
}
recoveryStart = System.currentTimeMillis();
Environment env =
new Environment(new File(envHome), envConfig);
CheckpointConfig forceConfig = new CheckpointConfig();
forceConfig.setForce(true);
Thread statsPrinter = null;
if (printStats) {
statsPrinter = new StatsPrinter(env);
statsPrinter.start();
}
boolean promptForShutdown = false;
actionStart = System.currentTimeMillis();
switch(doAction) {
case BATCH_CLEAN:
/* Since this is batch cleaning, repeat until no progress. */
while (true) {
int nFiles = env.cleanLog();
System.out.println("Files cleaned: " + nFiles);
if (nFiles == 0) {
break;
}
}
env.checkpoint(forceConfig);
break;
case COMPRESS:
env.compress();
break;
case CHECKPOINT:
env.checkpoint(forceConfig);
break;
case EVICT:
preload(env, dbName);
break;
case REMOVEDB:
removeAndClean(env, dbName, false);
break;
case REMOVEDB_AND_CLEAN:
removeAndClean(env, dbName, true);
break;
case ACTIVATE_CLEANER_THREADS:
EnvironmentImpl envImpl =
DbInternal.getNonNullEnvImpl(env);
envImpl.getCleaner().wakeupActivate();
promptForShutdown = true;
break;
case VERIFY_UTILIZATION:
EnvironmentImpl envImpl2 =
DbInternal.getNonNullEnvImpl(env);
VerifyUtils. verifyUtilization
(envImpl2,
true, // expectAccurateObsoleteLNCount
true); // expectAccurateObsoleteLNSize
break;
}
actionEnd = System.currentTimeMillis();
if (promptForShutdown) {
/*
* If the requested action is a daemon driven one, we don't
* want the main thread to shutdown the environment until we
* say we're ready
*/
waitForShutdown();
}
if (statsPrinter != null) {
statsPrinter.interrupt();
statsPrinter.join();
}
env.close();
} catch (Exception e) {
e.printStackTrace();
System.out.println(e.getMessage());
usage();
System.exit(1);
} finally {
DecimalFormat f = new DecimalFormat();
f.setMaximumFractionDigits(2);
long recoveryDuration = actionStart - recoveryStart;
System.out.println("\nrecovery time = " +
f.format(recoveryDuration) +
" millis " +
f.format((double)recoveryDuration/60000) +
" minutes");
long actionDuration = actionEnd - actionStart;
System.out.println("action time = " +
f.format(actionDuration) +
" millis " +
f.format(actionDuration/60000) +
" minutes");
}
}
private static void removeAndClean(Environment env,
String name,
boolean doCleaning)
throws Exception {
long a, c, d, e, f;
Transaction txn = null;
CheckpointConfig force = new CheckpointConfig();
force.setForce(true);
a = System.currentTimeMillis();
env.removeDatabase(txn, name);
c = System.currentTimeMillis();
int cleanedCount = 0;
if (doCleaning) {
while (env.cleanLog() > 0) {
cleanedCount++;
}
}
d = System.currentTimeMillis();
System.out.println("cleanedCount=" + cleanedCount);
e = 0;
f = 0;
if (cleanedCount > 0) {
e = System.currentTimeMillis();
env.checkpoint(force);
f = System.currentTimeMillis();
}
System.out.println("Remove of " + name +
" remove: " + getSecs(a, c) +
" clean: " + getSecs(c, d) +
" checkpoint: " + getSecs(e, f));
}
private static String getSecs(long start, long end) {
return (end-start) / 1000 + " secs";
}
private static void preload(Environment env, String dbName)
throws Exception {
System.out.println("Preload starting");
Database db = env.openDatabase(null, dbName, null);
Cursor cursor = db.openCursor(null, null);
try {
DatabaseEntry key = new DatabaseEntry();
DatabaseEntry data = new DatabaseEntry();
int count = 0;
while (cursor.getNext(key, data, LockMode.DEFAULT) ==
OperationStatus.SUCCESS) {
count++;
if ((count % 50000) == 0) {
System.out.println(count + "...");
}
}
System.out.println("Preloaded " + count + " records");
} finally {
cursor.close();
db.close();
}
}
@SuppressWarnings("unused")
private static void doEvict(Environment env)
throws DatabaseException {
/* Push the cache size down by half to force eviction. */
EnvironmentImpl envImpl = DbInternal.getNonNullEnvImpl(env);
long cacheUsage = envImpl.getMemoryBudget().getCacheMemoryUsage();
EnvironmentMutableConfig c = new EnvironmentMutableConfig();
c.setCacheSize(cacheUsage/2);
env.setMutableConfig(c);
long start = System.currentTimeMillis();
env.evictMemory();
long end = System.currentTimeMillis();
DecimalFormat f = new DecimalFormat();
f.setMaximumFractionDigits(2);
System.out.println("evict time=" + f.format(end-start));
}
private static void waitForShutdown()
throws IOException {
System.out.println
("Wait for daemon activity to run. When ready to stop, type (y)");
BufferedReader reader =
new BufferedReader(new InputStreamReader(System.in));
do {
String val = reader.readLine();
if (val != null &&
(val.equalsIgnoreCase("y") ||
val.equalsIgnoreCase("yes"))) {
break;
} else {
System.out.println("Shutdown? (y)");
}
} while (true);
}
private static class StatsPrinter extends Thread {
private Environment env;
StatsPrinter(Environment env) {
this.env = env;
}
@Override
public void run() {
StatsConfig clearConfig = new StatsConfig();
clearConfig.setClear(true);
while (true) {
try {
synchronized (this) {
wait(30 * 1000);
}
EnvironmentStats stats = env.getStats(clearConfig);
System.out.println("\n" + stats + "\n");
} catch (DatabaseException e) {
e.printStackTrace();
break;
} catch (InterruptedException e) {
break;
}
}
}
}
private static void usage() {
System.out.println("Usage: \n " +
CmdUtil.getJavaCommand(DbRunAction.class));
System.out.println(" -h <environment home> ");
System.out.println(" -a <batchClean|compress|evict|checkpoint|" +
"removeDb|removeDbAndClean|activateCleaner|" +
"verifyUtilization>");
System.out.println(" -ro (read-only - defaults to read-write)");
System.out.println(" -s <dbName> (for removeDb)");
System.out.println(" -stats (print every 30 seconds)");
}
}