mirror of
https://github.com/berkeleydb/je.git
synced 2024-11-15 01:46:24 +00:00
625 lines
23 KiB
Java
625 lines
23 KiB
Java
|
/*-
|
|||
|
* 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.math.BigInteger;
|
|||
|
import java.security.SecureRandom;
|
|||
|
|
|||
|
import com.sleepycat.bind.tuple.TupleInput;
|
|||
|
import com.sleepycat.bind.tuple.TupleOutput;
|
|||
|
import com.sleepycat.je.CacheMode;
|
|||
|
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.DbInternal;
|
|||
|
import com.sleepycat.je.Environment;
|
|||
|
import com.sleepycat.je.EnvironmentConfig;
|
|||
|
import com.sleepycat.je.EnvironmentStats;
|
|||
|
import com.sleepycat.je.LockConflictException;
|
|||
|
import com.sleepycat.je.LockMode;
|
|||
|
import com.sleepycat.je.OperationStatus;
|
|||
|
import com.sleepycat.je.StatsConfig;
|
|||
|
import com.sleepycat.je.Transaction;
|
|||
|
import com.sleepycat.je.dbi.MemoryBudget;
|
|||
|
|
|||
|
/**
|
|||
|
* A large database with random key distribution has lots of IN waste,
|
|||
|
* especially if records are small; this creates a worst-case scenario for the
|
|||
|
* cleaner and also possibly for the evictor. Simulate such an application and
|
|||
|
* measure how well the cleaner and evictor keep up.
|
|||
|
*
|
|||
|
* Some commonly used command lines for running this program are:
|
|||
|
*
|
|||
|
* # Init new DB, then do updates forever.
|
|||
|
* java BigRandom -h HOME -init
|
|||
|
*
|
|||
|
* # Do updates on an existing DB forever.
|
|||
|
* java BigRandom -h HOME
|
|||
|
*
|
|||
|
* # Init new DB, then stop and print total rate (MOST COMMON OPTION)
|
|||
|
* java BigRandom -h HOME -initonly
|
|||
|
*
|
|||
|
* # -locality N adds locality of reference for N transactions.
|
|||
|
* java BigRandom -h HOME -initonly -locality 5
|
|||
|
*
|
|||
|
* # -nosync speeds things up quite a bit
|
|||
|
* java BigRandom -h HOME -initonly -locality 5 -nosync
|
|||
|
*
|
|||
|
* Each transaction does the following in "grow" mode. In "no grow" mode, it
|
|||
|
* does one less insert, keeping the total number of keys constant.
|
|||
|
*
|
|||
|
* 2 inserts, 1 delete, 1 update, 10 reads
|
|||
|
*
|
|||
|
* The delete and update operations include a read to find the record.
|
|||
|
*
|
|||
|
* Every operation uses a random key, unless the -locality option is used. If
|
|||
|
* "-locality 100" is specified, each thread will perform 100 transactions by
|
|||
|
* incrementing the insertion key rather than generating a random number. Then
|
|||
|
* a random number is generated as the next starting key. This is done per
|
|||
|
* thread, so each thread will be working in a different key area.
|
|||
|
*/
|
|||
|
public class BigRandom implements Runnable {
|
|||
|
|
|||
|
private String homeDir = "tmp";
|
|||
|
private Environment env;
|
|||
|
private Database db;
|
|||
|
private boolean done;
|
|||
|
private int nDeadlocks;
|
|||
|
private boolean init;
|
|||
|
private boolean initOnly;
|
|||
|
private boolean fastInit;
|
|||
|
private boolean verbose;
|
|||
|
private boolean sequentialKeys;
|
|||
|
private boolean noSync;
|
|||
|
private int nMaxKeys = 10000000;
|
|||
|
private long nKeys;
|
|||
|
private long sequence;
|
|||
|
private int nTransactions;
|
|||
|
private int nMaxTransactions;
|
|||
|
private int nThreads = 4;
|
|||
|
private int oneThreadKeys;
|
|||
|
private long traceInterval = 10000; // 10 seconds
|
|||
|
private boolean preload;
|
|||
|
private int maxLocalKeyTxns;
|
|||
|
private int keySize = 10;
|
|||
|
private int dataSize = 20;
|
|||
|
private int nReadsPerWrite = 10;
|
|||
|
private int maxRetries = 100;
|
|||
|
private SecureRandom random = new SecureRandom();
|
|||
|
private long startTime;
|
|||
|
private long priorTime = startTime;
|
|||
|
private int priorTxns;
|
|||
|
private int[] tpTxns = new int[120]; // 120 * 10 sec = ~20 minutes worth
|
|||
|
private long[] tpMillis = new long[tpTxns.length];
|
|||
|
private int tpIndex = tpTxns.length - 1;
|
|||
|
private int tpMaxIndex;
|
|||
|
private long tpTotalTxns;
|
|||
|
private long tpTotalMillis;
|
|||
|
private int thisTxns;
|
|||
|
private int thisSecs;
|
|||
|
private int thisTp;
|
|||
|
private int avgTp;
|
|||
|
private long time;
|
|||
|
private int totalSecs;
|
|||
|
private int subDir = 0;
|
|||
|
|
|||
|
public static void main(String args[]) {
|
|||
|
try {
|
|||
|
new BigRandom().run(args);
|
|||
|
System.exit(0);
|
|||
|
} catch (Throwable e) {
|
|||
|
e.printStackTrace(System.out);
|
|||
|
System.exit(1);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
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("-v")) {
|
|||
|
verbose = true;
|
|||
|
} else if (arg.equals("-seq")) {
|
|||
|
sequentialKeys = true;
|
|||
|
} else if (arg.equals("-nosync")) {
|
|||
|
noSync = true;
|
|||
|
} else if (arg.equals("-h") && moreArgs) {
|
|||
|
homeDir = args[++i];
|
|||
|
} else if (arg.equals("-preload")) {
|
|||
|
preload = true;
|
|||
|
} else if (arg.equals("-init")) {
|
|||
|
init = true;
|
|||
|
} else if (arg.equals("-initonly")) {
|
|||
|
init = true;
|
|||
|
initOnly = true;
|
|||
|
} else if (arg.equals("-fastinit")) {
|
|||
|
init = true;
|
|||
|
fastInit = true;
|
|||
|
initOnly = true;
|
|||
|
} else if (arg.equals("-keys") && moreArgs) {
|
|||
|
nMaxKeys = Integer.parseInt(args[++i]);
|
|||
|
} else if (arg.equals("-txns") && moreArgs) {
|
|||
|
nMaxTransactions = Integer.parseInt(args[++i]);
|
|||
|
} else if (arg.equals("-threads") && moreArgs) {
|
|||
|
nThreads = Integer.parseInt(args[++i]);
|
|||
|
} else if (arg.equals("-onethreadkeys") && moreArgs) {
|
|||
|
oneThreadKeys = Integer.parseInt(args[++i]);
|
|||
|
} else if (arg.equals("-locality") && moreArgs) {
|
|||
|
maxLocalKeyTxns = Integer.parseInt(args[++i]);
|
|||
|
} else if (arg.equals("-subDir") && moreArgs) {
|
|||
|
subDir = Integer.parseInt(args[++i]);
|
|||
|
} else {
|
|||
|
usage("Unknown arg: " + arg);
|
|||
|
}
|
|||
|
}
|
|||
|
openEnv();
|
|||
|
printArgs(args);
|
|||
|
printLegend();
|
|||
|
if (sequentialKeys) {
|
|||
|
sequence = getLastSequence();
|
|||
|
}
|
|||
|
if (preload) {
|
|||
|
doPreload();
|
|||
|
}
|
|||
|
StatsConfig statsConfig = new StatsConfig();
|
|||
|
statsConfig.setFast(true);
|
|||
|
statsConfig.setClear(true);
|
|||
|
startTime = System.currentTimeMillis();
|
|||
|
priorTime = startTime;
|
|||
|
|
|||
|
Thread[] threads = new Thread[nThreads];
|
|||
|
if (oneThreadKeys > 0) {
|
|||
|
threads[0] = new Thread(this);
|
|||
|
threads[0].start();
|
|||
|
} else {
|
|||
|
for (int i = 0; i < nThreads; i += 1) {
|
|||
|
threads[i] = new Thread(this);
|
|||
|
threads[i].start();
|
|||
|
Thread.sleep(1000); /* Stagger threads. */
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
while (!done) {
|
|||
|
Thread.sleep(traceInterval);
|
|||
|
calcThroughput();
|
|||
|
/* JE-only begin */
|
|||
|
EnvironmentStats stats = env.getStats(statsConfig);
|
|||
|
MemoryBudget mb =
|
|||
|
DbInternal.getNonNullEnvImpl(env).getMemoryBudget();
|
|||
|
int inListSize =
|
|||
|
DbInternal.getNonNullEnvImpl(env).getInMemoryINs().
|
|||
|
getSize();
|
|||
|
System.out.println("\nsec: " + totalSecs + ',' + thisSecs +
|
|||
|
" txn: " + thisTxns + ',' +
|
|||
|
thisTp + ',' + avgTp +
|
|||
|
" keys: " + nKeys +
|
|||
|
" dlck: " + nDeadlocks +
|
|||
|
" buf: " +
|
|||
|
stats.getNNotResident() + ',' +
|
|||
|
stats.getNCacheMiss() +
|
|||
|
"\ncleaner: " +
|
|||
|
stats.getNCleanerEntriesRead() + ',' +
|
|||
|
stats.getNCleanerRuns() + ',' +
|
|||
|
stats.getNCleanerDeletions() + ',' +
|
|||
|
stats.getCleanerBacklog() +
|
|||
|
" evict: " +
|
|||
|
stats.getNBINsStripped() + ',' +
|
|||
|
stats.getNNodesExplicitlyEvicted() + ',' +
|
|||
|
mb.getCacheMemoryUsage() + ',' +
|
|||
|
inListSize +
|
|||
|
" ckpt: " +
|
|||
|
stats.getNCheckpoints() + ',' +
|
|||
|
stats.getNFullINFlush() + ',' +
|
|||
|
stats.getNFullBINFlush() + ',' +
|
|||
|
stats.getNDeltaINFlush());
|
|||
|
/* JE-only end */
|
|||
|
nDeadlocks = 0;
|
|||
|
|
|||
|
if (oneThreadKeys > 0 && oneThreadKeys >= nKeys) {
|
|||
|
for (int i = 1; i < nThreads; i += 1) {
|
|||
|
threads[i] = new Thread(this);
|
|||
|
threads[i].start();
|
|||
|
Thread.sleep(1000); /* Stagger threads. */
|
|||
|
}
|
|||
|
oneThreadKeys = 0;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
for (int i = 0; i < nThreads; i += 1) {
|
|||
|
if (threads[i] != null) {
|
|||
|
threads[i].join();
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
time = System.currentTimeMillis();
|
|||
|
totalSecs = (int) ((time - startTime) / 1000);
|
|||
|
System.out.println("\nTotal seconds: " + totalSecs +
|
|||
|
" txn/sec: " + (nTransactions / totalSecs));
|
|||
|
closeEnv();
|
|||
|
}
|
|||
|
|
|||
|
private void calcThroughput() {
|
|||
|
|
|||
|
time = System.currentTimeMillis();
|
|||
|
totalSecs = (int) ((time - startTime) / 1000);
|
|||
|
int txns = nTransactions;
|
|||
|
thisTxns = txns - priorTxns;
|
|||
|
int thisMillis = (int) (time - priorTime);
|
|||
|
thisSecs = thisMillis / 1000;
|
|||
|
thisTp = thisTxns / thisSecs;
|
|||
|
|
|||
|
tpIndex += 1;
|
|||
|
if (tpIndex == tpTxns.length) {
|
|||
|
tpIndex = 0;
|
|||
|
}
|
|||
|
tpTotalTxns += thisTxns;
|
|||
|
tpTotalTxns -= tpTxns[tpIndex];
|
|||
|
tpTotalMillis += thisMillis;
|
|||
|
tpTotalMillis -= tpMillis[tpIndex];
|
|||
|
tpTxns[tpIndex] = thisTxns;
|
|||
|
tpMillis[tpIndex] = thisMillis;
|
|||
|
if (tpMaxIndex < tpTxns.length) {
|
|||
|
tpMaxIndex = tpIndex + 1;
|
|||
|
}
|
|||
|
avgTp = (int) ((tpTotalTxns / (tpTotalMillis / 1000)));
|
|||
|
|
|||
|
priorTxns = txns;
|
|||
|
priorTime = time;
|
|||
|
}
|
|||
|
|
|||
|
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();
|
|||
|
}
|
|||
|
|
|||
|
private void printLegend() {
|
|||
|
|
|||
|
/* JE-only begin */
|
|||
|
System.out.println(
|
|||
|
"Legend:\n" +
|
|||
|
"sec: <totalSeconds>,<runSeconds>\n" +
|
|||
|
"txn: <txns>,<txnPerSec>,<runningAvgTxnPerSec>\n" +
|
|||
|
"keys: <totalKeys>\n" +
|
|||
|
"dlck: <deadlocks>\n" +
|
|||
|
"buf: <notResident>,<cacheMisses>\n" +
|
|||
|
"clean: <entriesRead>,<filesCleaned>,<filesDeleted>,<backlog>\n" +
|
|||
|
"evict: <binsStripped>,<nodesEvicted>,<cacheSize>,<INListSize>\n" +
|
|||
|
"ckpt: <checkpointsStarted>,<fullINs>,<fullBINs>,<deltaINs>");
|
|||
|
/* JE-only end */
|
|||
|
}
|
|||
|
|
|||
|
private void usage(String error) {
|
|||
|
|
|||
|
if (error != null) {
|
|||
|
System.err.println(error);
|
|||
|
}
|
|||
|
System.err.println
|
|||
|
("java " + getClass().getName() + '\n' +
|
|||
|
" [-h <homeDir>] [-v] [-init | -initonly | -fastinit]\n" +
|
|||
|
" [-keys <maxKeys>] [-txns <maxTxns>] [-seq]\n" +
|
|||
|
" [-threads <appThreads>] [-onethreadkeys <nKeys>]\n" +
|
|||
|
" [-locality <nTxns>] [-nosync] [-preload]");
|
|||
|
System.exit(2);
|
|||
|
}
|
|||
|
|
|||
|
private void openEnv() throws Exception {
|
|||
|
|
|||
|
EnvironmentConfig envConfig = new EnvironmentConfig();
|
|||
|
envConfig.setTransactional(true);
|
|||
|
envConfig.setAllowCreate(init);
|
|||
|
envConfig.setCacheMode(CacheMode.EVICT_LN);
|
|||
|
envConfig.setConfigParam(EnvironmentConfig.MAX_OFF_HEAP_MEMORY,
|
|||
|
"" + (50 * 1024 * 1024));
|
|||
|
|
|||
|
if (noSync) {
|
|||
|
envConfig.setTxnNoSync(true);
|
|||
|
}
|
|||
|
|
|||
|
if (subDir > 0) {
|
|||
|
envConfig.setConfigParam
|
|||
|
(EnvironmentConfig.LOG_N_DATA_DIRECTORIES, subDir + "");
|
|||
|
Utils.createSubDirs(new File(homeDir), subDir);
|
|||
|
}
|
|||
|
|
|||
|
long startTime = System.currentTimeMillis();
|
|||
|
env = new Environment(new File(homeDir), envConfig);
|
|||
|
long endTime = System.currentTimeMillis();
|
|||
|
System.out.println("Recovery time: " + ((endTime - startTime) / 1000));
|
|||
|
System.out.println();
|
|||
|
|
|||
|
DatabaseConfig dbConfig = new DatabaseConfig();
|
|||
|
dbConfig.setAllowCreate(init);
|
|||
|
dbConfig.setExclusiveCreate(init);
|
|||
|
dbConfig.setTransactional(true);
|
|||
|
/* JE-only begin */
|
|||
|
db = env.openDatabase(null, "BigRandom", dbConfig);
|
|||
|
/* JE-only end */
|
|||
|
}
|
|||
|
|
|||
|
private void closeEnv()
|
|||
|
throws DatabaseException {
|
|||
|
|
|||
|
db.close();
|
|||
|
env.close();
|
|||
|
}
|
|||
|
|
|||
|
public void run() {
|
|||
|
|
|||
|
/*
|
|||
|
* The key is reused over multiple loop iterations for computing a
|
|||
|
* local insertion key, so it must be instantiated at the top of the
|
|||
|
* loop. In makeInsertKey a local insertion key is creating by adding
|
|||
|
* one to the last key accessed.
|
|||
|
*/
|
|||
|
DatabaseEntry data = new DatabaseEntry();
|
|||
|
DatabaseEntry key = new DatabaseEntry();
|
|||
|
int localKeyTxns = 0;
|
|||
|
byte[] lastInsertKey = null;
|
|||
|
OperationStatus status;
|
|||
|
|
|||
|
while (!done) {
|
|||
|
|
|||
|
try {
|
|||
|
/*
|
|||
|
* When using local keys, only the first insert will be with a
|
|||
|
* random key, and only if we've exceeded the maximum number of
|
|||
|
* local key transactions. When not using local keys, all keys
|
|||
|
* are randomly generated.
|
|||
|
*/
|
|||
|
boolean useLocalKeys = maxLocalKeyTxns > 0;
|
|||
|
boolean insertRandomKey = true;
|
|||
|
if (useLocalKeys) {
|
|||
|
if (localKeyTxns < maxLocalKeyTxns) {
|
|||
|
insertRandomKey = false;
|
|||
|
localKeyTxns += 1;
|
|||
|
} else {
|
|||
|
localKeyTxns = 0;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
/* Perform the transaction. */
|
|||
|
for (int retry = 0;; retry += 1) {
|
|||
|
Transaction txn = env.beginTransaction(null, null);
|
|||
|
Cursor c = db.openCursor(txn, null);
|
|||
|
try {
|
|||
|
boolean addedKey = false;
|
|||
|
if (init && nKeys < nMaxKeys) {
|
|||
|
key.setData(lastInsertKey);
|
|||
|
insert(c, key, data, insertRandomKey);
|
|||
|
lastInsertKey = copyData(key);
|
|||
|
insertRandomKey = !useLocalKeys;
|
|||
|
addedKey = true;
|
|||
|
}
|
|||
|
if (!fastInit) {
|
|||
|
/* Insert. */
|
|||
|
key.setData(lastInsertKey);
|
|||
|
insert(c, key, data, insertRandomKey);
|
|||
|
lastInsertKey = copyData(key);
|
|||
|
if (useLocalKeys) {
|
|||
|
/* Update the following key. */
|
|||
|
status = c.getNext(key, data, LockMode.RMW);
|
|||
|
if (status == OperationStatus.SUCCESS) {
|
|||
|
c.putCurrent(data);
|
|||
|
/* Delete the following key. */
|
|||
|
status = c.getNext
|
|||
|
(key, data, LockMode.RMW);
|
|||
|
if (status == OperationStatus.SUCCESS) {
|
|||
|
c.delete();
|
|||
|
}
|
|||
|
}
|
|||
|
/* Read. Use RMW to avoid deadlocks. */
|
|||
|
for (int i = 0; i < nReadsPerWrite; i += 1) {
|
|||
|
c.getNext(key, data, LockMode.RMW);
|
|||
|
}
|
|||
|
} else {
|
|||
|
/* Update */
|
|||
|
getRandom(c, key, data, LockMode.RMW);
|
|||
|
c.putCurrent(data);
|
|||
|
/* Delete */
|
|||
|
getRandom(c, key, data, LockMode.RMW);
|
|||
|
c.delete();
|
|||
|
/* Read */
|
|||
|
for (int i = 0; i < nReadsPerWrite; i += 1) {
|
|||
|
getRandom(c, key, data, null);
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
c.close();
|
|||
|
txn.commit();
|
|||
|
nTransactions += 1;
|
|||
|
if (addedKey) {
|
|||
|
nKeys += 1;
|
|||
|
}
|
|||
|
if (initOnly && nKeys >= nMaxKeys) {
|
|||
|
done = true;
|
|||
|
}
|
|||
|
if (nMaxTransactions != 0 &&
|
|||
|
nTransactions >= nMaxTransactions) {
|
|||
|
done = true;
|
|||
|
}
|
|||
|
break;
|
|||
|
} catch (LockConflictException e) {
|
|||
|
c.close();
|
|||
|
txn.abort();
|
|||
|
if (retry >= maxRetries) {
|
|||
|
throw e;
|
|||
|
}
|
|||
|
/* Break deadlock cycle with a small sleep. */
|
|||
|
Thread.sleep(5);
|
|||
|
nDeadlocks += 1;
|
|||
|
}
|
|||
|
}
|
|||
|
} catch (Throwable e) {
|
|||
|
e.printStackTrace();
|
|||
|
System.exit(1);
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
private void insert(Cursor c, DatabaseEntry key, DatabaseEntry data,
|
|||
|
boolean insertRandomKey)
|
|||
|
throws DatabaseException {
|
|||
|
|
|||
|
makeData(data);
|
|||
|
while (true) {
|
|||
|
makeInsertKey(c, key, insertRandomKey);
|
|||
|
OperationStatus status = c.putNoOverwrite(key, data);
|
|||
|
if (status == OperationStatus.KEYEXIST) {
|
|||
|
if (sequentialKeys) {
|
|||
|
System.out.println("****** Duplicate sequential key.");
|
|||
|
} else if (insertRandomKey) {
|
|||
|
System.out.println("****** Duplicate random key.");
|
|||
|
} else {
|
|||
|
System.out.println("****** Duplicate local key.");
|
|||
|
}
|
|||
|
} else {
|
|||
|
if (status != OperationStatus.SUCCESS) {
|
|||
|
System.out.println
|
|||
|
("Unexpected return value from insert(): " + status);
|
|||
|
}
|
|||
|
break;
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
private void getRandom(Cursor c, DatabaseEntry key, DatabaseEntry data,
|
|||
|
LockMode lockMode)
|
|||
|
throws DatabaseException {
|
|||
|
|
|||
|
makeRandomKey(key);
|
|||
|
OperationStatus status = c.getSearchKeyRange(key, data, lockMode);
|
|||
|
if (status == OperationStatus.NOTFOUND) {
|
|||
|
status = c.getLast(key, data, lockMode);
|
|||
|
if (status != OperationStatus.SUCCESS) {
|
|||
|
System.out.println
|
|||
|
("Unexpected return value from getRandom(): " + status);
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
private long getLastSequence()
|
|||
|
throws DatabaseException {
|
|||
|
|
|||
|
if (!sequentialKeys) throw new IllegalStateException();
|
|||
|
DatabaseEntry data = new DatabaseEntry();
|
|||
|
DatabaseEntry key = new DatabaseEntry();
|
|||
|
Cursor c = db.openCursor(null, null);
|
|||
|
try {
|
|||
|
OperationStatus status = c.getLast(key, data, null);
|
|||
|
if (status == OperationStatus.SUCCESS) {
|
|||
|
TupleInput in = new TupleInput(key.getData(),
|
|||
|
key.getOffset(),
|
|||
|
key.getSize());
|
|||
|
return in.readLong();
|
|||
|
} else {
|
|||
|
return 0;
|
|||
|
}
|
|||
|
} finally {
|
|||
|
c.close();
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
private void doPreload()
|
|||
|
throws DatabaseException {
|
|||
|
|
|||
|
System.out.println("Preloading");
|
|||
|
DatabaseEntry data = new DatabaseEntry();
|
|||
|
DatabaseEntry key = new DatabaseEntry();
|
|||
|
Cursor c = db.openCursor(null, null);
|
|||
|
try {
|
|||
|
long startTime = System.currentTimeMillis();
|
|||
|
int count = 0;
|
|||
|
while (c.getNext(key, data, LockMode.READ_UNCOMMITTED) ==
|
|||
|
OperationStatus.SUCCESS) {
|
|||
|
count += 1;
|
|||
|
}
|
|||
|
long endTime = System.currentTimeMillis();
|
|||
|
int seconds = (int) ((endTime - startTime) / 1000);
|
|||
|
System.out.println
|
|||
|
("Preloaded records=" + count + " seconds=" + seconds);
|
|||
|
} finally {
|
|||
|
c.close();
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
private void makeInsertKey(Cursor c, DatabaseEntry key,
|
|||
|
boolean insertRandomKey) {
|
|||
|
if (sequentialKeys) {
|
|||
|
long val;
|
|||
|
synchronized (this) {
|
|||
|
val = ++sequence;
|
|||
|
}
|
|||
|
makeLongKey(key, val);
|
|||
|
} else if (!insertRandomKey && key.getData() != null) {
|
|||
|
BigInteger num = new BigInteger(copyData(key));
|
|||
|
num = num.add(BigInteger.ONE);
|
|||
|
key.setData(num.toByteArray());
|
|||
|
} else {
|
|||
|
makeRandomKey(key);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
private void makeRandomKey(DatabaseEntry key) {
|
|||
|
|
|||
|
if (sequentialKeys) {
|
|||
|
makeLongKey(key, (long) (random.nextFloat() * sequence));
|
|||
|
} else {
|
|||
|
byte[] bytes = new byte[keySize];
|
|||
|
random.nextBytes(bytes);
|
|||
|
key.setData(bytes);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
private void makeLongKey(DatabaseEntry key, long val) {
|
|||
|
|
|||
|
TupleOutput out = new TupleOutput();
|
|||
|
out.writeLong(val);
|
|||
|
byte[] pad = new byte[keySize - 8];
|
|||
|
out.writeFast(pad);
|
|||
|
if (out.getBufferOffset() != 0 || out.getBufferLength() != keySize) {
|
|||
|
throw new IllegalStateException();
|
|||
|
}
|
|||
|
key.setData(out.getBufferBytes(), 0, keySize);
|
|||
|
}
|
|||
|
|
|||
|
private void makeData(DatabaseEntry data) {
|
|||
|
|
|||
|
byte[] bytes = new byte[dataSize];
|
|||
|
for (int i = 0; i < bytes.length; i += 1) {
|
|||
|
bytes[i] = (byte) i;
|
|||
|
}
|
|||
|
data.setData(bytes);
|
|||
|
}
|
|||
|
|
|||
|
private byte[] copyData(DatabaseEntry data) {
|
|||
|
|
|||
|
byte[] buf = new byte[data.getSize()];
|
|||
|
System.arraycopy(data.getData(), data.getOffset(), buf, 0, buf.length);
|
|||
|
return buf;
|
|||
|
}
|
|||
|
}
|