Remove definition of builtin function
[platform/upstream/db4.git] / examples_stl / StlTpcbExample.cpp
1 /*-
2  * See the file LICENSE for redistribution information.
3  *
4  * Copyright (c) 1997-2009 Oracle.  All rights reserved.
5  *
6  * $Id$
7  */
8
9 #include <sys/types.h>
10
11 #include <errno.h>
12 #include <stdlib.h>
13 #include <string.h>
14 #include <time.h>
15
16 #include <iostream>
17
18 #include "dbstl_vector.h"
19 #include "dbstl_map.h"
20
21 using std::cout;
22 using std::cerr;
23
24 typedef enum { ACCOUNT, BRANCH, TELLER } FTYPE;
25
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);
30
31 int verbose;
32 const char *progname = "StlTpcbExample";                    // Program name.
33
34 // Forward declared data classes
35 class Defrec;
36 class Histrec;
37
38 typedef dbstl::db_map<u_int32_t, Defrec > DefrecMap;
39 typedef dbstl::db_vector<Histrec > HistrecVector;
40
41 class StlTpcbExample : public DbEnv
42 {
43 public:
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);
48         void populateHistory(
49             HistrecVector *, int, u_int32_t, u_int32_t, u_int32_t);
50         void populateTable(
51             DefrecMap *, u_int32_t, u_int32_t, int, const char *);
52
53         // Note: the constructor creates a DbEnv(), which is
54         // not fully initialized until the DbEnv::open() method
55         // is called.
56         //
57         StlTpcbExample(const char *home, int cachesize, int flags);
58
59 private:
60         static const char FileName[];
61
62         // no need for copy and assignment
63         StlTpcbExample(const StlTpcbExample &);
64         void operator = (const StlTpcbExample &);
65 };
66
67 //
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).
75 //
76 #define TELLERS_PER_BRANCH      100
77 #define ACCOUNTS_PER_TELLER     1000
78 #define HISTORY_PER_BRANCH      2592000
79
80 /*
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.
85  */
86 #ifdef  VALID_SCALING
87 #define ACCOUNTS         1000000
88 #define BRANCHES              10
89 #define TELLERS              100
90 #define HISTORY         25920000
91 #endif
92
93 #ifdef  TINY
94 #define ACCOUNTS            1000
95 #define BRANCHES              10
96 #define TELLERS              100
97 #define HISTORY            10000
98 #endif
99
100 #if !defined(VALID_SCALING) && !defined(TINY)
101 #define ACCOUNTS          100000
102 #define BRANCHES              10
103 #define TELLERS              100
104 #define HISTORY           259200
105 #endif
106
107 #define HISTORY_LEN         100
108 #define RECLEN              100
109 #define BEGID           1000000
110
111 class Defrec {
112 public:
113         u_int32_t   id;
114         u_int32_t   balance;
115         u_int8_t    pad[RECLEN - sizeof(u_int32_t) - sizeof(u_int32_t)];
116 };
117
118 class Histrec {
119 public:
120         u_int32_t   aid;
121         u_int32_t   bid;
122         u_int32_t   tid;
123         u_int32_t   amount;
124         u_int8_t    pad[RECLEN - 4 * sizeof(u_int32_t)];
125 };
126
127 int
128 main(int argc, char *argv[])
129 {
130         unsigned long seed;
131         int accounts, branches, tellers, history;
132         int iflag, mpool, ntxns, txn_no_sync;
133         const char *home;
134         char *endarg;
135
136         home = "TESTDIR";
137         accounts = branches = history = tellers = 0;
138         txn_no_sync = 0;
139         mpool = ntxns = 0;
140         verbose = 0;
141         iflag = 0;
142         seed = (unsigned long)time(NULL);
143
144         for (int i = 1; i < argc; ++i) {
145
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]));
150                 }
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]));
155                 }
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]));
160                 }
161                 else if (strcmp(argv[i], "-f") == 0) {
162                         // Fast mode: no txn sync.
163                         txn_no_sync = 1;
164                 }
165                 else if (strcmp(argv[i], "-h") == 0) {
166                         // DB  home.
167                         home = argv[++i];
168                 }
169                 else if (strcmp(argv[i], "-i") == 0) {
170                         // Initialize the test.
171                         iflag = 1;
172                 }
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]));
177                 }
178                 else if (strcmp(argv[i], "-S") == 0) {
179                         // Random number seed.
180                         seed = strtoul(argv[++i], &endarg, 0);
181                         if (*endarg != '\0')
182                                 return (invarg('S', argv[i]));
183                 }
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]));
188                 }
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]));
193                 }
194                 else if (strcmp(argv[i], "-v") == 0) {
195                         // Verbose option.
196                         verbose = 1;
197                 }
198                 else {
199                         return (usage());
200                 }
201         }
202
203         srand((unsigned int)seed);
204
205         accounts = accounts == 0 ? ACCOUNTS : accounts;
206         branches = branches == 0 ? BRANCHES : branches;
207         tellers = tellers == 0 ? TELLERS : tellers;
208         history = history == 0 ? HISTORY : history;
209
210         if (verbose)
211                 cout << (long)accounts << " Accounts, "
212                      << (long)branches << " Branches, "
213                      << (long)tellers << " Tellers, "
214                      << (long)history << " History\n";
215
216         try {
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.
220                 //
221                 StlTpcbExample app(
222                     home, mpool, txn_no_sync ? DB_TXN_NOSYNC : 0);
223
224                 if (iflag) {
225                         if (ntxns != 0)
226                                 return (usage());
227                         app.populate(accounts, branches, history, tellers);
228                 }
229                 else {
230                         if (ntxns == 0)
231                                 return (usage());
232                         app.run(ntxns, accounts, branches, tellers);
233                 }
234
235                 dbstl::dbstl_exit();
236                 return (EXIT_SUCCESS);
237         }
238         catch (DbException &dbe) {
239                 cerr << "StlTpcbExample: " << dbe.what() << "\n";
240                 return (EXIT_FAILURE);
241         }
242 }
243
244 static int
245 invarg(int arg, char *str)
246 {
247         cerr << "StlTpcbExample: invalid argument for -"
248              << (char)arg << ": " << str << "\n";
249         return (EXIT_FAILURE);
250 }
251
252 static int
253 usage()
254 {
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);
259 }
260
261 StlTpcbExample::StlTpcbExample(const char *home, int cachesize, int flags)
262 :       DbEnv(DB_CXX_NO_EXCEPTIONS)
263 {
264         u_int32_t local_flags;
265
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);
271
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);
278
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);
283 }
284
285 //
286 // Initialize the database to the specified number of accounts, branches,
287 // history records, and tellers.
288 //
289 void
290 StlTpcbExample::populate(int accounts, int branches, int history, int tellers)
291 {
292         Db *dbp;
293         DefrecMap *accounts_map, *branches_map, *tellers_map;
294         HistrecVector *history_vector;
295
296         int err, oflags;
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;
300
301         idnum = BEGID;
302         balance = 500000;
303         oflags = DB_CREATE;
304
305         dbp = new Db(this, DB_CXX_NO_EXCEPTIONS);
306         dbp->set_h_nelem((unsigned int)accounts);
307
308         if ((err = dbp->open(NULL, "account", NULL,
309             DB_HASH, oflags, 0644)) != 0) {
310                 DbException except("Account file create failed", err);
311                 throw except;
312         }
313
314         dbstl::register_db(dbp);
315         accounts_map = new DefrecMap(dbp, this);
316         start_anum = idnum;
317         populateTable(accounts_map, idnum, balance, accounts, "account");
318         idnum += accounts;
319         end_anum = idnum - 1;
320         // Automatically closes the underlying database.
321         delete accounts_map;
322         dbstl::close_db(dbp);
323         delete dbp;
324         if (verbose)
325                 cout << "Populated accounts: "
326                      << (long)start_anum << " - " << (long)end_anum << "\n";
327
328         dbp = new Db(this, DB_CXX_NO_EXCEPTIONS);
329         //
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.
333         //
334         dbp->set_h_ffactor(1);
335         dbp->set_h_nelem((unsigned int)branches);
336         dbp->set_pagesize(512);
337
338         if ((err = dbp->open(NULL, 
339             "branch", NULL, DB_HASH, oflags, 0644)) != 0) {
340                 DbException except("Branch file create failed", err);
341                 throw except;
342         }
343         dbstl::register_db(dbp);
344         branches_map = new DefrecMap(dbp, this);
345         start_bnum = idnum;
346         populateTable(branches_map, idnum, balance, branches, "branch");
347         idnum += branches;
348         end_bnum = idnum - 1;
349         delete branches_map;
350         dbstl::close_db(dbp);
351         delete dbp;
352
353         if (verbose)
354                 cout << "Populated branches: "
355                      << (long)start_bnum << " - " << (long)end_bnum << "\n";
356
357         dbp = new Db(this, DB_CXX_NO_EXCEPTIONS);
358         //
359         // In the case of tellers, we also want small pages, but we'll let
360         // the fill factor dynamically adjust itself.
361         //
362         dbp->set_h_ffactor(0);
363         dbp->set_h_nelem((unsigned int)tellers);
364         dbp->set_pagesize(512);
365
366         if ((err = dbp->open(NULL,
367             "teller", NULL, DB_HASH, oflags, 0644)) != 0) {
368                 DbException except("Teller file create failed", err);
369                 throw except;
370         }
371
372         dbstl::register_db(dbp);
373         tellers_map = new DefrecMap(dbp, this);
374         start_tnum = idnum;
375         populateTable(tellers_map, idnum, balance, tellers, "teller");
376         idnum += tellers;
377         end_tnum = idnum - 1;
378         delete tellers_map;
379         dbstl::close_db(dbp);
380         delete dbp;
381         if (verbose)
382                 cout << "Populated tellers: "
383                      << (long)start_tnum << " - " << (long)end_tnum << "\n";
384
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);
390                 throw except;
391         }
392
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);
398         delete dbp;
399 }
400
401 void
402 StlTpcbExample::populateTable(DefrecMap *drm, u_int32_t start_id,
403                               u_int32_t balance, int nrecs, const char *msg)
404 {
405         Defrec drec;
406         int i;
407         dbstl::pair<dbstl::db_map<u_int32_t, Defrec >::iterator, bool > ib;
408
409         memset(&drec.pad[0], 1, sizeof(drec.pad));
410         try {
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";
417                 }
418         } catch (...) {
419                 throw;
420         }
421 }
422
423 void
424 StlTpcbExample::populateHistory(HistrecVector *hrm, int nrecs,
425                                 u_int32_t accounts, u_int32_t branches, 
426                                 u_int32_t tellers)
427 {
428         Histrec hrec;
429         int i;
430
431         memset(&hrec.pad[0], 1, sizeof(hrec.pad));
432         hrec.amount = 10;
433         try {
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);
442                 }
443         } catch (...) {
444                 throw;
445         }
446 }
447
448 u_int32_t
449 random_int(u_int32_t lo, u_int32_t hi)
450 {
451         u_int32_t ret;
452         int t;
453
454         t = rand();
455         ret = (u_int32_t)(((double)t / ((double)(RAND_MAX) + 1)) *
456             (hi - lo + 1));
457         ret += lo;
458         return (ret);
459 }
460
461 u_int32_t
462 random_id(FTYPE type, u_int32_t accounts, u_int32_t branches, u_int32_t tellers)
463 {
464         u_int32_t min, max, num;
465
466         max = min = BEGID;
467         num = accounts;
468         switch (type) {
469         case TELLER:
470                 min += branches;
471                 num = tellers;
472                 // Fallthrough
473         case BRANCH:
474                 if (type == BRANCH)
475                         num = branches;
476                 min += accounts;
477                 // Fallthrough
478         case ACCOUNT:
479                 max = min + num - 1;
480         }
481         return (random_int(min, max));
482 }
483
484 void
485 StlTpcbExample::run(int n, int accounts, int branches, int tellers)
486 {
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;
492
493         //
494         // Open the database files.
495         //
496         oflags = DB_AUTO_COMMIT;
497
498         int err;
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);
503                 throw except;
504         }
505         dbstl::register_db(adb);
506         accounts_map = new DefrecMap(adb);
507
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);
512                 throw except;
513         }
514         dbstl::register_db(bdb);
515         branches_map = new DefrecMap(bdb);
516
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);
521                 throw except;
522         }
523         dbstl::register_db(tdb);
524         tellers_map = new DefrecMap(tdb);
525
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);
530                 throw except;
531         }
532         dbstl::register_db(hdb);
533         history_vector = new HistrecVector(hdb);
534
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)
539                         ++failed;
540         (void)time(&end_time);
541         if (end_time == start_time)
542                 ++end_time;
543         // We use printf because it provides much simpler
544         // formatting than iostreams.
545         //
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));
549
550         delete accounts_map;
551         delete branches_map;
552         delete tellers_map;
553         delete history_vector;
554         dbstl::close_all_dbs();
555 }
556
557 //
558 // XXX Figure out the appropriate way to pick out IDs.
559 //
560 int
561 StlTpcbExample::txn(DefrecMap *accounts_map, DefrecMap *branches_map, 
562                     DefrecMap *tellers_map, HistrecVector *history_vector,
563                     int accounts, int branches, int tellers)
564 {
565         Histrec hrec;
566         DefrecMap::value_type_wrap::second_type recref, recref2, recref3;
567         int account, branch, teller;
568
569         /*
570          * !!!
571          * This is sample code -- we could move a lot of this into the driver
572          * to make it faster.
573          */
574         account = random_id(ACCOUNT, accounts, branches, tellers);
575         branch = random_id(BRANCH, accounts, branches, tellers);
576         teller = random_id(TELLER, accounts, branches, tellers);
577
578         hrec.aid = account;
579         hrec.bid = branch;
580         hrec.tid = teller;
581         hrec.amount = 10;
582         
583         /*
584          * START PER-TRANSACTION TIMING.
585          *
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.
590          */
591         try {
592                 dbstl::begin_txn(0, this);
593
594                 /* Account record */
595                 recref = (*accounts_map)[account];
596                 recref.balance += 10;
597                 recref._DB_STL_StoreElement();
598
599                 /* Branch record */
600                 recref2 = (*branches_map)[branch];
601                 recref2.balance += 10;
602                 recref2._DB_STL_StoreElement();
603
604                 /* Teller record */
605                 recref3 = (*tellers_map)[teller];
606                 recref3.balance += 10;
607                 recref3._DB_STL_StoreElement();
608
609                 /* History record */
610                 history_vector->push_back(hrec);
611                 dbstl::commit_txn(this);
612                 /* END PER-TRANSACTION TIMING. */
613                 return (0);
614
615         } catch (DbDeadlockException) {
616                 dbstl::abort_txn(this);
617                 if (verbose)
618                         cout << "Transaction A=" << (long)account
619                              << " B=" << (long)branch
620                              << " T=" << (long)teller << " failed\n";
621                 return (DB_LOCK_DEADLOCK);
622         } catch(...) {
623                 dbstl::abort_txn(this);
624                 throw;
625         }
626 }