Main Page | Class Hierarchy | Data Structures | Directories | File List | Data Fields | Related Pages

TpcbExample.cpp

00001 /*-
00002  * See the file LICENSE for redistribution information.
00003  *
00004  * Copyright (c) 1997-2005
00005  *      Sleepycat Software.  All rights reserved.
00006  *
00007  * $Id: TpcbExample.cpp,v 12.3 2005/09/22 03:53:46 mjc Exp $
00008  */
00009 
00010 #include <sys/types.h>
00011 
00012 #include <errno.h>
00013 #include <stdlib.h>
00014 #include <string.h>
00015 #include <time.h>
00016 
00017 #include <iostream>
00018 #include <iomanip>
00019 #include <db_cxx.h>
00020 
00021 using std::cout;
00022 using std::cerr;
00023 
00024 typedef enum { ACCOUNT, BRANCH, TELLER } FTYPE;
00025 
00026 static int        invarg(int, char *);
00027 u_int32_t random_id(FTYPE, u_int32_t, u_int32_t, u_int32_t);
00028 u_int32_t random_int(u_int32_t, u_int32_t);
00029 static int        usage(void);
00030 
00031 int verbose;
00032 const char *progname = "TpcbExample";                       // Program name.
00033 
00034 class TpcbExample : public DbEnv
00035 {
00036 public:
00037         void populate(int, int, int, int);
00038         void run(int, int, int, int);
00039         int txn(Db *, Db *, Db *, Db *,
00040                 int, int, int);
00041         void populateHistory(Db *, int, u_int32_t, u_int32_t, u_int32_t);
00042         void populateTable(Db *, u_int32_t, u_int32_t, int, const char *);
00043 
00044         // Note: the constructor creates a DbEnv(), which is
00045         // not fully initialized until the DbEnv::open() method
00046         // is called.
00047         //
00048         TpcbExample(const char *home, int cachesize, int flags);
00049 
00050 private:
00051         static const char FileName[];
00052 
00053         // no need for copy and assignment
00054         TpcbExample(const TpcbExample &);
00055         void operator = (const TpcbExample &);
00056 };
00057 
00058 //
00059 // This program implements a basic TPC/B driver program.  To create the
00060 // TPC/B database, run with the -i (init) flag.  The number of records
00061 // with which to populate the account, history, branch, and teller tables
00062 // is specified by the a, s, b, and t flags respectively.  To run a TPC/B
00063 // test, use the n flag to indicate a number of transactions to run (note
00064 // that you can run many of these processes in parallel to simulate a
00065 // multiuser test run).
00066 //
00067 #define TELLERS_PER_BRANCH      100
00068 #define ACCOUNTS_PER_TELLER     1000
00069 #define HISTORY_PER_BRANCH      2592000
00070 
00071 /*
00072  * The default configuration that adheres to TPCB scaling rules requires
00073  * nearly 3 GB of space.  To avoid requiring that much space for testing,
00074  * we set the parameters much lower.  If you want to run a valid 10 TPS
00075  * configuration, define VALID_SCALING.
00076  */
00077 #ifdef  VALID_SCALING
00078 #define ACCOUNTS         1000000
00079 #define BRANCHES              10
00080 #define TELLERS              100
00081 #define HISTORY         25920000
00082 #endif
00083 
00084 #ifdef  TINY
00085 #define ACCOUNTS            1000
00086 #define BRANCHES              10
00087 #define TELLERS              100
00088 #define HISTORY            10000
00089 #endif
00090 
00091 #if !defined(VALID_SCALING) && !defined(TINY)
00092 #define ACCOUNTS          100000
00093 #define BRANCHES              10
00094 #define TELLERS              100
00095 #define HISTORY           259200
00096 #endif
00097 
00098 #define HISTORY_LEN         100
00099 #define RECLEN              100
00100 #define BEGID           1000000
00101 
00102 struct Defrec {
00103         u_int32_t   id;
00104         u_int32_t   balance;
00105         u_int8_t    pad[RECLEN - sizeof(u_int32_t) - sizeof(u_int32_t)];
00106 };
00107 
00108 struct Histrec {
00109         u_int32_t   aid;
00110         u_int32_t   bid;
00111         u_int32_t   tid;
00112         u_int32_t   amount;
00113         u_int8_t    pad[RECLEN - 4 * sizeof(u_int32_t)];
00114 };
00115 
00116 int
00117 main(int argc, char *argv[])
00118 {
00119         unsigned long seed;
00120         int accounts, branches, tellers, history;
00121         int iflag, mpool, ntxns, txn_no_sync;
00122         const char *home;
00123         char *endarg;
00124 
00125         home = "TESTDIR";
00126         accounts = branches = history = tellers = 0;
00127         txn_no_sync = 0;
00128         mpool = ntxns = 0;
00129         verbose = 0;
00130         iflag = 0;
00131         seed = (unsigned long)time(NULL);
00132 
00133         for (int i = 1; i < argc; ++i) {
00134 
00135                 if (strcmp(argv[i], "-a") == 0) {
00136                         // Number of account records
00137                         if ((accounts = atoi(argv[++i])) <= 0)
00138                                 return (invarg('a', argv[i]));
00139                 }
00140                 else if (strcmp(argv[i], "-b") == 0) {
00141                         // Number of branch records
00142                         if ((branches = atoi(argv[++i])) <= 0)
00143                                 return (invarg('b', argv[i]));
00144                 }
00145                 else if (strcmp(argv[i], "-c") == 0) {
00146                         // Cachesize in bytes
00147                         if ((mpool = atoi(argv[++i])) <= 0)
00148                                 return (invarg('c', argv[i]));
00149                 }
00150                 else if (strcmp(argv[i], "-f") == 0) {
00151                         // Fast mode: no txn sync.
00152                         txn_no_sync = 1;
00153                 }
00154                 else if (strcmp(argv[i], "-h") == 0) {
00155                         // DB  home.
00156                         home = argv[++i];
00157                 }
00158                 else if (strcmp(argv[i], "-i") == 0) {
00159                         // Initialize the test.
00160                         iflag = 1;
00161                 }
00162                 else if (strcmp(argv[i], "-n") == 0) {
00163                         // Number of transactions
00164                         if ((ntxns = atoi(argv[++i])) <= 0)
00165                                 return (invarg('n', argv[i]));
00166                 }
00167                 else if (strcmp(argv[i], "-S") == 0) {
00168                         // Random number seed.
00169                         seed = strtoul(argv[++i], &endarg, 0);
00170                         if (*endarg != '\0')
00171                                 return (invarg('S', argv[i]));
00172                 }
00173                 else if (strcmp(argv[i], "-s") == 0) {
00174                         // Number of history records
00175                         if ((history = atoi(argv[++i])) <= 0)
00176                                 return (invarg('s', argv[i]));
00177                 }
00178                 else if (strcmp(argv[i], "-t") == 0) {
00179                         // Number of teller records
00180                         if ((tellers = atoi(argv[++i])) <= 0)
00181                                 return (invarg('t', argv[i]));
00182                 }
00183                 else if (strcmp(argv[i], "-v") == 0) {
00184                         // Verbose option.
00185                         verbose = 1;
00186                 }
00187                 else {
00188                         return (usage());
00189                 }
00190         }
00191 
00192         srand((unsigned int)seed);
00193 
00194         accounts = accounts == 0 ? ACCOUNTS : accounts;
00195         branches = branches == 0 ? BRANCHES : branches;
00196         tellers = tellers == 0 ? TELLERS : tellers;
00197         history = history == 0 ? HISTORY : history;
00198 
00199         if (verbose)
00200                 cout << (long)accounts << " Accounts, "
00201                      << (long)branches << " Branches, "
00202                      << (long)tellers << " Tellers, "
00203                      << (long)history << " History\n";
00204 
00205         try {
00206                 // Initialize the database environment.
00207                 // Must be done in within a try block, unless you
00208                 // change the error model in the environment options.
00209                 //
00210                 TpcbExample app(home, mpool, txn_no_sync ? DB_TXN_NOSYNC : 0);
00211 
00212                 if (iflag) {
00213                         if (ntxns != 0)
00214                                 return (usage());
00215                         app.populate(accounts, branches, history, tellers);
00216                 }
00217                 else {
00218                         if (ntxns == 0)
00219                                 return (usage());
00220                         app.run(ntxns, accounts, branches, tellers);
00221                 }
00222 
00223                 app.close(0);
00224                 return (EXIT_SUCCESS);
00225         }
00226         catch (DbException &dbe) {
00227                 cerr << "TpcbExample: " << dbe.what() << "\n";
00228                 return (EXIT_FAILURE);
00229         }
00230 }
00231 
00232 static int
00233 invarg(int arg, char *str)
00234 {
00235         cerr << "TpcbExample: invalid argument for -"
00236              << (char)arg << ": " << str << "\n";
00237         return (EXIT_FAILURE);
00238 }
00239 
00240 static int
00241 usage()
00242 {
00243         cerr << "usage: TpcbExample [-fiv] [-a accounts] [-b branches]\n"
00244              << "                   [-c cachesize] [-h home] [-n transactions ]\n"
00245              << "                   [-S seed] [-s history] [-t tellers]\n";
00246         return (EXIT_FAILURE);
00247 }
00248 
00249 TpcbExample::TpcbExample(const char *home, int cachesize, int flags)
00250 :       DbEnv(0)
00251 {
00252         u_int32_t local_flags;
00253 
00254         set_error_stream(&cerr);
00255         set_errpfx("TpcbExample");
00256         (void)set_cachesize(0, cachesize == 0 ?
00257                             4 * 1024 * 1024 : (u_int32_t)cachesize, 0);
00258 
00259         if (flags & (DB_TXN_NOSYNC))
00260                 set_flags(DB_TXN_NOSYNC, 1);
00261         flags &= ~(DB_TXN_NOSYNC);
00262 
00263         local_flags = flags | DB_CREATE | DB_INIT_LOCK | DB_INIT_LOG |
00264             DB_INIT_MPOOL | DB_INIT_TXN;
00265         open(home, local_flags, 0);
00266 }
00267 
00268 //
00269 // Initialize the database to the specified number of accounts, branches,
00270 // history records, and tellers.
00271 //
00272 void
00273 TpcbExample::populate(int accounts, int branches, int history, int tellers)
00274 {
00275         Db *dbp;
00276 
00277         int err;
00278         u_int32_t balance, idnum;
00279         u_int32_t end_anum, end_bnum, end_tnum;
00280         u_int32_t start_anum, start_bnum, start_tnum;
00281 
00282         idnum = BEGID;
00283         balance = 500000;
00284 
00285         dbp = new Db(this, 0);
00286         dbp->set_h_nelem((unsigned int)accounts);
00287 
00288         if ((err = dbp->open(NULL, "account", NULL, DB_HASH,
00289                              DB_CREATE, 0644)) != 0) {
00290                 DbException except("Account file create failed", err);
00291                 throw except;
00292         }
00293 
00294         start_anum = idnum;
00295         populateTable(dbp, idnum, balance, accounts, "account");
00296         idnum += accounts;
00297         end_anum = idnum - 1;
00298         if ((err = dbp->close(0)) != 0) {
00299                 DbException except("Account file close failed", err);
00300                 throw except;
00301         }
00302         delete dbp;
00303         if (verbose)
00304                 cout << "Populated accounts: "
00305                      << (long)start_anum << " - " << (long)end_anum << "\n";
00306 
00307         dbp = new Db(this, 0);
00308         //
00309         // Since the number of branches is very small, we want to use very
00310         // small pages and only 1 key per page.  This is the poor-man's way
00311         // of getting key locking instead of page locking.
00312         //
00313         dbp->set_h_ffactor(1);
00314         dbp->set_h_nelem((unsigned int)branches);
00315         dbp->set_pagesize(512);
00316 
00317         if ((err = dbp->open(NULL, "branch", NULL, DB_HASH,
00318                              DB_CREATE, 0644)) != 0) {
00319                 DbException except("Branch file create failed", err);
00320                 throw except;
00321         }
00322         start_bnum = idnum;
00323         populateTable(dbp, idnum, balance, branches, "branch");
00324         idnum += branches;
00325         end_bnum = idnum - 1;
00326         if ((err = dbp->close(0)) != 0) {
00327                 DbException except("Close of branch file failed", err);
00328                 throw except;
00329         }
00330         delete dbp;
00331 
00332         if (verbose)
00333                 cout << "Populated branches: "
00334                      << (long)start_bnum << " - " << (long)end_bnum << "\n";
00335 
00336         dbp = new Db(this, 0);
00337         //
00338         // In the case of tellers, we also want small pages, but we'll let
00339         // the fill factor dynamically adjust itself.
00340         //
00341         dbp->set_h_ffactor(0);
00342         dbp->set_h_nelem((unsigned int)tellers);
00343         dbp->set_pagesize(512);
00344 
00345         if ((err = dbp->open(NULL, "teller", NULL, DB_HASH,
00346                              DB_CREATE, 0644)) != 0) {
00347                 DbException except("Teller file create failed", err);
00348                 throw except;
00349         }
00350 
00351         start_tnum = idnum;
00352         populateTable(dbp, idnum, balance, tellers, "teller");
00353         idnum += tellers;
00354         end_tnum = idnum - 1;
00355         if ((err = dbp->close(0)) != 0) {
00356                 DbException except("Close of teller file failed", err);
00357                 throw except;
00358         }
00359         delete dbp;
00360         if (verbose)
00361                 cout << "Populated tellers: "
00362                      << (long)start_tnum << " - " << (long)end_tnum << "\n";
00363 
00364         dbp = new Db(this, 0);
00365         dbp->set_re_len(HISTORY_LEN);
00366         if ((err = dbp->open(NULL, "history", NULL, DB_RECNO,
00367                              DB_CREATE, 0644)) != 0) {
00368                 DbException except("Create of history file failed", err);
00369                 throw except;
00370         }
00371 
00372         populateHistory(dbp, history, accounts, branches, tellers);
00373         if ((err = dbp->close(0)) != 0) {
00374                 DbException except("Close of history file failed", err);
00375                 throw except;
00376         }
00377         delete dbp;
00378 }
00379 
00380 void
00381 TpcbExample::populateTable(Db *dbp,
00382                            u_int32_t start_id, u_int32_t balance,
00383                            int nrecs, const char *msg)
00384 {
00385         Defrec drec;
00386         memset(&drec.pad[0], 1, sizeof(drec.pad));
00387 
00388         Dbt kdbt(&drec.id, sizeof(u_int32_t));
00389         Dbt ddbt(&drec, sizeof(drec));
00390 
00391         for (int i = 0; i < nrecs; i++) {
00392                 drec.id = start_id + (u_int32_t)i;
00393                 drec.balance = balance;
00394                 int err;
00395                 if ((err =
00396                      dbp->put(NULL, &kdbt, &ddbt, DB_NOOVERWRITE)) != 0) {
00397                         cerr << "Failure initializing " << msg << " file: "
00398                              << strerror(err) << "\n";
00399                         DbException except("failure initializing file", err);
00400                         throw except;
00401                 }
00402         }
00403 }
00404 
00405 void
00406 TpcbExample::populateHistory(Db *dbp, int nrecs, u_int32_t accounts,
00407                              u_int32_t branches, u_int32_t tellers)
00408 {
00409         Histrec hrec;
00410         memset(&hrec.pad[0], 1, sizeof(hrec.pad));
00411         hrec.amount = 10;
00412         db_recno_t key;
00413 
00414         Dbt kdbt(&key, sizeof(u_int32_t));
00415         Dbt ddbt(&hrec, sizeof(hrec));
00416 
00417         for (int i = 1; i <= nrecs; i++) {
00418                 hrec.aid = random_id(ACCOUNT, accounts, branches, tellers);
00419                 hrec.bid = random_id(BRANCH, accounts, branches, tellers);
00420                 hrec.tid = random_id(TELLER, accounts, branches, tellers);
00421 
00422                 int err;
00423                 key = (db_recno_t)i;
00424                 if ((err = dbp->put(NULL, &kdbt, &ddbt, DB_APPEND)) != 0) {
00425                         DbException except("failure initializing history file",
00426                                            err);
00427                         throw except;
00428                 }
00429         }
00430 }
00431 
00432 u_int32_t
00433 random_int(u_int32_t lo, u_int32_t hi)
00434 {
00435         u_int32_t ret;
00436         int t;
00437 
00438         t = rand();
00439         ret = (u_int32_t)(((double)t / ((double)(RAND_MAX) + 1)) *
00440                           (hi - lo + 1));
00441         ret += lo;
00442         return (ret);
00443 }
00444 
00445 u_int32_t
00446 random_id(FTYPE type, u_int32_t accounts, u_int32_t branches, u_int32_t tellers)
00447 {
00448         u_int32_t min, max, num;
00449 
00450         max = min = BEGID;
00451         num = accounts;
00452         switch (type) {
00453         case TELLER:
00454                 min += branches;
00455                 num = tellers;
00456                 // Fallthrough
00457         case BRANCH:
00458                 if (type == BRANCH)
00459                         num = branches;
00460                 min += accounts;
00461                 // Fallthrough
00462         case ACCOUNT:
00463                 max = min + num - 1;
00464         }
00465         return (random_int(min, max));
00466 }
00467 
00468 void
00469 TpcbExample::run(int n, int accounts, int branches, int tellers)
00470 {
00471         Db *adb, *bdb, *hdb, *tdb;
00472         int failed, ret, txns;
00473         time_t start_time, end_time;
00474 
00475         //
00476         // Open the database files.
00477         //
00478 
00479         int err;
00480         adb = new Db(this, 0);
00481         if ((err = adb->open(NULL, "account", NULL, DB_UNKNOWN,
00482                              DB_AUTO_COMMIT, 0)) != 0) {
00483                 DbException except("Open of account file failed", err);
00484                 throw except;
00485         }
00486 
00487         bdb = new Db(this, 0);
00488         if ((err = bdb->open(NULL, "branch", NULL, DB_UNKNOWN,
00489                              DB_AUTO_COMMIT, 0)) != 0) {
00490                 DbException except("Open of branch file failed", err);
00491                 throw except;
00492         }
00493 
00494         tdb = new Db(this, 0);
00495         if ((err = tdb->open(NULL, "teller", NULL, DB_UNKNOWN,
00496                              DB_AUTO_COMMIT, 0)) != 0) {
00497                 DbException except("Open of teller file failed", err);
00498                 throw except;
00499         }
00500 
00501         hdb = new Db(this, 0);
00502         if ((err = hdb->open(NULL, "history", NULL, DB_UNKNOWN,
00503                              DB_AUTO_COMMIT, 0)) != 0) {
00504                 DbException except("Open of history file failed", err);
00505                 throw except;
00506         }
00507 
00508         (void)time(&start_time);
00509         for (txns = n, failed = 0; n-- > 0;)
00510                 if ((ret = txn(adb, bdb, tdb, hdb,
00511                     accounts, branches, tellers)) != 0)
00512                         ++failed;
00513         (void)time(&end_time);
00514         if (end_time == start_time)
00515                 ++end_time;
00516         // We use printf because it provides much simpler
00517         // formatting than iostreams.
00518         //
00519         printf("%s: %d txns: %d failed, %.2f TPS\n", progname, txns, failed,
00520             (txns - failed) / (double)(end_time - start_time));
00521 
00522         (void)adb->close(0);
00523         (void)bdb->close(0);
00524         (void)tdb->close(0);
00525         (void)hdb->close(0);
00526 }
00527 
00528 //
00529 // XXX Figure out the appropriate way to pick out IDs.
00530 //
00531 int
00532 TpcbExample::txn(Db *adb, Db *bdb, Db *tdb, Db *hdb,
00533                  int accounts, int branches, int tellers)
00534 {
00535         Dbc *acurs = NULL;
00536         Dbc *bcurs = NULL;
00537         Dbc *tcurs = NULL;
00538         DbTxn *t = NULL;
00539 
00540         db_recno_t key;
00541         Defrec rec;
00542         Histrec hrec;
00543         int account, branch, teller, ret;
00544 
00545         Dbt d_dbt;
00546         Dbt d_histdbt;
00547         Dbt k_dbt;
00548         Dbt k_histdbt(&key, sizeof(key));
00549 
00550         // !!!
00551         // This is sample code -- we could move a lot of this into the driver
00552         // to make it faster.
00553         //
00554         account = random_id(ACCOUNT, accounts, branches, tellers);
00555         branch = random_id(BRANCH, accounts, branches, tellers);
00556         teller = random_id(TELLER, accounts, branches, tellers);
00557 
00558         k_dbt.set_size(sizeof(int));
00559 
00560         d_dbt.set_flags(DB_DBT_USERMEM);
00561         d_dbt.set_data(&rec);
00562         d_dbt.set_ulen(sizeof(rec));
00563 
00564         hrec.aid = account;
00565         hrec.bid = branch;
00566         hrec.tid = teller;
00567         hrec.amount = 10;
00568         // Request 0 bytes since we're just positioning.
00569         d_histdbt.set_flags(DB_DBT_PARTIAL);
00570 
00571         // START PER-TRANSACTION TIMING.
00572         //
00573         // Technically, TPCB requires a limit on response time, you only get
00574         // to count transactions that complete within 2 seconds.  That's not
00575         // an issue for this sample application -- regardless, here's where
00576         // the transaction begins.
00577         if (txn_begin(NULL, &t, 0) != 0)
00578                 goto err;
00579 
00580         if (adb->cursor(t, &acurs, 0) != 0 ||
00581             bdb->cursor(t, &bcurs, 0) != 0 ||
00582             tdb->cursor(t, &tcurs, 0) != 0)
00583                 goto err;
00584 
00585         // Account record
00586         k_dbt.set_data(&account);
00587         if (acurs->get(&k_dbt, &d_dbt, DB_SET) != 0)
00588                 goto err;
00589         rec.balance += 10;
00590         if (acurs->put(&k_dbt, &d_dbt, DB_CURRENT) != 0)
00591                 goto err;
00592 
00593         // Branch record
00594         k_dbt.set_data(&branch);
00595         if (bcurs->get(&k_dbt, &d_dbt, DB_SET) != 0)
00596                 goto err;
00597         rec.balance += 10;
00598         if (bcurs->put(&k_dbt, &d_dbt, DB_CURRENT) != 0)
00599                 goto err;
00600 
00601         // Teller record
00602         k_dbt.set_data(&teller);
00603         if (tcurs->get(&k_dbt, &d_dbt, DB_SET) != 0)
00604                 goto err;
00605         rec.balance += 10;
00606         if (tcurs->put(&k_dbt, &d_dbt, DB_CURRENT) != 0)
00607                 goto err;
00608 
00609         // History record
00610         d_histdbt.set_flags(0);
00611         d_histdbt.set_data(&hrec);
00612         d_histdbt.set_ulen(sizeof(hrec));
00613         if (hdb->put(t, &k_histdbt, &d_histdbt, DB_APPEND) != 0)
00614                 goto err;
00615 
00616         if (acurs->close() != 0 || bcurs->close() != 0 || tcurs->close() != 0)
00617                 goto err;
00618 
00619         ret = t->commit(0);
00620         t = NULL;
00621         if (ret != 0)
00622                 goto err;
00623 
00624         // END PER-TRANSACTION TIMING.
00625         return (0);
00626 
00627 err:
00628         if (acurs != NULL)
00629                 (void)acurs->close();
00630         if (bcurs != NULL)
00631                 (void)bcurs->close();
00632         if (tcurs != NULL)
00633                 (void)tcurs->close();
00634         if (t != NULL)
00635                 (void)t->abort();
00636 
00637         if (verbose)
00638                 cout << "Transaction A=" << (long)account
00639                      << " B=" << (long)branch
00640                      << " T=" << (long)teller << " failed\n";
00641         return (-1);
00642 }

Generated on Sun Dec 25 12:14:26 2005 for Berkeley DB 4.4.16 by  doxygen 1.4.2