je/test/standalone/RemoveDbStress.java
2021-06-06 13:46:45 -04:00

297 lines
10 KiB
Java
Raw Permalink Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*-
* Copyright (C) 2002, 2017, 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.
*/
import java.io.File;
import java.util.Random;
import com.sleepycat.je.Cursor;
import com.sleepycat.je.Database;
import com.sleepycat.je.DatabaseConfig;
import com.sleepycat.je.DatabaseEntry;
import com.sleepycat.je.DatabaseException;
import com.sleepycat.je.Environment;
import com.sleepycat.je.EnvironmentConfig;
import com.sleepycat.je.LockConflictException;
import com.sleepycat.je.LockMode;
import com.sleepycat.je.OperationStatus;
import com.sleepycat.je.config.EnvironmentParams;
import com.sleepycat.je.dbi.MemoryBudget;
/**
* Make sure no bugs are spotted when remove/truncate database is being called
* and the log cleaner and checkpointer threads are interacting with the db.
*/
public class RemoveDbStress {
/*
* The following check ensures that locks are held by the handle in
* the face of NameLN migration. However, it causes the program
* to take over 20 hours to run. So if it is enabled, the number
* of iterations should be decreased.
*/
private static final boolean CHECK_REMOVE_WITH_OPEN_HANDLE = false;
private int totalIterations = 500000;
private int totalThreads = 4;
private boolean offHeap = false;
private int subDir = 0;
private String envHome = "./tmp";
private Random random = new Random();
private Environment env = null;
public static void main(String[] args) {
RemoveDbStress stress = new RemoveDbStress();
try {
stress.run(args);
} catch (Throwable e){
System.err.println("Error initializing env!");
e.printStackTrace();
System.exit(1);
}
}
/** Kickoff the run. */
private void run(String args[]) throws Exception {
for (int i = 0; i < args.length; i += 1) {
String arg = args[i];
boolean moreArgs = i < args.length - 1;
if (arg.equals("-h") && moreArgs) {
envHome = args[++i];
} else if (arg.equals("-iter") && moreArgs) {
totalIterations = Integer.parseInt(args[++i]);
} else if (arg.equals("-threads") && moreArgs) {
totalThreads = Integer.parseInt(args[++i]);
} else if (arg.equals("-offheap") && moreArgs) {
offHeap = Boolean.parseBoolean(args[++i]);
} else if (arg.equals("-subDir") && moreArgs) {
subDir = Integer.parseInt(args[++i]);
} else {
usage("Unknown arg: " + arg);
}
}
openEnv();
printArgs(args);
/*
* Perform some operations to simulate a scenario to find bugs:
* make sure remove/truncate database is being called and the log
* cleaner and checkpointer threads are interacting with the db.
*/
Worker[] workers = new Worker[totalThreads];
for (int i = 0; i < totalThreads; i += 1) {
workers[i] = new Worker(i);
workers[i].start();
Thread.sleep(1000); /* Stagger threads. */
}
for (int i = 0; i < totalThreads; i += 1) {
workers[i].join();
}
closeEnv();
}
/** Print usage. */
private void usage(String error) {
if (error != null) {
System.err.println(error);
}
System.err.println
("java " + getClass().getName() + '\n' +
" [-h <homeDir>] [-iter <iterations>] " +
"[-threads <appThreads>]\n");
System.exit(1);
}
/** Print cmd arguments and database env settings to log file. */
private void printArgs(String[] args)
throws DatabaseException {
System.out.print("Command line arguments:");
for (String arg : args) {
System.out.print(' ');
System.out.print(arg);
}
System.out.println();
System.out.println();
System.out.println("Environment configuration:");
System.out.println(env.getConfig());
System.out.println();
}
/**
* Open an Environment.
*/
private void openEnv() throws Exception {
EnvironmentConfig config = new EnvironmentConfig();
config.setAllowCreate(true);
if (offHeap) {
config.setOffHeapCacheSize(500000);
}
config.setConfigParam(
EnvironmentConfig.MAX_MEMORY,
MemoryBudget.MIN_MAX_MEMORY_SIZE_STRING);
/* Don't track detail with a tiny cache size. */
config.setConfigParam(
EnvironmentParams.CLEANER_TRACK_DETAIL.getName(), "false");
config.setConfigParam(
EnvironmentConfig.CLEANER_BYTES_INTERVAL, "100");
config.setConfigParam(
EnvironmentConfig.CHECKPOINTER_BYTES_INTERVAL, "100");
config.setConfigParam(
EnvironmentConfig.COMPRESSOR_WAKEUP_INTERVAL, "1000000");
config.setConfigParam(
EnvironmentConfig.LOG_TOTAL_BUFFER_BYTES,
EnvironmentParams.LOG_MEM_SIZE_MIN_STRING);
config.setConfigParam(
EnvironmentConfig.LOG_NUM_BUFFERS, "2");
config.setConfigParam(
EnvironmentConfig.ENV_RUN_EVICTOR, "true");
config.setConfigParam(
EnvironmentConfig.ENV_RUN_IN_COMPRESSOR, "true");
config.setConfigParam(
EnvironmentConfig.ENV_RUN_CLEANER, "true");
config.setConfigParam(
EnvironmentConfig.ENV_RUN_CHECKPOINTER, "true");
/*
config.setConfigParam(EnvironmentConfig.TXN_DEADLOCK_STACK_TRACE,
"true");
*/
if (subDir > 0) {
config.setConfigParam
(EnvironmentConfig.LOG_N_DATA_DIRECTORIES, subDir + "");
Utils.createSubDirs(new File(envHome), subDir);
}
env = new Environment(new File(envHome), config);
}
private void closeEnv()
throws DatabaseException {
env.close();
}
class Worker extends Thread {
private int iterations = 0;
/** The identifier of the current thread. */
private int id;
/**
* Creates a new worker thread object.
*/
public Worker(int id) {
this.id = id;
}
/**
* This thread is responsible for executing transactions.
*/
public void run() {
long startTime = System.currentTimeMillis();
while (iterations < totalIterations) {
try {
doOperations();
} catch (Throwable e) {
System.err.println
("Error! " + iterations +
" iterations processed so far.");
e.printStackTrace();
System.exit(1);
}
iterations += 1;
if ((iterations % 1000) == 0)
System.out.println
(new java.util.Date() + ": Thread " + id +
" finishes " + iterations + " iterations.");
}
long endTime = System.currentTimeMillis();
float elapsedSec = (float) ((endTime - startTime) / 1e3);
float throughput = ((float) totalIterations) / elapsedSec;
System.out.println
("Thread " + id + " finishes " + iterations +
" iterations in: " + elapsedSec +
" sec, average throughput: " + throughput + " op/sec.");
}
/**
* Perform some insert and delete operations in order to wakeup the
* checkpointer, cleaner and evictor.
*/
private void doOperations() throws DatabaseException {
String dbName = "testDb" + id;
DatabaseConfig dbConfig = new DatabaseConfig();
dbConfig.setAllowCreate(true);
dbConfig.setSortedDuplicates(true);
Database db = env.openDatabase(null, dbName, dbConfig);
Cursor cursor = db.openCursor(null, null);
doSimpleCursorPutAndDelete(cursor);
cursor.close();
if (CHECK_REMOVE_WITH_OPEN_HANDLE) {
try {
Thread.sleep(1000);
env.removeDatabase(null, dbName);
throw new RuntimeException("Expected LockConflict");
} catch (LockConflictException expected) {
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
db.close();
if (random.nextFloat() < .5) {
env.removeDatabase(null, dbName);
} else {
env.truncateDatabase(null, dbName, false);
}
}
/**
* Write some data to wakeup the checkpointer, cleaner and evictor.
*/
protected void doSimpleCursorPutAndDelete(Cursor cursor)
throws DatabaseException {
String[] simpleKeyStrings = {
"foo", "bar", "baz", "aaa", "fubar",
"foobar", "quux", "mumble", "froboy" };
DatabaseEntry key = new DatabaseEntry();
DatabaseEntry data = new DatabaseEntry(new byte[10240]);
for (int i = 0; i < simpleKeyStrings.length; i++) {
key.setData(simpleKeyStrings[i].getBytes());
if (cursor.putNoOverwrite(key, data) !=
OperationStatus.SUCCESS) {
throw new RuntimeException("Could not insert");
}
}
OperationStatus status =
cursor.getFirst(key, data, LockMode.DEFAULT);
while (status == OperationStatus.SUCCESS) {
cursor.delete();
status = cursor.getNext(key, data, LockMode.DEFAULT);
}
}
}
}