00001
00002
00003
00004
00005
00006
00007
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 #ifdef _WIN32
00018 extern int getopt(int, char * const *, const char *);
00019 #else
00020 #include <unistd.h>
00021 #endif
00022
00023 #include <db.h>
00024
00025 typedef enum { ACCOUNT, BRANCH, TELLER } FTYPE;
00026
00027 DB_ENV *db_init __P((const char *, const char *, int, u_int32_t));
00028 int hpopulate __P((DB *, int, int, int, int));
00029 int populate __P((DB *, u_int32_t, u_int32_t, int, const char *));
00030 u_int32_t random_id __P((FTYPE, int, int, int));
00031 u_int32_t random_int __P((u_int32_t, u_int32_t));
00032 int tp_populate __P((DB_ENV *, int, int, int, int, int));
00033 int tp_run __P((DB_ENV *, int, int, int, int, int));
00034 int tp_txn __P((DB_ENV *, DB *, DB *, DB *, DB *, int, int, int, int));
00035
00036 int invarg __P((const char *, int, const char *));
00037 int main __P((int, char *[]));
00038 int usage __P((const char *));
00039
00040
00041
00042
00043
00044
00045
00046
00047
00048
00049 #define TELLERS_PER_BRANCH 10
00050 #define ACCOUNTS_PER_TELLER 10000
00051 #define HISTORY_PER_BRANCH 2592000
00052
00053
00054
00055
00056
00057
00058
00059 #ifdef VALID_SCALING
00060 #define ACCOUNTS 1000000
00061 #define BRANCHES 10
00062 #define TELLERS 100
00063 #define HISTORY 25920000
00064 #endif
00065
00066 #ifdef TINY
00067 #define ACCOUNTS 1000
00068 #define BRANCHES 10
00069 #define TELLERS 100
00070 #define HISTORY 10000
00071 #endif
00072
00073 #ifdef VERY_TINY
00074 #define ACCOUNTS 500
00075 #define BRANCHES 10
00076 #define TELLERS 50
00077 #define HISTORY 5000
00078 #endif
00079
00080 #if !defined(VALID_SCALING) && !defined(TINY) && !defined(VERY_TINY)
00081 #define ACCOUNTS 100000
00082 #define BRANCHES 10
00083 #define TELLERS 100
00084 #define HISTORY 259200
00085 #endif
00086
00087 #define HISTORY_LEN 100
00088 #define RECLEN 100
00089 #define BEGID 1000000
00090
00091 typedef struct _defrec {
00092 u_int32_t id;
00093 u_int32_t balance;
00094 u_int8_t pad[RECLEN - sizeof(u_int32_t) - sizeof(u_int32_t)];
00095 } defrec;
00096
00097 typedef struct _histrec {
00098 u_int32_t aid;
00099 u_int32_t bid;
00100 u_int32_t tid;
00101 u_int32_t amount;
00102 u_int8_t pad[RECLEN - 4 * sizeof(u_int32_t)];
00103 } histrec;
00104
00105 char *progname = "ex_tpcb";
00106
00107 int
00108 main(argc, argv)
00109 int argc;
00110 char *argv[];
00111 {
00112 extern char *optarg;
00113 extern int optind;
00114 DB_ENV *dbenv;
00115 int accounts, branches, seed, tellers, history;
00116 int ch, iflag, mpool, ntxns, ret, txn_no_sync, verbose;
00117 const char *home;
00118
00119 home = "TESTDIR";
00120 accounts = branches = history = tellers = 0;
00121 iflag = mpool = ntxns = txn_no_sync = verbose = 0;
00122 seed = (int)time(NULL);
00123
00124 while ((ch = getopt(argc, argv, "a:b:c:fh:in:S:s:t:v")) != EOF)
00125 switch (ch) {
00126 case 'a':
00127 if ((accounts = atoi(optarg)) <= 0)
00128 return (invarg(progname, ch, optarg));
00129 break;
00130 case 'b':
00131 if ((branches = atoi(optarg)) <= 0)
00132 return (invarg(progname, ch, optarg));
00133 break;
00134 case 'c':
00135 if ((mpool = atoi(optarg)) <= 0)
00136 return (invarg(progname, ch, optarg));
00137 break;
00138 case 'f':
00139 txn_no_sync = 1;
00140 break;
00141 case 'h':
00142 home = optarg;
00143 break;
00144 case 'i':
00145 iflag = 1;
00146 break;
00147 case 'n':
00148 if ((ntxns = atoi(optarg)) <= 0)
00149 return (invarg(progname, ch, optarg));
00150 break;
00151 case 'S':
00152 if ((seed = atoi(optarg)) <= 0)
00153 return (invarg(progname, ch, optarg));
00154 break;
00155 case 's':
00156 if ((history = atoi(optarg)) <= 0)
00157 return (invarg(progname, ch, optarg));
00158 break;
00159 case 't':
00160 if ((tellers = atoi(optarg)) <= 0)
00161 return (invarg(progname, ch, optarg));
00162 break;
00163 case 'v':
00164 verbose = 1;
00165 break;
00166 case '?':
00167 default:
00168 return (usage(progname));
00169 }
00170 argc -= optind;
00171 argv += optind;
00172
00173 srand((u_int)seed);
00174
00175
00176 if ((dbenv = db_init(home,
00177 progname, mpool, txn_no_sync ? DB_TXN_NOSYNC : 0)) == NULL)
00178 return (EXIT_FAILURE);
00179
00180 accounts = accounts == 0 ? ACCOUNTS : accounts;
00181 branches = branches == 0 ? BRANCHES : branches;
00182 tellers = tellers == 0 ? TELLERS : tellers;
00183 history = history == 0 ? HISTORY : history;
00184
00185 if (verbose)
00186 printf("%ld Accounts, %ld Branches, %ld Tellers, %ld History\n",
00187 (long)accounts, (long)branches,
00188 (long)tellers, (long)history);
00189
00190 if (iflag) {
00191 if (ntxns != 0)
00192 return (usage(progname));
00193 tp_populate(dbenv,
00194 accounts, branches, history, tellers, verbose);
00195 } else {
00196 if (ntxns == 0)
00197 return (usage(progname));
00198 tp_run(dbenv, ntxns, accounts, branches, tellers, verbose);
00199 }
00200
00201 if ((ret = dbenv->close(dbenv, 0)) != 0) {
00202 fprintf(stderr, "%s: dbenv->close failed: %s\n",
00203 progname, db_strerror(ret));
00204 return (EXIT_FAILURE);
00205 }
00206
00207 return (EXIT_SUCCESS);
00208 }
00209
00210 int
00211 invarg(progname, arg, str)
00212 const char *progname;
00213 int arg;
00214 const char *str;
00215 {
00216 (void)fprintf(stderr,
00217 "%s: invalid argument for -%c: %s\n", progname, arg, str);
00218 return (EXIT_FAILURE);
00219 }
00220
00221 int
00222 usage(progname)
00223 const char *progname;
00224 {
00225 const char *a1, *a2;
00226
00227 a1 = "[-fv] [-a accounts] [-b branches]\n";
00228 a2 = "\t[-c cache_size] [-h home] [-S seed] [-s history] [-t tellers]";
00229 (void)fprintf(stderr, "usage: %s -i %s %s\n", progname, a1, a2);
00230 (void)fprintf(stderr,
00231 " %s -n transactions %s %s\n", progname, a1, a2);
00232 return (EXIT_FAILURE);
00233 }
00234
00235
00236
00237
00238
00239 DB_ENV *
00240 db_init(home, prefix, cachesize, flags)
00241 const char *home, *prefix;
00242 int cachesize;
00243 u_int32_t flags;
00244 {
00245 DB_ENV *dbenv;
00246 u_int32_t local_flags;
00247 int ret;
00248
00249 if ((ret = db_env_create(&dbenv, 0)) != 0) {
00250 dbenv->err(dbenv, ret, "db_env_create");
00251 return (NULL);
00252 }
00253 dbenv->set_errfile(dbenv, stderr);
00254 dbenv->set_errpfx(dbenv, prefix);
00255 (void)dbenv->set_cachesize(dbenv, 0,
00256 cachesize == 0 ? 4 * 1024 * 1024 : (u_int32_t)cachesize, 0);
00257
00258 if (flags & (DB_TXN_NOSYNC))
00259 (void)dbenv->set_flags(dbenv, DB_TXN_NOSYNC, 1);
00260 flags &= ~(DB_TXN_NOSYNC);
00261
00262 local_flags = flags | DB_CREATE | DB_INIT_LOCK | DB_INIT_LOG |
00263 DB_INIT_MPOOL | DB_INIT_TXN;
00264 if ((ret = dbenv->open(dbenv, home, local_flags, 0)) != 0) {
00265 dbenv->err(dbenv, ret, "DB_ENV->open: %s", home);
00266 (void)dbenv->close(dbenv, 0);
00267 return (NULL);
00268 }
00269 return (dbenv);
00270 }
00271
00272
00273
00274
00275
00276 int
00277 tp_populate(env, accounts, branches, history, tellers, verbose)
00278 DB_ENV *env;
00279 int accounts, branches, history, tellers, verbose;
00280 {
00281 DB *dbp;
00282 u_int32_t balance, idnum, oflags;
00283 u_int32_t end_anum, end_bnum, end_tnum;
00284 u_int32_t start_anum, start_bnum, start_tnum;
00285 int ret;
00286
00287 idnum = BEGID;
00288 balance = 500000;
00289 oflags = DB_CREATE;
00290
00291 if ((ret = db_create(&dbp, env, 0)) != 0) {
00292 env->err(env, ret, "db_create");
00293 return (1);
00294 }
00295 (void)dbp->set_h_nelem(dbp, (u_int32_t)accounts);
00296
00297 if ((ret = dbp->open(dbp, NULL, "account", NULL,
00298 DB_HASH, oflags, 0644)) != 0) {
00299 env->err(env, ret, "DB->open: account");
00300 return (1);
00301 }
00302
00303 start_anum = idnum;
00304 populate(dbp, idnum, balance, accounts, "account");
00305 idnum += accounts;
00306 end_anum = idnum - 1;
00307 if ((ret = dbp->close(dbp, 0)) != 0) {
00308 env->err(env, ret, "DB->close: account");
00309 return (1);
00310 }
00311 if (verbose)
00312 printf("Populated accounts: %ld - %ld\n",
00313 (long)start_anum, (long)end_anum);
00314
00315
00316
00317
00318
00319
00320 if ((ret = db_create(&dbp, env, 0)) != 0) {
00321 env->err(env, ret, "db_create");
00322 return (1);
00323 }
00324 (void)dbp->set_h_ffactor(dbp, 1);
00325 (void)dbp->set_h_nelem(dbp, (u_int32_t)branches);
00326 (void)dbp->set_pagesize(dbp, 512);
00327 if ((ret = dbp->open(dbp, NULL, "branch", NULL,
00328 DB_HASH, oflags, 0644)) != 0) {
00329 env->err(env, ret, "DB->open: branch");
00330 return (1);
00331 }
00332 start_bnum = idnum;
00333 populate(dbp, idnum, balance, branches, "branch");
00334 idnum += branches;
00335 end_bnum = idnum - 1;
00336 if ((ret = dbp->close(dbp, 0)) != 0) {
00337 env->err(env, ret, "DB->close: branch");
00338 return (1);
00339 }
00340 if (verbose)
00341 printf("Populated branches: %ld - %ld\n",
00342 (long)start_bnum, (long)end_bnum);
00343
00344
00345
00346
00347
00348 if ((ret = db_create(&dbp, env, 0)) != 0) {
00349 env->err(env, ret, "db_create");
00350 return (1);
00351 }
00352 (void)dbp->set_h_ffactor(dbp, 0);
00353 (void)dbp->set_h_nelem(dbp, (u_int32_t)tellers);
00354 (void)dbp->set_pagesize(dbp, 512);
00355 if ((ret = dbp->open(dbp, NULL, "teller", NULL,
00356 DB_HASH, oflags, 0644)) != 0) {
00357 env->err(env, ret, "DB->open: teller");
00358 return (1);
00359 }
00360
00361 start_tnum = idnum;
00362 populate(dbp, idnum, balance, tellers, "teller");
00363 idnum += tellers;
00364 end_tnum = idnum - 1;
00365 if ((ret = dbp->close(dbp, 0)) != 0) {
00366 env->err(env, ret, "DB->close: teller");
00367 return (1);
00368 }
00369 if (verbose)
00370 printf("Populated tellers: %ld - %ld\n",
00371 (long)start_tnum, (long)end_tnum);
00372
00373 if ((ret = db_create(&dbp, env, 0)) != 0) {
00374 env->err(env, ret, "db_create");
00375 return (1);
00376 }
00377 (void)dbp->set_re_len(dbp, HISTORY_LEN);
00378 if ((ret = dbp->open(dbp, NULL, "history", NULL,
00379 DB_RECNO, oflags, 0644)) != 0) {
00380 env->err(env, ret, "DB->open: history");
00381 return (1);
00382 }
00383
00384 hpopulate(dbp, history, accounts, branches, tellers);
00385 if ((ret = dbp->close(dbp, 0)) != 0) {
00386 env->err(env, ret, "DB->close: history");
00387 return (1);
00388 }
00389 return (0);
00390 }
00391
00392 int
00393 populate(dbp, start_id, balance, nrecs, msg)
00394 DB *dbp;
00395 u_int32_t start_id, balance;
00396 int nrecs;
00397 const char *msg;
00398 {
00399 DBT kdbt, ddbt;
00400 defrec drec;
00401 int i, ret;
00402
00403 kdbt.flags = 0;
00404 kdbt.data = &drec.id;
00405 kdbt.size = sizeof(u_int32_t);
00406 ddbt.flags = 0;
00407 ddbt.data = &drec;
00408 ddbt.size = sizeof(drec);
00409 memset(&drec.pad[0], 1, sizeof(drec.pad));
00410
00411 for (i = 0; i < nrecs; i++) {
00412 drec.id = start_id + (u_int32_t)i;
00413 drec.balance = balance;
00414 if ((ret =
00415 (dbp->put)(dbp, NULL, &kdbt, &ddbt, DB_NOOVERWRITE)) != 0) {
00416 dbp->err(dbp,
00417 ret, "Failure initializing %s file\n", msg);
00418 return (1);
00419 }
00420 }
00421 return (0);
00422 }
00423
00424 int
00425 hpopulate(dbp, history, accounts, branches, tellers)
00426 DB *dbp;
00427 int history, accounts, branches, tellers;
00428 {
00429 DBT kdbt, ddbt;
00430 histrec hrec;
00431 db_recno_t key;
00432 int i, ret;
00433
00434 memset(&kdbt, 0, sizeof(kdbt));
00435 memset(&ddbt, 0, sizeof(ddbt));
00436 ddbt.data = &hrec;
00437 ddbt.size = sizeof(hrec);
00438 kdbt.data = &key;
00439 kdbt.size = sizeof(key);
00440 memset(&hrec.pad[0], 1, sizeof(hrec.pad));
00441 hrec.amount = 10;
00442
00443 for (i = 1; i <= history; i++) {
00444 hrec.aid = random_id(ACCOUNT, accounts, branches, tellers);
00445 hrec.bid = random_id(BRANCH, accounts, branches, tellers);
00446 hrec.tid = random_id(TELLER, accounts, branches, tellers);
00447 if ((ret = dbp->put(dbp, NULL, &kdbt, &ddbt, DB_APPEND)) != 0) {
00448 dbp->err(dbp, ret, "dbp->put");
00449 return (1);
00450 }
00451 }
00452 return (0);
00453 }
00454
00455 u_int32_t
00456 random_int(lo, hi)
00457 u_int32_t lo, hi;
00458 {
00459 u_int32_t ret;
00460 int t;
00461
00462 #ifndef RAND_MAX
00463 #define RAND_MAX 0x7fffffff
00464 #endif
00465 t = rand();
00466 ret = (u_int32_t)(((double)t / ((double)(RAND_MAX) + 1)) *
00467 (hi - lo + 1));
00468 ret += lo;
00469 return (ret);
00470 }
00471
00472 u_int32_t
00473 random_id(type, accounts, branches, tellers)
00474 FTYPE type;
00475 int accounts, branches, tellers;
00476 {
00477 u_int32_t min, max, num;
00478
00479 max = min = BEGID;
00480 num = accounts;
00481 switch (type) {
00482 case TELLER:
00483 min += branches;
00484 num = tellers;
00485
00486 case BRANCH:
00487 if (type == BRANCH)
00488 num = branches;
00489 min += accounts;
00490
00491 case ACCOUNT:
00492 max = min + num - 1;
00493 }
00494 return (random_int(min, max));
00495 }
00496
00497 int
00498 tp_run(dbenv, n, accounts, branches, tellers, verbose)
00499 DB_ENV *dbenv;
00500 int n, accounts, branches, tellers, verbose;
00501 {
00502 DB *adb, *bdb, *hdb, *tdb;
00503 int failed, ret, txns;
00504 time_t start_time, end_time;
00505
00506 adb = bdb = hdb = tdb = NULL;
00507
00508
00509
00510
00511 if ((ret = db_create(&adb, dbenv, 0)) != 0) {
00512 dbenv->err(dbenv, ret, "db_create");
00513 goto err;
00514 }
00515 if ((ret = adb->open(adb, NULL, "account", NULL, DB_UNKNOWN,
00516 DB_AUTO_COMMIT, 0)) != 0) {
00517 dbenv->err(dbenv, ret, "DB->open: account");
00518 goto err;
00519 }
00520 if ((ret = db_create(&bdb, dbenv, 0)) != 0) {
00521 dbenv->err(dbenv, ret, "db_create");
00522 goto err;
00523 }
00524 if ((ret = bdb->open(bdb, NULL, "branch", NULL, DB_UNKNOWN,
00525 DB_AUTO_COMMIT, 0)) != 0) {
00526 dbenv->err(dbenv, ret, "DB->open: branch");
00527 goto err;
00528 }
00529 if ((ret = db_create(&hdb, dbenv, 0)) != 0) {
00530 dbenv->err(dbenv, ret, "db_create");
00531 goto err;
00532 }
00533 if ((ret = hdb->open(hdb, NULL, "history", NULL, DB_UNKNOWN,
00534 DB_AUTO_COMMIT, 0)) != 0) {
00535 dbenv->err(dbenv, ret, "DB->open: history");
00536 goto err;
00537 }
00538 if ((ret = db_create(&tdb, dbenv, 0)) != 0) {
00539 dbenv->err(dbenv, ret, "db_create");
00540 goto err;
00541 }
00542 if ((ret = tdb->open(tdb, NULL, "teller", NULL, DB_UNKNOWN,
00543 DB_AUTO_COMMIT, 0)) != 0) {
00544 dbenv->err(dbenv, ret, "DB->open: teller");
00545 goto err;
00546 }
00547
00548 (void)time(&start_time);
00549 for (txns = n, failed = 0; n-- > 0;)
00550 if ((ret = tp_txn(dbenv, adb, bdb, tdb, hdb,
00551 accounts, branches, tellers, verbose)) != 0)
00552 ++failed;
00553 (void)time(&end_time);
00554 if (end_time == start_time)
00555 ++end_time;
00556 printf("%s: %d txns: %d failed, %.2f TPS\n", progname,
00557 txns, failed, (txns - failed) / (double)(end_time - start_time));
00558
00559 err: if (adb != NULL)
00560 (void)adb->close(adb, 0);
00561 if (bdb != NULL)
00562 (void)bdb->close(bdb, 0);
00563 if (tdb != NULL)
00564 (void)tdb->close(tdb, 0);
00565 if (hdb != NULL)
00566 (void)hdb->close(hdb, 0);
00567 return (ret == 0 ? 0 : 1);
00568 }
00569
00570
00571
00572
00573 int
00574 tp_txn(dbenv, adb, bdb, tdb, hdb, accounts, branches, tellers, verbose)
00575 DB_ENV *dbenv;
00576 DB *adb, *bdb, *tdb, *hdb;
00577 int accounts, branches, tellers, verbose;
00578 {
00579 DBC *acurs, *bcurs, *tcurs;
00580 DBT d_dbt, d_histdbt, k_dbt, k_histdbt;
00581 DB_TXN *t;
00582 db_recno_t key;
00583 defrec rec;
00584 histrec hrec;
00585 int account, branch, teller, ret;
00586
00587 t = NULL;
00588 acurs = bcurs = tcurs = NULL;
00589
00590
00591
00592
00593
00594
00595 account = random_id(ACCOUNT, accounts, branches, tellers);
00596 branch = random_id(BRANCH, accounts, branches, tellers);
00597 teller = random_id(TELLER, accounts, branches, tellers);
00598
00599 memset(&d_histdbt, 0, sizeof(d_histdbt));
00600
00601 memset(&k_histdbt, 0, sizeof(k_histdbt));
00602 k_histdbt.data = &key;
00603 k_histdbt.size = sizeof(key);
00604
00605 memset(&k_dbt, 0, sizeof(k_dbt));
00606 k_dbt.size = sizeof(int);
00607
00608 memset(&d_dbt, 0, sizeof(d_dbt));
00609 d_dbt.flags = DB_DBT_USERMEM;
00610 d_dbt.data = &rec;
00611 d_dbt.ulen = sizeof(rec);
00612
00613 hrec.aid = account;
00614 hrec.bid = branch;
00615 hrec.tid = teller;
00616 hrec.amount = 10;
00617
00618 d_histdbt.flags = DB_DBT_PARTIAL;
00619
00620
00621
00622
00623
00624
00625
00626
00627
00628 if (dbenv->txn_begin(dbenv, NULL, &t, 0) != 0)
00629 goto err;
00630
00631 if (adb->cursor(adb, t, &acurs, 0) != 0 ||
00632 bdb->cursor(bdb, t, &bcurs, 0) != 0 ||
00633 tdb->cursor(tdb, t, &tcurs, 0) != 0)
00634 goto err;
00635
00636
00637 k_dbt.data = &account;
00638 if (acurs->c_get(acurs, &k_dbt, &d_dbt, DB_SET) != 0)
00639 goto err;
00640 rec.balance += 10;
00641 if (acurs->c_put(acurs, &k_dbt, &d_dbt, DB_CURRENT) != 0)
00642 goto err;
00643
00644
00645 k_dbt.data = &branch;
00646 if (bcurs->c_get(bcurs, &k_dbt, &d_dbt, DB_SET) != 0)
00647 goto err;
00648 rec.balance += 10;
00649 if (bcurs->c_put(bcurs, &k_dbt, &d_dbt, DB_CURRENT) != 0)
00650 goto err;
00651
00652
00653 k_dbt.data = &teller;
00654 if (tcurs->c_get(tcurs, &k_dbt, &d_dbt, DB_SET) != 0)
00655 goto err;
00656 rec.balance += 10;
00657 if (tcurs->c_put(tcurs, &k_dbt, &d_dbt, DB_CURRENT) != 0)
00658 goto err;
00659
00660
00661 d_histdbt.flags = 0;
00662 d_histdbt.data = &hrec;
00663 d_histdbt.ulen = sizeof(hrec);
00664 if (hdb->put(hdb, t, &k_histdbt, &d_histdbt, DB_APPEND) != 0)
00665 goto err;
00666
00667 if (acurs->c_close(acurs) != 0 || bcurs->c_close(bcurs) != 0 ||
00668 tcurs->c_close(tcurs) != 0)
00669 goto err;
00670
00671 ret = t->commit(t, 0);
00672 t = NULL;
00673 if (ret != 0)
00674 goto err;
00675
00676
00677 return (0);
00678
00679 err: if (acurs != NULL)
00680 (void)acurs->c_close(acurs);
00681 if (bcurs != NULL)
00682 (void)bcurs->c_close(bcurs);
00683 if (tcurs != NULL)
00684 (void)tcurs->c_close(tcurs);
00685 if (t != NULL)
00686 (void)t->abort(t);
00687
00688 if (verbose)
00689 printf("Transaction A=%ld B=%ld T=%ld failed\n",
00690 (long)account, (long)branch, (long)teller);
00691 return (-1);
00692 }