/*******************************************************************************
* Copyright 2010 Cees De Groot, Alex Boisvert, Jan Kotek
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
******************************************************************************/
package org.apache.jdbm;
import java.util.Random;
/**
* This class contains stress tests for this package.
*/
public class TestStress extends TestCaseWithTestFile {
// test parameters
final int RECORDS = 10000;
final int MAXSIZE = 500;
final int ROUNDS = 1 * 1000 * 1000;
final int RPPROMILLE = ROUNDS / 1000;
Random rnd = new Random(42);
// holder for record data so we can compare
class RecordData {
long rowid;
int size;
byte b;
RecordData(long rowid, int size, byte b) {
this.rowid = rowid;
this.size = size;
this.b = b;
}
public String toString() {
return "slot(" + rowid + ",sz=" + size + ",b=" + b + ")";
}
}
private int getRandomAllocatedSlot(RecordData[] d) {
int slot = rnd.nextInt(RECORDS);
while (d[slot] == null) {
slot++;
if (slot == RECORDS)
slot = 0; // wrap
}
return slot;
}
// holder for root records
long[] roots = new long[Magic.FILE_HEADER_NROOTS];
private int getRandomAllocatedRoot() {
int slot = rnd.nextInt(Magic.FILE_HEADER_NROOTS);
while (roots[slot] == 0) {
slot++;
if (slot == Magic.FILE_HEADER_NROOTS)
slot = 0; // wrap
}
return slot;
}
/**
* Test basics
*/
public void testBasics() throws Exception {
String file = newTestFile();
DBStore db = new DBStore(file, false, false,false);
// as this code is meant to test data structure calculcations
// and stuff like that, we may want to disable transactions
// that just slow us down.
// mgr.disableTransactions();
RecordData[] d = new RecordData[RECORDS];
int recordCount = 0, rootCount = 0;
int inserts = 0, updates = 0, deletes = 0, fetches = 0;
int rootgets = 0, rootsets = 0;
int slot = -1;
try {
for (int i = 0; i < ROUNDS; i++) {
if ((i % RPPROMILLE) == 0)
System.out.print("\rComplete: "
+ i / RPPROMILLE + "/1000th");
// close and re-open a couple of times during the
// test, in order to check flushing etcetera.
if ((i % (ROUNDS / 5)) == 0) {
System.out.print(" (reopened at round "
+ i / RPPROMILLE + ")");
db.close();
db = new DBStore(file, false, false,false);
// db.disableTransactions();
}
// generate a random number and assign ranges to operations:
// 0-10 = insert, 20 = delete, 30-50 = update, 51 = set root,
// 52 = get root, rest = fetch.
int op = rnd.nextInt(100);
if (op <= 10) {
// INSERT RECORD
if (recordCount == RECORDS) {
i -= 1;
continue;
}
slot = 0;
while (d[slot] != null)
slot++;
d[slot] = new RecordData(0, rnd.nextInt(MAXSIZE),
(byte) rnd.nextInt());
d[slot].rowid =
db.insert(UtilTT.makeRecord(d[slot].size,
d[slot].b));
recordCount++;
inserts++;
} else if (op == 20) {
// DELETE RECORD
if (recordCount == 0) {
i -= 1;
continue;
}
slot = getRandomAllocatedSlot(d);
db.delete(d[slot].rowid);
d[slot] = null;
recordCount--;
deletes++;
} else if (op <= 50) {
// UPDATE RECORD
if (recordCount == 0) {
i -= 1;
continue;
}
slot = getRandomAllocatedSlot(d);
d[slot].size = rnd.nextInt(MAXSIZE);
d[slot].b = (byte) rnd.nextInt();
db.update(d[slot].rowid,
UtilTT.makeRecord(d[slot].size,
d[slot].b));
updates++;
} else if (op == 51) {
// SET ROOT
int root = rnd.nextInt(Magic.FILE_HEADER_NROOTS);
if (root > 10) { //DONT do this for reserved roots
roots[root] = rnd.nextLong();
db.setRoot((byte) root, roots[root]);
rootsets++;
}
} else if (op == 52) {
// GET ROOT
if (rootCount == 0) {
i -= 1;
continue;
}
int root = getRandomAllocatedRoot();
if (root > 10) { //DONT do this for reserved roots
assertEquals("root", roots[root], db.getRoot((byte) root));
rootgets++;
}
} else {
// FETCH RECORD
if (recordCount == 0) {
i -= 1;
continue;
}
slot = getRandomAllocatedSlot(d);
byte[] data = (byte[]) db.fetch(d[slot].rowid);
assertTrue("fetch round=" + i + ", slot=" + slot
+ ", " + d[slot],
UtilTT.checkRecord(data, d[slot].size, d[slot].b));
fetches++;
}
}
db.close();
} catch (Throwable e) {
e.printStackTrace();
throw new RuntimeException("aborting test at slot " + slot + ": ", e);
} finally {
System.out.println("records : " + recordCount);
System.out.println("deletes : " + deletes);
System.out.println("inserts : " + inserts);
System.out.println("updates : " + updates);
System.out.println("fetches : " + fetches);
System.out.println("rootget : " + rootgets);
System.out.println("rootset : " + rootsets);
int totalSize = 0;
for (int i = 0; i < RECORDS; i++)
if (d[i] != null)
totalSize += d[i].size;
System.out.println("total outstanding size: " + totalSize);
//System.out.println("---");
//for (int i = 0; i < RECORDS; i++)
// if (d[i] != null)
// System.out.println("slot " + i + ": " + d[i]);
}
}
}