2
* See the file LICENSE for redistribution information.
4
* Copyright (c) 1997-2002
5
* Sleepycat Software. All rights reserved.
10
#include <sys/types.h>
24
typedef enum { ACCOUNT, BRANCH, TELLER } FTYPE;
26
static int invarg(int, char *);
27
u_int32_t random_id(FTYPE, u_int32_t, u_int32_t, u_int32_t);
28
u_int32_t random_int(u_int32_t, u_int32_t);
29
static int usage(void);
32
const char *progname = "TpcbExample"; // Program name.
34
class TpcbExample : public DbEnv
37
void populate(int, int, int, int);
38
void run(int, int, int, int);
39
int txn(Db *, Db *, Db *, Db *,
41
void populateHistory(Db *, int, u_int32_t, u_int32_t, u_int32_t);
42
void populateTable(Db *, u_int32_t, u_int32_t, int, const char *);
44
// Note: the constructor creates a DbEnv(), which is
45
// not fully initialized until the DbEnv::open() method
48
TpcbExample(const char *home, int cachesize,
49
int initializing, int flags);
52
static const char FileName[];
54
// no need for copy and assignment
55
TpcbExample(const TpcbExample &);
56
void operator = (const TpcbExample &);
60
// This program implements a basic TPC/B driver program. To create the
61
// TPC/B database, run with the -i (init) flag. The number of records
62
// with which to populate the account, history, branch, and teller tables
63
// is specified by the a, s, b, and t flags respectively. To run a TPC/B
64
// test, use the n flag to indicate a number of transactions to run (note
65
// that you can run many of these processes in parallel to simulate a
66
// multiuser test run).
68
#define TELLERS_PER_BRANCH 100
69
#define ACCOUNTS_PER_TELLER 1000
70
#define HISTORY_PER_BRANCH 2592000
73
* The default configuration that adheres to TPCB scaling rules requires
74
* nearly 3 GB of space. To avoid requiring that much space for testing,
75
* we set the parameters much lower. If you want to run a valid 10 TPS
76
* configuration, define VALID_SCALING.
79
#define ACCOUNTS 1000000
82
#define HISTORY 25920000
92
#if !defined(VALID_SCALING) && !defined(TINY)
93
#define ACCOUNTS 100000
96
#define HISTORY 259200
99
#define HISTORY_LEN 100
101
#define BEGID 1000000
106
u_int8_t pad[RECLEN - sizeof(u_int32_t) - sizeof(u_int32_t)];
114
u_int8_t pad[RECLEN - 4 * sizeof(u_int32_t)];
118
main(int argc, char *argv[])
121
int accounts, branches, tellers, history;
122
int iflag, mpool, ntxns, txn_no_sync;
127
accounts = branches = history = tellers = 0;
132
seed = (unsigned long)time(NULL);
134
for (int i = 1; i < argc; ++i) {
136
if (strcmp(argv[i], "-a") == 0) {
137
// Number of account records
138
if ((accounts = atoi(argv[++i])) <= 0)
139
return (invarg('a', argv[i]));
141
else if (strcmp(argv[i], "-b") == 0) {
142
// Number of branch records
143
if ((branches = atoi(argv[++i])) <= 0)
144
return (invarg('b', argv[i]));
146
else if (strcmp(argv[i], "-c") == 0) {
147
// Cachesize in bytes
148
if ((mpool = atoi(argv[++i])) <= 0)
149
return (invarg('c', argv[i]));
151
else if (strcmp(argv[i], "-f") == 0) {
152
// Fast mode: no txn sync.
155
else if (strcmp(argv[i], "-h") == 0) {
159
else if (strcmp(argv[i], "-i") == 0) {
160
// Initialize the test.
163
else if (strcmp(argv[i], "-n") == 0) {
164
// Number of transactions
165
if ((ntxns = atoi(argv[++i])) <= 0)
166
return (invarg('n', argv[i]));
168
else if (strcmp(argv[i], "-S") == 0) {
169
// Random number seed.
170
seed = strtoul(argv[++i], &endarg, 0);
172
return (invarg('S', argv[i]));
174
else if (strcmp(argv[i], "-s") == 0) {
175
// Number of history records
176
if ((history = atoi(argv[++i])) <= 0)
177
return (invarg('s', argv[i]));
179
else if (strcmp(argv[i], "-t") == 0) {
180
// Number of teller records
181
if ((tellers = atoi(argv[++i])) <= 0)
182
return (invarg('t', argv[i]));
184
else if (strcmp(argv[i], "-v") == 0) {
193
srand((unsigned int)seed);
195
accounts = accounts == 0 ? ACCOUNTS : accounts;
196
branches = branches == 0 ? BRANCHES : branches;
197
tellers = tellers == 0 ? TELLERS : tellers;
198
history = history == 0 ? HISTORY : history;
201
cout << (long)accounts << " Accounts, "
202
<< (long)branches << " Branches, "
203
<< (long)tellers << " Tellers, "
204
<< (long)history << " History\n";
207
// Initialize the database environment.
208
// Must be done in within a try block, unless you
209
// change the error model in the environment options.
211
TpcbExample app(home, mpool, iflag,
212
txn_no_sync ? DB_TXN_NOSYNC : 0);
217
app.populate(accounts, branches, history, tellers);
222
app.run(ntxns, accounts, branches, tellers);
226
return (EXIT_SUCCESS);
228
catch (DbException &dbe) {
229
cerr << "TpcbExample: " << dbe.what() << "\n";
230
return (EXIT_FAILURE);
235
invarg(int arg, char *str)
237
cerr << "TpcbExample: invalid argument for -"
238
<< (char)arg << ": " << str << "\n";
239
return (EXIT_FAILURE);
245
cerr << "usage: TpcbExample [-fiv] [-a accounts] [-b branches]\n"
246
<< " [-c cachesize] [-h home] [-n transactions ]\n"
247
<< " [-S seed] [-s history] [-t tellers]\n";
248
return (EXIT_FAILURE);
251
TpcbExample::TpcbExample(const char *home, int cachesize,
252
int initializing, int flags)
255
u_int32_t local_flags;
257
set_error_stream(&cerr);
258
set_errpfx("TpcbExample");
259
(void)set_cachesize(0, cachesize == 0 ?
260
4 * 1024 * 1024 : (u_int32_t)cachesize, 0);
262
if (flags & (DB_TXN_NOSYNC))
263
set_flags(DB_TXN_NOSYNC, 1);
264
flags &= ~(DB_TXN_NOSYNC);
266
local_flags = flags | DB_CREATE | DB_INIT_MPOOL;
268
local_flags |= DB_INIT_TXN | DB_INIT_LOCK | DB_INIT_LOG;
269
open(home, local_flags, 0);
273
// Initialize the database to the specified number of accounts, branches,
274
// history records, and tellers.
277
TpcbExample::populate(int accounts, int branches, int history, int tellers)
282
u_int32_t balance, idnum;
283
u_int32_t end_anum, end_bnum, end_tnum;
284
u_int32_t start_anum, start_bnum, start_tnum;
289
dbp = new Db(this, 0);
290
dbp->set_h_nelem((unsigned int)accounts);
292
if ((err = dbp->open(NULL, "account", NULL, DB_HASH,
293
DB_CREATE | DB_TRUNCATE, 0644)) != 0) {
294
DbException except("Account file create failed", err);
299
populateTable(dbp, idnum, balance, accounts, "account");
301
end_anum = idnum - 1;
302
if ((err = dbp->close(0)) != 0) {
303
DbException except("Account file close failed", err);
308
cout << "Populated accounts: "
309
<< (long)start_anum << " - " << (long)end_anum << "\n";
311
dbp = new Db(this, 0);
313
// Since the number of branches is very small, we want to use very
314
// small pages and only 1 key per page. This is the poor-man's way
315
// of getting key locking instead of page locking.
317
dbp->set_h_ffactor(1);
318
dbp->set_h_nelem((unsigned int)branches);
319
dbp->set_pagesize(512);
321
if ((err = dbp->open(NULL, "branch", NULL, DB_HASH,
322
DB_CREATE | DB_TRUNCATE, 0644)) != 0) {
323
DbException except("Branch file create failed", err);
327
populateTable(dbp, idnum, balance, branches, "branch");
329
end_bnum = idnum - 1;
330
if ((err = dbp->close(0)) != 0) {
331
DbException except("Close of branch file failed", err);
337
cout << "Populated branches: "
338
<< (long)start_bnum << " - " << (long)end_bnum << "\n";
340
dbp = new Db(this, 0);
342
// In the case of tellers, we also want small pages, but we'll let
343
// the fill factor dynamically adjust itself.
345
dbp->set_h_ffactor(0);
346
dbp->set_h_nelem((unsigned int)tellers);
347
dbp->set_pagesize(512);
349
if ((err = dbp->open(NULL, "teller", NULL, DB_HASH,
350
DB_CREATE | DB_TRUNCATE, 0644)) != 0) {
351
DbException except("Teller file create failed", err);
356
populateTable(dbp, idnum, balance, tellers, "teller");
358
end_tnum = idnum - 1;
359
if ((err = dbp->close(0)) != 0) {
360
DbException except("Close of teller file failed", err);
365
cout << "Populated tellers: "
366
<< (long)start_tnum << " - " << (long)end_tnum << "\n";
368
dbp = new Db(this, 0);
369
dbp->set_re_len(HISTORY_LEN);
370
if ((err = dbp->open(NULL, "history", NULL, DB_RECNO,
371
DB_CREATE | DB_TRUNCATE, 0644)) != 0) {
372
DbException except("Create of history file failed", err);
376
populateHistory(dbp, history, accounts, branches, tellers);
377
if ((err = dbp->close(0)) != 0) {
378
DbException except("Close of history file failed", err);
385
TpcbExample::populateTable(Db *dbp,
386
u_int32_t start_id, u_int32_t balance,
387
int nrecs, const char *msg)
390
memset(&drec.pad[0], 1, sizeof(drec.pad));
392
Dbt kdbt(&drec.id, sizeof(u_int32_t));
393
Dbt ddbt(&drec, sizeof(drec));
395
for (int i = 0; i < nrecs; i++) {
396
drec.id = start_id + (u_int32_t)i;
397
drec.balance = balance;
400
dbp->put(NULL, &kdbt, &ddbt, DB_NOOVERWRITE)) != 0) {
401
cerr << "Failure initializing " << msg << " file: "
402
<< strerror(err) << "\n";
403
DbException except("failure initializing file", err);
410
TpcbExample::populateHistory(Db *dbp, int nrecs, u_int32_t accounts,
411
u_int32_t branches, u_int32_t tellers)
414
memset(&hrec.pad[0], 1, sizeof(hrec.pad));
418
Dbt kdbt(&key, sizeof(u_int32_t));
419
Dbt ddbt(&hrec, sizeof(hrec));
421
for (int i = 1; i <= nrecs; i++) {
422
hrec.aid = random_id(ACCOUNT, accounts, branches, tellers);
423
hrec.bid = random_id(BRANCH, accounts, branches, tellers);
424
hrec.tid = random_id(TELLER, accounts, branches, tellers);
428
if ((err = dbp->put(NULL, &kdbt, &ddbt, DB_APPEND)) != 0) {
429
DbException except("failure initializing history file",
437
random_int(u_int32_t lo, u_int32_t hi)
443
ret = (u_int32_t)(((double)t / ((double)(RAND_MAX) + 1)) *
450
random_id(FTYPE type, u_int32_t accounts, u_int32_t branches, u_int32_t tellers)
452
u_int32_t min, max, num;
469
return (random_int(min, max));
473
TpcbExample::run(int n, int accounts, int branches, int tellers)
475
Db *adb, *bdb, *hdb, *tdb;
477
int failed, ifailed, ret, txns;
478
time_t starttime, curtime, lasttime;
481
// Open the database files.
485
adb = new Db(this, 0);
486
if ((err = adb->open(NULL, "account", NULL, DB_UNKNOWN,
487
DB_AUTO_COMMIT, 0)) != 0) {
488
DbException except("Open of account file failed", err);
492
bdb = new Db(this, 0);
493
if ((err = bdb->open(NULL, "branch", NULL, DB_UNKNOWN,
494
DB_AUTO_COMMIT, 0)) != 0) {
495
DbException except("Open of branch file failed", err);
499
tdb = new Db(this, 0);
500
if ((err = tdb->open(NULL, "teller", NULL, DB_UNKNOWN,
501
DB_AUTO_COMMIT, 0)) != 0) {
502
DbException except("Open of teller file failed", err);
506
hdb = new Db(this, 0);
507
if ((err = hdb->open(NULL, "history", NULL, DB_UNKNOWN,
508
DB_AUTO_COMMIT, 0)) != 0) {
509
DbException except("Open of history file failed", err);
513
txns = failed = ifailed = 0;
514
starttime = time(NULL);
515
lasttime = starttime;
518
ret = txn(adb, bdb, tdb, hdb, accounts, branches, tellers);
524
curtime = time(NULL);
525
gtps = (double)(txns - failed) / (curtime - starttime);
526
itps = (double)(5000 - ifailed) / (curtime - lasttime);
528
// We use printf because it provides much simpler
529
// formatting than iostreams.
531
printf("%d txns %d failed ", txns, failed);
532
printf("%6.2f TPS (gross) %6.2f TPS (interval)\n",
544
cout << (long)txns << " transactions begun "
545
<< (long)failed << " failed\n";
549
// XXX Figure out the appropriate way to pick out IDs.
552
TpcbExample::txn(Db *adb, Db *bdb, Db *tdb, Db *hdb,
553
int accounts, int branches, int tellers)
563
int account, branch, teller, ret;
568
Dbt k_histdbt(&key, sizeof(key));
571
// XXX We could move a lot of this into the driver to make this
574
account = random_id(ACCOUNT, accounts, branches, tellers);
575
branch = random_id(BRANCH, accounts, branches, tellers);
576
teller = random_id(TELLER, accounts, branches, tellers);
578
k_dbt.set_size(sizeof(int));
580
d_dbt.set_flags(DB_DBT_USERMEM);
581
d_dbt.set_data(&rec);
582
d_dbt.set_ulen(sizeof(rec));
588
// Request 0 bytes since we're just positioning.
589
d_histdbt.set_flags(DB_DBT_PARTIAL);
592
if (txn_begin(NULL, &t, 0) != 0)
595
if (adb->cursor(t, &acurs, 0) != 0 ||
596
bdb->cursor(t, &bcurs, 0) != 0 ||
597
tdb->cursor(t, &tcurs, 0) != 0)
601
k_dbt.set_data(&account);
602
if (acurs->get(&k_dbt, &d_dbt, DB_SET) != 0)
605
if (acurs->put(&k_dbt, &d_dbt, DB_CURRENT) != 0)
609
k_dbt.set_data(&branch);
610
if (bcurs->get(&k_dbt, &d_dbt, DB_SET) != 0)
613
if (bcurs->put(&k_dbt, &d_dbt, DB_CURRENT) != 0)
617
k_dbt.set_data(&teller);
618
if (tcurs->get(&k_dbt, &d_dbt, DB_SET) != 0)
621
if (tcurs->put(&k_dbt, &d_dbt, DB_CURRENT) != 0)
625
d_histdbt.set_flags(0);
626
d_histdbt.set_data(&hrec);
627
d_histdbt.set_ulen(sizeof(hrec));
628
if (hdb->put(t, &k_histdbt, &d_histdbt, DB_APPEND) != 0)
631
if (acurs->close() != 0 || bcurs->close() != 0 || tcurs->close() != 0)
644
(void)acurs->close();
646
(void)bcurs->close();
648
(void)tcurs->close();
653
cout << "Transaction A=" << (long)account
654
<< " B=" << (long)branch
655
<< " T=" << (long)teller << " failed\n";