blob: 82c0f467defa0e6ec1e48e0520a4970ee1c4e8f6 [file]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to you 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.derbyTesting.system.oe.load;
import java.sql.Connection;
import java.sql.SQLException;
import javax.sql.DataSource;
import org.apache.derbyTesting.system.oe.client.Load;
/**
* Multi-threaded version of SimpleInsert.
* A number of threads execute the INSERT statements
* (each with their own connection).
* All the rows for a given warehouse are executed by
* one thread. The threads each get a warehouse to
* complete and then insert all the rows. Then they
* loop back to get the next warehouse. Warehouses
* are assigned to threads in a first come first served
* fashion. The main thread also inserts the ITEM table.
* <BR>
* By default the number of threads is the number
* of cpus on the machine, unless the scale to be loaded
* is larger than the number of threads. Then the number
* of threads will be equal to the scale. If the scale
* is one or the number of threads is one then the
* load is just like SimpleInsert.
* <BR>
* The number of threads can be set but if it
* it larger than the scale then the number of threads
* will be equal to the scale.
*
* <BR>
* It is assumed that foreign key constraints are
* created after the load.
*
*/
public class ThreadInsert extends SimpleInsert implements Runnable {
/**
* The main ThreadInsert, created by the main
* thread and holds the valid nextWarehouse.
*/
private final ThreadInsert master;
private SQLException loadExceptions;
private int threadCount;
private DataSource ds;
/**
* Next warehouse that needs to be populated.
*/
private short nextWarehouse = 1;
/**
* Create a ThreadInsert loader.
* @param ds getConnection() will be used to create connections
* for the spaened threads.
*/
public ThreadInsert(DataSource ds) {
super();
master = this;
this.ds = ds;
}
private ThreadInsert(ThreadInsert master) {
super();
this.master = master;
}
/**
* Initialize the load by calling the super-class's method
* and default the number of threads to the smaller of the number
* of cpus and the scale.
*/
public void setupLoad(Connection conn, short scale) throws SQLException {
super.setupLoad(conn, scale);
int cpuCount = Runtime.getRuntime().availableProcessors();
setThreadCount(cpuCount);
}
/**
* Set the thread count.
* If the scale is less than the number of threads
* then the number of threads inserting data will
* be equal to the scale.
*/
public void setThreadCount(int threadCount) {
if (scale == 1) {
this.threadCount = 1;
return;
}
if (scale < threadCount) {
this.threadCount = scale;
return;
}
this.threadCount = threadCount;
}
/**
* Populate the database.
*/
public void populateAllTables() throws Exception {
if (threadCount == 1)
super.populateAllTables();
else
threadPopulate();
}
/**
* Populate the database using multiple threads.
* The main thread (ie the one calling this method).
* is one of the threads that will insert the data
* It will handle the ITEM table by itself and
* its fair share of the remaining tables.
* @throws SQLException
* @throws InterruptedException
*
*/
private void threadPopulate() throws SQLException, InterruptedException {
Thread[] threads = new Thread[threadCount - 1];
for (int t = 1; t < threadCount; t++)
{
// Need to open a new connection.
Connection loaderConn = ds.getConnection();
ThreadInsert ti = new ThreadInsert(this);
ti.setSeed((seed * t) / 17);
ti.setupConnection(loaderConn, scale);
ti.setRandomGenerator();
Thread loader = new Thread(ti, "OELoader:"+t);
threads[t - 1] = loader;
loader.start();
}
// load item table
itemTable(1, Load.ITEM_COUNT);
// and my share of the warehouse based tables.
run();
for (int j = 0; j < threads.length; j++)
{
threads[j].join();
}
synchronized (this) {
if (loadExceptions != null)
throw loadExceptions;
}
}
/**
* Get the next warehouse to populate.
* If all have been populated then -1 is returned.
* Warehouse is one based.
*/
synchronized short getNextWarehouse() {
short next = nextWarehouse++;
if (next > scale)
return -1;
return next;
}
/**
* Save all the exceptions seen during load.
*/
synchronized void addException(SQLException sqle)
{
if (loadExceptions == null)
loadExceptions = sqle;
else
{
SQLException chain = loadExceptions;
for (;;)
{
SQLException e = chain.getNextException();
if (e != null) {
chain = e;
continue;
}
chain.setNextException(sqle);
break;
}
}
}
/**
* Run the load for a thread. Loop insert the data
* for a single warehouse while there are warehouses
* left to do.
*/
public void run() {
short w;
while ((w = master.getNextWarehouse()) != -1)
{
try {
populateForOneWarehouse(w);
} catch (SQLException e) {
master.addException(e);
break;
}
}
}
}