2 * See the file LICENSE for redistribution information.
4 * Copyright (c) 1997-2009 Oracle. All rights reserved.
18 #include "dbstl_vector.h"
19 #include "dbstl_map.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 = "StlTpcbExample"; // Program name.
34 // Forward declared data classes
38 typedef dbstl::db_map<u_int32_t, Defrec > DefrecMap;
39 typedef dbstl::db_vector<Histrec > HistrecVector;
41 class StlTpcbExample : public DbEnv
44 void populate(int, int, int, int);
45 void run(int, int, int, int);
46 int txn(DefrecMap *, DefrecMap *, DefrecMap *, HistrecVector *,
47 int accounts, int branches, int tellers);
49 HistrecVector *, int, u_int32_t, u_int32_t, u_int32_t);
51 DefrecMap *, u_int32_t, u_int32_t, int, const char *);
53 // Note: the constructor creates a DbEnv(), which is
54 // not fully initialized until the DbEnv::open() method
57 StlTpcbExample(const char *home, int cachesize, int flags);
60 static const char FileName[];
62 // no need for copy and assignment
63 StlTpcbExample(const StlTpcbExample &);
64 void operator = (const StlTpcbExample &);
68 // This program implements a basic TPC/B driver program. To create the
69 // TPC/B database, run with the -i (init) flag. The number of records
70 // with which to populate the account, history, branch, and teller tables
71 // is specified by the a, s, b, and t flags respectively. To run a TPC/B
72 // test, use the n flag to indicate a number of transactions to run (note
73 // that you can run many of these processes in parallel to simulate a
74 // multiuser test run).
76 #define TELLERS_PER_BRANCH 100
77 #define ACCOUNTS_PER_TELLER 1000
78 #define HISTORY_PER_BRANCH 2592000
81 * The default configuration that adheres to TPCB scaling rules requires
82 * nearly 3 GB of space. To avoid requiring that much space for testing,
83 * we set the parameters much lower. If you want to run a valid 10 TPS
84 * configuration, define VALID_SCALING.
87 #define ACCOUNTS 1000000
90 #define HISTORY 25920000
100 #if !defined(VALID_SCALING) && !defined(TINY)
101 #define ACCOUNTS 100000
104 #define HISTORY 259200
107 #define HISTORY_LEN 100
109 #define BEGID 1000000
115 u_int8_t pad[RECLEN - sizeof(u_int32_t) - sizeof(u_int32_t)];
124 u_int8_t pad[RECLEN - 4 * sizeof(u_int32_t)];
128 main(int argc, char *argv[])
131 int accounts, branches, tellers, history;
132 int iflag, mpool, ntxns, txn_no_sync;
137 accounts = branches = history = tellers = 0;
142 seed = (unsigned long)time(NULL);
144 for (int i = 1; i < argc; ++i) {
146 if (strcmp(argv[i], "-a") == 0) {
147 // Number of account records
148 if ((accounts = atoi(argv[++i])) <= 0)
149 return (invarg('a', argv[i]));
151 else if (strcmp(argv[i], "-b") == 0) {
152 // Number of branch records
153 if ((branches = atoi(argv[++i])) <= 0)
154 return (invarg('b', argv[i]));
156 else if (strcmp(argv[i], "-c") == 0) {
157 // Cachesize in bytes
158 if ((mpool = atoi(argv[++i])) <= 0)
159 return (invarg('c', argv[i]));
161 else if (strcmp(argv[i], "-f") == 0) {
162 // Fast mode: no txn sync.
165 else if (strcmp(argv[i], "-h") == 0) {
169 else if (strcmp(argv[i], "-i") == 0) {
170 // Initialize the test.
173 else if (strcmp(argv[i], "-n") == 0) {
174 // Number of transactions
175 if ((ntxns = atoi(argv[++i])) <= 0)
176 return (invarg('n', argv[i]));
178 else if (strcmp(argv[i], "-S") == 0) {
179 // Random number seed.
180 seed = strtoul(argv[++i], &endarg, 0);
182 return (invarg('S', argv[i]));
184 else if (strcmp(argv[i], "-s") == 0) {
185 // Number of history records
186 if ((history = atoi(argv[++i])) <= 0)
187 return (invarg('s', argv[i]));
189 else if (strcmp(argv[i], "-t") == 0) {
190 // Number of teller records
191 if ((tellers = atoi(argv[++i])) <= 0)
192 return (invarg('t', argv[i]));
194 else if (strcmp(argv[i], "-v") == 0) {
203 srand((unsigned int)seed);
205 accounts = accounts == 0 ? ACCOUNTS : accounts;
206 branches = branches == 0 ? BRANCHES : branches;
207 tellers = tellers == 0 ? TELLERS : tellers;
208 history = history == 0 ? HISTORY : history;
211 cout << (long)accounts << " Accounts, "
212 << (long)branches << " Branches, "
213 << (long)tellers << " Tellers, "
214 << (long)history << " History\n";
217 // Initialize the database environment.
218 // Must be done in within a try block, unless you
219 // change the error model in the environment options.
222 home, mpool, txn_no_sync ? DB_TXN_NOSYNC : 0);
227 app.populate(accounts, branches, history, tellers);
232 app.run(ntxns, accounts, branches, tellers);
236 return (EXIT_SUCCESS);
238 catch (DbException &dbe) {
239 cerr << "StlTpcbExample: " << dbe.what() << "\n";
240 return (EXIT_FAILURE);
245 invarg(int arg, char *str)
247 cerr << "StlTpcbExample: invalid argument for -"
248 << (char)arg << ": " << str << "\n";
249 return (EXIT_FAILURE);
255 cerr << "usage: StlTpcbExample [-fiv] [-a accounts] [-b branches]\n"
256 << " [-c cachesize] [-h home] [-n transactions]\n"
257 << " [-S seed] [-s history] [-t tellers]\n";
258 return (EXIT_FAILURE);
261 StlTpcbExample::StlTpcbExample(const char *home, int cachesize, int flags)
262 : DbEnv(DB_CXX_NO_EXCEPTIONS)
264 u_int32_t local_flags;
266 set_error_stream(&cerr);
267 set_errpfx("StlTpcbExample");
268 (void)set_lk_detect(DB_LOCK_DEFAULT);
269 (void)set_cachesize(0, cachesize == 0 ?
270 4 * 1024 * 1024 : (u_int32_t)cachesize, 0);
272 set_lk_max_lockers(1024 * 128);
273 set_lk_max_locks(1024 * 128);
274 set_lk_max_objects(1024 * 128);
275 if (flags & (DB_TXN_NOSYNC))
276 set_flags(DB_TXN_NOSYNC, 1);
277 flags &= ~(DB_TXN_NOSYNC);
279 local_flags = flags | DB_CREATE | DB_INIT_LOCK | DB_INIT_LOG |
280 DB_INIT_MPOOL | DB_INIT_TXN;
281 open(home, local_flags, 0);
282 dbstl::register_db_env(this);
286 // Initialize the database to the specified number of accounts, branches,
287 // history records, and tellers.
290 StlTpcbExample::populate(int accounts, int branches, int history, int tellers)
293 DefrecMap *accounts_map, *branches_map, *tellers_map;
294 HistrecVector *history_vector;
297 u_int32_t balance, idnum;
298 u_int32_t end_anum, end_bnum, end_tnum;
299 u_int32_t start_anum, start_bnum, start_tnum;
305 dbp = new Db(this, DB_CXX_NO_EXCEPTIONS);
306 dbp->set_h_nelem((unsigned int)accounts);
308 if ((err = dbp->open(NULL, "account", NULL,
309 DB_HASH, oflags, 0644)) != 0) {
310 DbException except("Account file create failed", err);
314 dbstl::register_db(dbp);
315 accounts_map = new DefrecMap(dbp, this);
317 populateTable(accounts_map, idnum, balance, accounts, "account");
319 end_anum = idnum - 1;
320 // Automatically closes the underlying database.
322 dbstl::close_db(dbp);
325 cout << "Populated accounts: "
326 << (long)start_anum << " - " << (long)end_anum << "\n";
328 dbp = new Db(this, DB_CXX_NO_EXCEPTIONS);
330 // Since the number of branches is very small, we want to use very
331 // small pages and only 1 key per page. This is the poor-man's way
332 // of getting key locking instead of page locking.
334 dbp->set_h_ffactor(1);
335 dbp->set_h_nelem((unsigned int)branches);
336 dbp->set_pagesize(512);
338 if ((err = dbp->open(NULL,
339 "branch", NULL, DB_HASH, oflags, 0644)) != 0) {
340 DbException except("Branch file create failed", err);
343 dbstl::register_db(dbp);
344 branches_map = new DefrecMap(dbp, this);
346 populateTable(branches_map, idnum, balance, branches, "branch");
348 end_bnum = idnum - 1;
350 dbstl::close_db(dbp);
354 cout << "Populated branches: "
355 << (long)start_bnum << " - " << (long)end_bnum << "\n";
357 dbp = new Db(this, DB_CXX_NO_EXCEPTIONS);
359 // In the case of tellers, we also want small pages, but we'll let
360 // the fill factor dynamically adjust itself.
362 dbp->set_h_ffactor(0);
363 dbp->set_h_nelem((unsigned int)tellers);
364 dbp->set_pagesize(512);
366 if ((err = dbp->open(NULL,
367 "teller", NULL, DB_HASH, oflags, 0644)) != 0) {
368 DbException except("Teller file create failed", err);
372 dbstl::register_db(dbp);
373 tellers_map = new DefrecMap(dbp, this);
375 populateTable(tellers_map, idnum, balance, tellers, "teller");
377 end_tnum = idnum - 1;
379 dbstl::close_db(dbp);
382 cout << "Populated tellers: "
383 << (long)start_tnum << " - " << (long)end_tnum << "\n";
385 dbp = new Db(this, DB_CXX_NO_EXCEPTIONS);
386 dbp->set_re_len(HISTORY_LEN);
387 if ((err = dbp->open(NULL,
388 "history", NULL, DB_RECNO, oflags, 0644)) != 0) {
389 DbException except("Create of history file failed", err);
393 dbstl::register_db(dbp);
394 history_vector = new HistrecVector(dbp, this);
395 populateHistory(history_vector, history, accounts, branches, tellers);
396 delete history_vector;
397 dbstl::close_db(dbp);
402 StlTpcbExample::populateTable(DefrecMap *drm, u_int32_t start_id,
403 u_int32_t balance, int nrecs, const char *msg)
407 dbstl::pair<dbstl::db_map<u_int32_t, Defrec >::iterator, bool > ib;
409 memset(&drec.pad[0], 1, sizeof(drec.pad));
411 for (i = 0; i < nrecs; i++) {
412 drec.id = start_id + (u_int32_t)i;
413 drec.balance = balance;
414 ib = drm->insert(dbstl::make_pair(drec.id, drec));
415 if (ib.second == false)
416 throw "failed to insert record";
424 StlTpcbExample::populateHistory(HistrecVector *hrm, int nrecs,
425 u_int32_t accounts, u_int32_t branches,
431 memset(&hrec.pad[0], 1, sizeof(hrec.pad));
434 for (i = 1; i <= nrecs; i++) {
435 hrec.aid = random_id(
436 ACCOUNT, accounts, branches, tellers);
437 hrec.bid = random_id(
438 BRANCH, accounts, branches, tellers);
439 hrec.tid = random_id(
440 TELLER, accounts, branches, tellers);
441 hrm->push_back(hrec);
449 random_int(u_int32_t lo, u_int32_t hi)
455 ret = (u_int32_t)(((double)t / ((double)(RAND_MAX) + 1)) *
462 random_id(FTYPE type, u_int32_t accounts, u_int32_t branches, u_int32_t tellers)
464 u_int32_t min, max, num;
481 return (random_int(min, max));
485 StlTpcbExample::run(int n, int accounts, int branches, int tellers)
487 Db *adb, *bdb, *hdb, *tdb;
488 DefrecMap *accounts_map, *branches_map, *tellers_map;
489 HistrecVector *history_vector;
490 int failed, oflags, ret, txns;
491 time_t start_time, end_time;
494 // Open the database files.
496 oflags = DB_AUTO_COMMIT;
499 adb = new Db(this, DB_CXX_NO_EXCEPTIONS);
500 if ((err = adb->open(NULL,
501 "account", NULL, DB_UNKNOWN, oflags, 0)) != 0) {
502 DbException except("Open of account file failed", err);
505 dbstl::register_db(adb);
506 accounts_map = new DefrecMap(adb);
508 bdb = new Db(this, DB_CXX_NO_EXCEPTIONS);
509 if ((err = bdb->open(NULL,
510 "branch", NULL, DB_UNKNOWN, oflags, 0)) != 0) {
511 DbException except("Open of branch file failed", err);
514 dbstl::register_db(bdb);
515 branches_map = new DefrecMap(bdb);
517 tdb = new Db(this, DB_CXX_NO_EXCEPTIONS);
518 if ((err = tdb->open(NULL,
519 "teller", NULL, DB_UNKNOWN, oflags, 0)) != 0) {
520 DbException except("Open of teller file failed", err);
523 dbstl::register_db(tdb);
524 tellers_map = new DefrecMap(tdb);
526 hdb = new Db(this, DB_CXX_NO_EXCEPTIONS);
527 if ((err = hdb->open(NULL,
528 "history", NULL, DB_UNKNOWN, oflags, 0)) != 0) {
529 DbException except("Open of history file failed", err);
532 dbstl::register_db(hdb);
533 history_vector = new HistrecVector(hdb);
535 (void)time(&start_time);
536 for (txns = n, failed = 0; n-- > 0;)
537 if ((ret = txn(accounts_map, branches_map, tellers_map,
538 history_vector, accounts, branches, tellers)) != 0)
540 (void)time(&end_time);
541 if (end_time == start_time)
543 // We use printf because it provides much simpler
544 // formatting than iostreams.
546 printf("%s: %d txns: %d failed, %d sec, %.2f TPS\n", progname,
547 txns, failed, (int)(end_time - start_time),
548 (txns - failed) / (double)(end_time - start_time));
553 delete history_vector;
554 dbstl::close_all_dbs();
558 // XXX Figure out the appropriate way to pick out IDs.
561 StlTpcbExample::txn(DefrecMap *accounts_map, DefrecMap *branches_map,
562 DefrecMap *tellers_map, HistrecVector *history_vector,
563 int accounts, int branches, int tellers)
566 DefrecMap::value_type_wrap::second_type recref, recref2, recref3;
567 int account, branch, teller;
571 * This is sample code -- we could move a lot of this into the driver
574 account = random_id(ACCOUNT, accounts, branches, tellers);
575 branch = random_id(BRANCH, accounts, branches, tellers);
576 teller = random_id(TELLER, accounts, branches, tellers);
584 * START PER-TRANSACTION TIMING.
586 * Technically, TPCB requires a limit on response time, you only get
587 * to count transactions that complete within 2 seconds. That's not
588 * an issue for this sample application -- regardless, here's where
589 * the transaction begins.
592 dbstl::begin_txn(0, this);
595 recref = (*accounts_map)[account];
596 recref.balance += 10;
597 recref._DB_STL_StoreElement();
600 recref2 = (*branches_map)[branch];
601 recref2.balance += 10;
602 recref2._DB_STL_StoreElement();
605 recref3 = (*tellers_map)[teller];
606 recref3.balance += 10;
607 recref3._DB_STL_StoreElement();
610 history_vector->push_back(hrec);
611 dbstl::commit_txn(this);
612 /* END PER-TRANSACTION TIMING. */
615 } catch (DbDeadlockException) {
616 dbstl::abort_txn(this);
618 cout << "Transaction A=" << (long)account
619 << " B=" << (long)branch
620 << " T=" << (long)teller << " failed\n";
621 return (DB_LOCK_DEADLOCK);
623 dbstl::abort_txn(this);