1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223
|
/*
Derby - Class org.apache.derbyTesting.perf.clients.BankTransactionClient
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.perf.clients;
import java.io.PrintStream;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Random;
/**
* This class implements a client thread which performs bank transactions. The
* transactions are intended to perform the same operations as the transactions
* specified by the TPC-B benchmark.
*/
public class BankTransactionClient implements Client {
/** Random number generator. */
private final Random random = new Random();
/** The number of branches in the database. */
private final int branches;
/** The number of tellers per branch. */
private final int tellersPerBranch;
/** The number of accounts per branch. */
private final int accountsPerBranch;
/** The connection on which the operations are performed. */
private Connection conn;
/** Statement that updates the balance of the account. */
private PreparedStatement updateAccount;
/** Statement that updated the history table. */
private PreparedStatement updateHistory;
/** Statement that updates the balance of the teller. */
private PreparedStatement updateTeller;
/** Statement that updated the balance of the branch. */
private PreparedStatement updateBranch;
/** Statement that retrieves the current account balance. */
private PreparedStatement retrieveAccountBalance;
/**
* Create a client that works on a database with the given number of
* branches, tellers and accounts.
*
* @param branches the number of branches in the database
* @param tellersPerBranch the number of tellers per branch
* @param accountsPerBranch the number of accounts per branch
*/
public BankTransactionClient(int branches, int tellersPerBranch,
int accountsPerBranch) {
if (branches <= 0 || tellersPerBranch <= 0 || accountsPerBranch <= 0) {
throw new IllegalArgumentException(
"all arguments must be greater than 0");
}
this.branches = branches;
this.tellersPerBranch = tellersPerBranch;
this.accountsPerBranch = accountsPerBranch;
}
/**
* Create a client that works on a database with the default number of
* tellers and accounts per branch.
*
* @param scale the scale factor for the database (equal to the number of
* branches)
*
* @see BankAccountFiller#BankAccountFiller(int)
*/
public BankTransactionClient(int scale) {
this(scale,
BankAccountFiller.DEFAULT_TELLERS_PER_BRANCH,
BankAccountFiller.DEFAULT_ACCOUNTS_PER_BRANCH);
}
/**
* Initialize the connection and the statements used by the test.
*/
public void init(Connection c) throws SQLException {
conn = c;
c.setAutoCommit(false);
updateAccount = c.prepareStatement(
"UPDATE " + BankAccountFiller.ACCOUNT_TABLE +
" SET ACCOUNT_BALANCE = ACCOUNT_BALANCE + ? WHERE ACCOUNT_ID = ?");
updateHistory = c.prepareStatement(
"INSERT INTO " + BankAccountFiller.HISTORY_TABLE +
"(ACCOUNT_ID, TELLER_ID, BRANCH_ID, AMOUNT, TIME_STAMP, " +
"EXTRA_DATA) VALUES (?, ?, ?, ?, CURRENT_TIMESTAMP, '" +
BankAccountFiller.createJunk(BankAccountFiller.HISTORY_EXTRA) +
"')");
updateTeller = c.prepareStatement(
"UPDATE " + BankAccountFiller.TELLER_TABLE +
" SET TELLER_BALANCE = TELLER_BALANCE + ? WHERE TELLER_ID = ?");
updateBranch = c.prepareStatement(
"UPDATE " + BankAccountFiller.BRANCH_TABLE +
" SET BRANCH_BALANCE = BRANCH_BALANCE + ? WHERE BRANCH_ID = ?");
retrieveAccountBalance = c.prepareStatement(
"SELECT ACCOUNT_BALANCE FROM " + BankAccountFiller.ACCOUNT_TABLE +
" WHERE ACCOUNT_ID = ?");
}
/**
* Perform a single transaction with a profile like the one specified in
* Clause 1.2 of the TPC-B specification.
*/
public void doWork() throws SQLException {
// Get the transaction input
final int tellerId = fetchTellerId();
final int branchId = fetchBranchId(tellerId);
final int accountId = fetchAccountId(branchId);
final int delta = fetchDelta();
// Update the account balance
updateAccount.setInt(1, delta);
updateAccount.setInt(2, accountId);
updateAccount.executeUpdate();
// Add a transaction log entry
updateHistory.setInt(1, accountId);
updateHistory.setInt(2, tellerId);
updateHistory.setInt(3, branchId);
updateHistory.setInt(4, delta);
updateHistory.executeUpdate();
// Update the teller balance
updateTeller.setInt(1, delta);
updateTeller.setInt(2, tellerId);
updateTeller.executeUpdate();
// Update the branch balance
updateBranch.setInt(1, delta);
updateBranch.setInt(2, branchId);
updateBranch.executeUpdate();
// Retrieve the balance
retrieveAccountBalance.setInt(1, accountId);
ResultSet rs = retrieveAccountBalance.executeQuery();
rs.next();
rs.getString(1);
rs.close();
conn.commit();
}
public void printReport(PrintStream out) {}
/**
* Generate a random teller id.
*/
private int fetchTellerId() {
return random.nextInt(tellersPerBranch * branches);
}
/**
* Find the branch the specified teller belongs to.
*
* @param tellerId the id of the teller
* @return the id of the branch for this teller
*/
private int fetchBranchId(int tellerId) {
return tellerId / tellersPerBranch;
}
/**
* Generate a random account id based on the specified branch. Per Clause
* 5.3.5 of the TPC-B specification, the accounts should be fetched from
* the selected branch 85% of the time (or always if that's the only
* branch), and from another branch the rest of the time.
*
* @param branchId the id of the selected branch
* @return the id of a random account
*/
private int fetchAccountId(int branchId) {
int branch;
if (branches == 1 || random.nextFloat() < 0.85f) {
// pick an account in the same branch
branch = branchId;
} else {
// pick an account in one of the other branches
branch = random.nextInt(branches - 1);
if (branch >= branchId) {
branch++;
}
}
// select a random account in the selected branch
return branch * accountsPerBranch + random.nextInt(accountsPerBranch);
}
/**
* Generate a random delta value between -99999 and +99999, both inclusive
* (TPC-B specification, Clause 5.3.6). The delta value specifies how much
* the balance should increase or decrease.
*
* @return a random value in the range [-99999,+99999]
*/
private int fetchDelta() {
return random.nextInt(199999) - 99999; // [-99999,+99999]
}
}
|