File: BankTransactionClient.java

package info (click to toggle)
derby 10.14.2.0-2
  • links: PTS, VCS
  • area: main
  • in suites: bookworm, bullseye
  • size: 78,896 kB
  • sloc: java: 691,930; sql: 42,686; xml: 20,511; sh: 3,373; sed: 96; makefile: 60
file content (223 lines) | stat: -rw-r--r-- 8,053 bytes parent folder | download | duplicates (4)
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]
    }
}