diff options
author | jbj <devnull@localhost> | 2001-03-21 18:33:35 +0000 |
---|---|---|
committer | jbj <devnull@localhost> | 2001-03-21 18:33:35 +0000 |
commit | 731946f4b90eb1173452dd30f1296dd825155d82 (patch) | |
tree | 67535f54ecb7e5463c06e62044e4efd84ae0291d /db/examples_c | |
parent | 7ed904da030dc4640ff9bce8458ba07cc09d830d (diff) | |
download | librpm-tizen-731946f4b90eb1173452dd30f1296dd825155d82.tar.gz librpm-tizen-731946f4b90eb1173452dd30f1296dd825155d82.tar.bz2 librpm-tizen-731946f4b90eb1173452dd30f1296dd825155d82.zip |
Initial revision
CVS patchset: 4644
CVS date: 2001/03/21 18:33:35
Diffstat (limited to 'db/examples_c')
-rw-r--r-- | db/examples_c/README | 23 | ||||
-rw-r--r-- | db/examples_c/ex_access.c | 171 | ||||
-rw-r--r-- | db/examples_c/ex_btrec.c | 241 | ||||
-rw-r--r-- | db/examples_c/ex_dbclient.c | 248 | ||||
-rw-r--r-- | db/examples_c/ex_env.c | 170 | ||||
-rw-r--r-- | db/examples_c/ex_lock.c | 235 | ||||
-rw-r--r-- | db/examples_c/ex_mpool.c | 280 | ||||
-rw-r--r-- | db/examples_c/ex_thread.c | 604 | ||||
-rw-r--r-- | db/examples_c/ex_tpcb.c | 811 | ||||
-rw-r--r-- | db/examples_c/ex_tpcb.h | 39 |
10 files changed, 2822 insertions, 0 deletions
diff --git a/db/examples_c/README b/db/examples_c/README new file mode 100644 index 000000000..f59ae00a6 --- /dev/null +++ b/db/examples_c/README @@ -0,0 +1,23 @@ +# $Id: README,v 11.3 2000/12/13 06:32:29 krinsky Exp $ + +ex_access.c Using just the DB access methods. + +ex_btrec.c Using the BTREE access method with record numbers. + +ex_env.c Setting up the DB environment. + +ex_lock.c Locking. + +ex_mpool.c Shared memory buffer pools. + +ex_tpcb.c TPC/B. + Ex_tpcb sets up a framework in which to run a TPC/B test. + Database initialization (the -i flag) and running the + benchmark (-n flag) must take place separately (i.e., + first create the database, then run 1 or more copies of + the benchmark). Furthermore, when running more than one + TPCB process, it is necessary to run the deadlock detector + (db_deadlock), since it is possible for concurrent tpcb + processes to deadlock. For performance measurement, it + will also be beneficial to run the db_checkpoint process + as well. diff --git a/db/examples_c/ex_access.c b/db/examples_c/ex_access.c new file mode 100644 index 000000000..3448daf43 --- /dev/null +++ b/db/examples_c/ex_access.c @@ -0,0 +1,171 @@ +/*- + * See the file LICENSE for redistribution information. + * + * Copyright (c) 1997, 1998, 1999, 2000 + * Sleepycat Software. All rights reserved. + * + * $Id: ex_access.c,v 11.7 2000/05/22 15:17:03 sue Exp $ + */ + +#include "db_config.h" + +#ifndef NO_SYSTEM_INCLUDES +#include <sys/types.h> + +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#endif + +#include <db.h> + +#ifdef HAVE_VXWORKS +#include "stdio.h" +#define DATABASE "/vxtmp/vxtmp/access.db" +#define ERROR_RETURN ERROR +#else +#define DATABASE "access.db" +#define ERROR_RETURN 1 +int main __P((int, char *[])); +void usage __P((char *)); +#endif + +int ex_access __P((void)); + +#ifndef HAVE_VXWORKS +int +main(argc, argv) + int argc; + char *argv[]; +{ + extern char *optarg; + extern int optind; + int ch; + + while ((ch = getopt(argc, argv, "")) != EOF) + switch (ch) { + case '?': + default: + usage(argv[0]); + } + argc -= optind; + argv += optind; + + return (ex_access()); +} + +void +usage(progname) + char *progname; +{ + (void)fprintf(stderr, "usage: %s\n", progname); + exit(1); +} +#endif + +int +ex_access() +{ + DB *dbp; + DBC *dbcp; + DBT key, data; + u_int32_t len; + int ret; + char *p, *t, buf[1024], rbuf[1024]; + const char *progname = "ex_access"; /* Program name. */ + + /* Remove the previous database. */ + (void)unlink(DATABASE); + + /* Create and initialize database object, open the database. */ + if ((ret = db_create(&dbp, NULL, 0)) != 0) { + fprintf(stderr, + "%s: db_create: %s\n", progname, db_strerror(ret)); + return (ERROR_RETURN); + } + dbp->set_errfile(dbp, stderr); + dbp->set_errpfx(dbp, progname); + if ((ret = dbp->set_pagesize(dbp, 1024)) != 0) { + dbp->err(dbp, ret, "set_pagesize"); + goto err1; + } + if ((ret = dbp->set_cachesize(dbp, 0, 32 * 1024, 0)) != 0) { + dbp->err(dbp, ret, "set_cachesize"); + goto err1; + } + if ((ret = + dbp->open(dbp, DATABASE, NULL, DB_BTREE, DB_CREATE, 0664)) != 0) { + dbp->err(dbp, ret, "%s: open", DATABASE); + goto err1; + } + + /* + * Insert records into the database, where the key is the user + * input and the data is the user input in reverse order. + */ + memset(&key, 0, sizeof(DBT)); + memset(&data, 0, sizeof(DBT)); + for (;;) { + printf("input> "); + fflush(stdout); + if (fgets(buf, sizeof(buf), stdin) == NULL) + break; + if ((len = strlen(buf)) <= 1) + continue; + for (t = rbuf, p = buf + (len - 2); p >= buf;) + *t++ = *p--; + *t++ = '\0'; + + key.data = buf; + data.data = rbuf; + data.size = key.size = len - 1; + + switch (ret = + dbp->put(dbp, NULL, &key, &data, DB_NOOVERWRITE)) { + case 0: + break; + default: + dbp->err(dbp, ret, "DB->put"); + if (ret != DB_KEYEXIST) + goto err1; + break; + } + } + printf("\n"); + + /* Acquire a cursor for the database. */ + if ((ret = dbp->cursor(dbp, NULL, &dbcp, 0)) != 0) { + dbp->err(dbp, ret, "DB->cursor"); + goto err1; + } + + /* Initialize the key/data pair so the flags aren't set. */ + memset(&key, 0, sizeof(key)); + memset(&data, 0, sizeof(data)); + + /* Walk through the database and print out the key/data pairs. */ + while ((ret = dbcp->c_get(dbcp, &key, &data, DB_NEXT)) == 0) + printf("%.*s : %.*s\n", + (int)key.size, (char *)key.data, + (int)data.size, (char *)data.data); + if (ret != DB_NOTFOUND) { + dbp->err(dbp, ret, "DBcursor->get"); + goto err2; + } + + /* Close everything down. */ + if ((ret = dbcp->c_close(dbcp)) != 0) { + dbp->err(dbp, ret, "DBcursor->close"); + goto err1; + } + if ((ret = dbp->close(dbp, 0)) != 0) { + fprintf(stderr, + "%s: DB->close: %s\n", progname, db_strerror(ret)); + return (ERROR_RETURN); + } + return (0); + +err2: (void)dbcp->c_close(dbcp); +err1: (void)dbp->close(dbp, 0); + return (ERROR_RETURN); +} diff --git a/db/examples_c/ex_btrec.c b/db/examples_c/ex_btrec.c new file mode 100644 index 000000000..b74f16b83 --- /dev/null +++ b/db/examples_c/ex_btrec.c @@ -0,0 +1,241 @@ +/*- + * See the file LICENSE for redistribution information. + * + * Copyright (c) 1997, 1998, 1999, 2000 + * Sleepycat Software. All rights reserved. + * + * $Id: ex_btrec.c,v 11.8 2000/05/22 15:17:03 sue Exp $ + */ + +#include "db_config.h" + +#ifndef NO_SYSTEM_INCLUDES +#include <sys/types.h> + +#include <errno.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#endif + +#include <db.h> + +#ifdef HAVE_VXWORKS +#define DATABASE "/vxtmp/vxtmp/access.db" +#define WORDLIST "/vxtmp/vxtmp/wordlist" +#define ERROR_RETURN ERROR +#else +#define DATABASE "access.db" +#define WORDLIST "../test/wordlist" +#define ERROR_RETURN 1 +int main __P((int, char *[])); +void usage __P((char *)); +#endif + +int ex_btrec __P((void)); +void show __P((char *, DBT *, DBT *)); + +#ifndef HAVE_VXWORKS +int +main(argc, argv) + int argc; + char *argv[]; +{ + extern char *optarg; + extern int optind; + int ch; + + while ((ch = getopt(argc, argv, "")) != EOF) + switch (ch) { + case '?': + default: + usage(argv[0]); + } + argc -= optind; + argv += optind; + + return (ex_btrec()); +} + +void +usage(progname) + char *progname; +{ + (void)fprintf(stderr, "usage: %s\n", progname); + exit(1); +} +#endif + +int +ex_btrec() +{ + DB *dbp; + DBC *dbcp; + DBT key, data; + DB_BTREE_STAT *statp; + FILE *fp; + db_recno_t recno; + u_int32_t len; + int cnt, ret; + char *p, *t, buf[1024], rbuf[1024]; + const char *progname = "ex_btrec"; /* Program name. */ + + /* Open the word database. */ + if ((fp = fopen(WORDLIST, "r")) == NULL) { + fprintf(stderr, "%s: open %s: %s\n", + progname, WORDLIST, db_strerror(errno)); + return (ERROR_RETURN); + } + + /* Remove the previous database. */ + (void)unlink(DATABASE); + + /* Create and initialize database object, open the database. */ + if ((ret = db_create(&dbp, NULL, 0)) != 0) { + fprintf(stderr, + "%s: db_create: %s\n", progname, db_strerror(ret)); + return (ERROR_RETURN); + } + dbp->set_errfile(dbp, stderr); + dbp->set_errpfx(dbp, progname); /* 1K page sizes. */ + if ((ret = dbp->set_pagesize(dbp, 1024)) != 0) { + dbp->err(dbp, ret, "set_pagesize"); + return (ERROR_RETURN); + } /* Record numbers. */ + if ((ret = dbp->set_flags(dbp, DB_RECNUM)) != 0) { + dbp->err(dbp, ret, "set_flags: DB_RECNUM"); + return (ERROR_RETURN); + } + if ((ret = + dbp->open(dbp, DATABASE, NULL, DB_BTREE, DB_CREATE, 0664)) != 0) { + dbp->err(dbp, ret, "open: %s", DATABASE); + return (ERROR_RETURN); + } + + /* + * Insert records into the database, where the key is the word + * preceded by its record number, and the data is the same, but + * in reverse order. + */ + memset(&key, 0, sizeof(DBT)); + memset(&data, 0, sizeof(DBT)); + for (cnt = 1; cnt <= 1000; ++cnt) { + (void)sprintf(buf, "%04d_", cnt); + if (fgets(buf + 4, sizeof(buf) - 4, fp) == NULL) + break; + len = strlen(buf); + for (t = rbuf, p = buf + (len - 2); p >= buf;) + *t++ = *p--; + *t++ = '\0'; + + key.data = buf; + data.data = rbuf; + data.size = key.size = len - 1; + + if ((ret = + dbp->put(dbp, NULL, &key, &data, DB_NOOVERWRITE)) != 0) { + dbp->err(dbp, ret, "DB->put"); + if (ret != DB_KEYEXIST) + goto err1; + } + } + + /* Close the word database. */ + (void)fclose(fp); + + /* Print out the number of records in the database. */ + if ((ret = dbp->stat(dbp, &statp, NULL, 0)) != 0) { + dbp->err(dbp, ret, "DB->stat"); + goto err1; + } + printf("%s: database contains %lu records\n", + progname, (u_long)statp->bt_ndata); + free(statp); + + /* Acquire a cursor for the database. */ + if ((ret = dbp->cursor(dbp, NULL, &dbcp, 0)) != 0) { + dbp->err(dbp, ret, "DB->cursor"); + goto err1; + } + + /* + * Prompt the user for a record number, then retrieve and display + * that record. + */ + for (;;) { + /* Get a record number. */ + printf("recno #> "); + fflush(stdout); + if (fgets(buf, sizeof(buf), stdin) == NULL) + break; + recno = atoi(buf); + + /* + * Reset the key each time, the dbp->c_get() routine returns + * the key and data pair, not just the key! + */ + key.data = &recno; + key.size = sizeof(recno); + if ((ret = dbcp->c_get(dbcp, &key, &data, DB_SET_RECNO)) != 0) + goto get_err; + + /* Display the key and data. */ + show("k/d\t", &key, &data); + + /* Move the cursor a record forward. */ + if ((ret = dbcp->c_get(dbcp, &key, &data, DB_NEXT)) != 0) + goto get_err; + + /* Display the key and data. */ + show("next\t", &key, &data); + + /* + * Retrieve the record number for the following record into + * local memory. + */ + data.data = &recno; + data.size = sizeof(recno); + data.ulen = sizeof(recno); + data.flags |= DB_DBT_USERMEM; + if ((ret = dbcp->c_get(dbcp, &key, &data, DB_GET_RECNO)) != 0) { +get_err: dbp->err(dbp, ret, "DBcursor->get"); + if (ret != DB_NOTFOUND && ret != DB_KEYEMPTY) + goto err2; + } else + printf("retrieved recno: %lu\n", (u_long)recno); + + /* Reset the data DBT. */ + memset(&data, 0, sizeof(data)); + } + + if ((ret = dbcp->c_close(dbcp)) != 0) { + dbp->err(dbp, ret, "DBcursor->close"); + goto err1; + } + if ((ret = dbp->close(dbp, 0)) != 0) { + fprintf(stderr, + "%s: DB->close: %s\n", progname, db_strerror(ret)); + return (ERROR_RETURN); + } + + return (0); + +err2: (void)dbcp->c_close(dbcp); +err1: (void)dbp->close(dbp, 0); + return (ret); + +} + +/* + * show -- + * Display a key/data pair. + */ +void +show(msg, key, data) + DBT *key, *data; + char *msg; +{ + printf("%s%.*s : %.*s\n", msg, + (int)key->size, (char *)key->data, + (int)data->size, (char *)data->data); +} diff --git a/db/examples_c/ex_dbclient.c b/db/examples_c/ex_dbclient.c new file mode 100644 index 000000000..27461a892 --- /dev/null +++ b/db/examples_c/ex_dbclient.c @@ -0,0 +1,248 @@ +/*- + * See the file LICENSE for redistribution information. + * + * Copyright (c) 1996, 1997, 1998, 1999, 2000 + * Sleepycat Software. All rights reserved. + * + * $Id: ex_dbclient.c,v 1.12 2000/10/26 14:13:05 bostic Exp $ + */ + +#include "db_config.h" + +#ifndef NO_SYSTEM_INCLUDES +#include <sys/types.h> + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#endif + +#include <db.h> + +#define DATABASE_HOME "database" + +#define DATABASE "access.db" + +int db_clientrun __P((DB_ENV *, char *)); +int ex_dbclient_run __P((char *, FILE *, char *, char *)); +#ifdef HAVE_VXWORKS +int ex_dbclient __P((char *)); +#define ERROR_RETURN ERROR +#define VXSHM_KEY 10 +#else +int main __P((int, char *[])); +#define ERROR_RETURN 1 +#endif + +/* + * An example of a program creating/configuring a Berkeley DB environment. + */ +#ifndef HAVE_VXWORKS +int +main(argc, argv) + int argc; + char *argv[]; +{ + char *home; + int ret; + + if (argc != 2) { + fprintf(stderr, "Usage: %s hostname\n",argv[0]); + exit(1); + } + /* + * All of the shared database files live in DATABASE_HOME, but + * data files will live in CONFIG_DATA_DIR. + */ + home = DATABASE_HOME; + + if ((ret = ex_dbclient_run(home, stderr, argv[1], argv[0])) != 0) + return (ret); + + return (0); +} +#endif + +int +ex_dbclient(host) + char *host; +{ + char *home; + char *progname = "ex_dbclient"; /* Program name. */ + int ret; + + /* + * All of the shared database files live in DATABASE_HOME, but + * data files will live in CONFIG_DATA_DIR. + */ + home = DATABASE_HOME; + + if ((ret = ex_dbclient_run(home, stderr, host, progname)) != 0) + return (ret); + + return (0); +} + +int +ex_dbclient_run(home, errfp, host, progname) + char *home, *host, *progname; + FILE *errfp; +{ + DB_ENV *dbenv; + int ret, retry; + + /* + * Create an environment object and initialize it for error + * reporting. + */ + if ((ret = db_env_create(&dbenv, DB_CLIENT)) != 0) { + fprintf(errfp, "%s: %s\n", progname, db_strerror(ret)); + return (ERROR_RETURN); + } +#ifdef HAVE_VXWORKS + if ((ret = dbenv->set_shm_key(dbenv, VXSHM_KEY)) != 0) { + fprintf(errfp, "%s: %s\n", progname, db_strerror(ret)); + return (ERROR_RETURN); + } +#endif + retry = 0; +retry: + while (retry < 5) { + /* + * Set the server host we are talking to. + */ + if ((ret = + dbenv->set_server(dbenv, host, 10000, 10000, 0)) != 0) { + fprintf(stderr, "Try %d: DBENV->set_server: %s\n", + retry, db_strerror(ret)); + retry++; + if ((ret = __os_sleep(dbenv, 15, 0)) != 0) + return (ret); + } else + break; + } + + if (retry >= 5) { + fprintf(stderr, "DBENV->set_server: %s\n", db_strerror(ret)); + dbenv->close(dbenv, 0); + return (ERROR_RETURN); + } + /* + * We want to specify the shared memory buffer pool cachesize, + * but everything else is the default. + */ + if ((ret = dbenv->set_cachesize(dbenv, 0, 64 * 1024, 0)) != 0) { + dbenv->err(dbenv, ret, "set_cachesize"); + dbenv->close(dbenv, 0); + return (ERROR_RETURN); + } + /* + * We have multiple processes reading/writing these files, so + * we need concurrency control and a shared buffer pool, but + * not logging or transactions. + */ + if ((ret = dbenv->open(dbenv, home, + DB_CREATE | DB_INIT_LOCK | DB_INIT_MPOOL | DB_INIT_TXN, 0)) != 0) { + dbenv->err(dbenv, ret, "environment open: %s", home); + dbenv->close(dbenv, 0); + if (ret == DB_NOSERVER) + goto retry; + return (ERROR_RETURN); + } + + ret = db_clientrun(dbenv, progname); + printf("db_clientrun returned %d\n", ret); + if (ret == DB_NOSERVER) + goto retry; + + /* Close the handle. */ + if ((ret = dbenv->close(dbenv, 0)) != 0) { + fprintf(stderr, "DBENV->close: %s\n", db_strerror(ret)); + return (ERROR_RETURN); + } + return (0); +} + +int +db_clientrun(dbenv, progname) + DB_ENV *dbenv; + char *progname; +{ + DB *dbp; + DBT key, data; + u_int32_t len; + int ret; + char *p, *t, buf[1024], rbuf[1024]; + + /* Remove the previous database. */ + + /* Create and initialize database object, open the database. */ + if ((ret = db_create(&dbp, dbenv, 0)) != 0) { + fprintf(stderr, + "%s: db_create: %s\n", progname, db_strerror(ret)); + return (ret); + } + if ((ret = dbp->set_pagesize(dbp, 1024)) != 0) { + dbp->err(dbp, ret, "set_pagesize"); + goto err1; + } + if ((ret = + dbp->open(dbp, DATABASE, NULL, DB_BTREE, DB_CREATE, 0664)) != 0) { + dbp->err(dbp, ret, "%s: open", DATABASE); + goto err1; + } + + /* + * Insert records into the database, where the key is the user + * input and the data is the user input in reverse order. + */ + memset(&key, 0, sizeof(DBT)); + memset(&data, 0, sizeof(DBT)); + for (;;) { + printf("input> "); + fflush(stdout); + if (fgets(buf, sizeof(buf), stdin) == NULL) + break; + if ((len = strlen(buf)) <= 1) + continue; + for (t = rbuf, p = buf + (len - 2); p >= buf;) + *t++ = *p--; + *t++ = '\0'; + + key.data = buf; + data.data = rbuf; + data.size = key.size = len - 1; + + switch (ret = + dbp->put(dbp, NULL, &key, &data, DB_NOOVERWRITE)) { + case 0: + break; + default: + dbp->err(dbp, ret, "DB->put"); + if (ret != DB_KEYEXIST) + goto err1; + break; + } + memset(&data, 0, sizeof(DBT)); + switch (ret = dbp->get(dbp, NULL, &key, &data, 0)) { + case 0: + printf("%.*s : %.*s\n", + (int)key.size, (char *)key.data, + (int)data.size, (char *)data.data); + break; + default: + dbp->err(dbp, ret, "DB->get"); + break; + } + } + if ((ret = dbp->close(dbp, 0)) != 0) { + fprintf(stderr, + "%s: DB->close: %s\n", progname, db_strerror(ret)); + return (1); + } + return (0); + +err1: (void)dbp->close(dbp, 0); + return (ret); +} diff --git a/db/examples_c/ex_env.c b/db/examples_c/ex_env.c new file mode 100644 index 000000000..5490723a3 --- /dev/null +++ b/db/examples_c/ex_env.c @@ -0,0 +1,170 @@ +/*- + * See the file LICENSE for redistribution information. + * + * Copyright (c) 1996, 1997, 1998, 1999, 2000 + * Sleepycat Software. All rights reserved. + * + * $Id: ex_env.c,v 11.18 2000/10/27 20:32:00 dda Exp $ + */ + +#include "db_config.h" + +#ifndef NO_SYSTEM_INCLUDES +#include <sys/types.h> + +#include <stddef.h> +#include <stdio.h> +#include <stdlib.h> +#endif + +#include <db.h> + +#ifdef macintosh +#define DATABASE_HOME ":database" +#define CONFIG_DATA_DIR ":database" +#else +#ifdef DB_WIN32 +#define DATABASE_HOME "\\tmp\\database" +#define CONFIG_DATA_DIR "\\database\\files" +#else +#ifdef HAVE_VXWORKS +#define DATABASE_HOME "/ata0/vxtmp/database" +#define CONFIG_DATA_DIR "/vxtmp/vxtmp/database/files" +#else +#define DATABASE_HOME "/tmp/database" +#define CONFIG_DATA_DIR "/database/files" +#endif +#endif +#endif + +int db_setup __P((char *, char *, FILE *, char *)); +int db_teardown __P((char *, char *, FILE *, char *)); +#ifdef HAVE_VXWORKS +int ex_env __P((void)); +#define ERROR_RETURN ERROR +#define VXSHM_KEY 11 +#else +int main __P((void)); +#define ERROR_RETURN 1 +#endif + +/* + * An example of a program creating/configuring a Berkeley DB environment. + */ +int +#ifdef HAVE_VXWORKS +ex_env() +#else +main() +#endif +{ + int ret; + char *data_dir, *home; + char *progname = "ex_env"; /* Program name. */ + + /* + * All of the shared database files live in DATABASE_HOME, but + * data files will live in CONFIG_DATA_DIR. + */ + home = DATABASE_HOME; + data_dir = CONFIG_DATA_DIR; + + printf("Setup env\n"); + if ((ret = db_setup(home, data_dir, stderr, progname)) != 0) + return (ret); + + printf("Teardown env\n"); + if ((ret = db_teardown(home, data_dir, stderr, progname)) != 0) + return (ret); + + return (0); +} + +int +db_setup(home, data_dir, errfp, progname) + char *home, *data_dir, *progname; + FILE *errfp; +{ + DB_ENV *dbenv; + int ret; + + /* + * Create an environment object and initialize it for error + * reporting. + */ + if ((ret = db_env_create(&dbenv, 0)) != 0) { + fprintf(errfp, "%s: %s\n", progname, db_strerror(ret)); + return (ERROR_RETURN); + } + dbenv->set_errfile(dbenv, errfp); + dbenv->set_errpfx(dbenv, progname); + +#ifdef HAVE_VXWORKS + /* VxWorks needs to specify a base segment ID. */ + if ((ret = dbenv->set_shm_key(dbenv, VXSHM_KEY)) != 0) { + fprintf(errfp, "%s: %s\n", progname, db_strerror(ret)); + return (ERROR_RETURN); + } +#endif + + /* + * We want to specify the shared memory buffer pool cachesize, + * but everything else is the default. + */ + if ((ret = dbenv->set_cachesize(dbenv, 0, 64 * 1024, 0)) != 0) { + dbenv->err(dbenv, ret, "set_cachesize"); + dbenv->close(dbenv, 0); + return (ERROR_RETURN); + } + + /* Databases are in a subdirectory. */ + (void)dbenv->set_data_dir(dbenv, data_dir); + + /* Open the environment with full transactional support. */ + if ((ret = dbenv->open(dbenv, home, + DB_CREATE | DB_INIT_LOCK | DB_INIT_LOG | DB_INIT_MPOOL | DB_INIT_TXN, + 0)) != 0) { + dbenv->err(dbenv, ret, "environment open: %s", home); + dbenv->close(dbenv, 0); + return (ERROR_RETURN); + } + + /* Do something interesting... */ + + /* Close the handle. */ + if ((ret = dbenv->close(dbenv, 0)) != 0) { + fprintf(stderr, "DBENV->close: %s\n", db_strerror(ret)); + return (ERROR_RETURN); + } + return (0); +} + +int +db_teardown(home, data_dir, errfp, progname) + char *home, *data_dir, *progname; + FILE *errfp; +{ + DB_ENV *dbenv; + int ret; + + /* Remove the shared database regions. */ + if ((ret = db_env_create(&dbenv, 0)) != 0) { + fprintf(errfp, "%s: %s\n", progname, db_strerror(ret)); + return (ERROR_RETURN); + } + dbenv->set_errfile(dbenv, errfp); + dbenv->set_errpfx(dbenv, progname); +#ifdef HAVE_VXWORKS + if ((ret = dbenv->set_shm_key(dbenv, VXSHM_KEY)) != 0) { + fprintf(errfp, "%s: %s\n", progname, db_strerror(ret)); + return (ERROR_RETURN); + } +#endif + + (void)dbenv->set_data_dir(dbenv, data_dir); + if ((ret = dbenv->remove(dbenv, home, 0)) != 0) { + fprintf(stderr, "DBENV->remove: %s\n", db_strerror(ret)); + return (ERROR_RETURN); + } + return (0); +} diff --git a/db/examples_c/ex_lock.c b/db/examples_c/ex_lock.c new file mode 100644 index 000000000..e858be6b3 --- /dev/null +++ b/db/examples_c/ex_lock.c @@ -0,0 +1,235 @@ +/*- + * See the file LICENSE for redistribution information. + * + * Copyright (c) 1997, 1998, 1999, 2000 + * Sleepycat Software. All rights reserved. + * + * $Id: ex_lock.c,v 11.6 2001/01/04 14:23:29 dda Exp $ + */ + +#include "db_config.h" + +#ifndef NO_SYSTEM_INCLUDES +#include <sys/types.h> + +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#endif + +#include <db.h> + +void db_init __P((char *, u_int32_t, int)); +int main __P((int, char *[])); +void usage __P((void)); + +DB_ENV *dbenv; +const char + *progname = "ex_lock"; /* Program name. */ + +int +main(argc, argv) + int argc; + char *argv[]; +{ + extern char *optarg; + extern int optind; + DBT lock_dbt; + DB_LOCK lock; + DB_LOCK *locks; + db_lockmode_t lock_type; + long held; + u_int32_t len, locker, maxlocks; + int ch, do_unlink, did_get, i, lockid, lockcount, ret; + char *home, opbuf[16], objbuf[1024], lockbuf[16]; + + home = "TESTDIR"; + maxlocks = 0; + do_unlink = 0; + while ((ch = getopt(argc, argv, "h:m:u")) != EOF) + switch (ch) { + case 'h': + home = optarg; + break; + case 'm': + if ((i = atoi(optarg)) <= 0) + usage(); + maxlocks = (u_int32_t)i; /* XXX: possible overflow. */ + break; + case 'u': + do_unlink = 1; + break; + case '?': + default: + usage(); + } + argc -= optind; + argv += optind; + + if (argc != 0) + usage(); + + /* Initialize the database environment. */ + db_init(home, maxlocks, do_unlink); + + locks = 0; + lockcount = 0; + + /* + * Accept lock requests. + */ + if ((ret = lock_id(dbenv, &locker)) != 0) { + dbenv->err(dbenv, ret, "unable to get locker id"); + (void)dbenv->close(dbenv, 0); + exit (1); + } + lockid = -1; + + memset(&lock_dbt, 0, sizeof(lock_dbt)); + for (held = 0, did_get = 0;;) { + printf("Operation get/release [get]> "); + fflush(stdout); + if (fgets(opbuf, sizeof(opbuf), stdin) == NULL) + break; + if ((len = strlen(opbuf)) <= 1 || strcmp(opbuf, "get\n") == 0) { + /* Acquire a lock. */ + printf("input object (text string) to lock> "); + fflush(stdout); + if (fgets(objbuf, sizeof(objbuf), stdin) == NULL) + break; + if ((len = strlen(objbuf)) <= 1) + continue; + + do { + printf("lock type read/write [read]> "); + fflush(stdout); + if (fgets(lockbuf, + sizeof(lockbuf), stdin) == NULL) + break; + len = strlen(lockbuf); + } while (len > 1 && + strcmp(lockbuf, "read\n") != 0 && + strcmp(lockbuf, "write\n") != 0); + if (len == 1 || strcmp(lockbuf, "read\n") == 0) + lock_type = DB_LOCK_READ; + else + lock_type = DB_LOCK_WRITE; + + lock_dbt.data = objbuf; + lock_dbt.size = strlen(objbuf); + ret = lock_get(dbenv, locker, + DB_LOCK_NOWAIT, &lock_dbt, lock_type, &lock); + if (ret == 0) { + did_get = 1; + lockid = lockcount++; + if (locks == NULL) + locks = + (DB_LOCK *)malloc(sizeof(DB_LOCK)); + else + locks = (DB_LOCK *)realloc(locks, + lockcount * sizeof(DB_LOCK)); + locks[lockid] = lock; + } + } else { + /* Release a lock. */ + do { + printf("input lock to release> "); + fflush(stdout); + if (fgets(objbuf, + sizeof(objbuf), stdin) == NULL) + break; + } while ((len = strlen(objbuf)) <= 1); + lockid = strtol(objbuf, NULL, 16); + if (lockid < 0 || lockid >= lockcount) { + printf("Lock #%d out of range\n", lockid); + continue; + } + lock = locks[lockid]; + ret = lock_put(dbenv, &lock); + did_get = 0; + } + switch (ret) { + case 0: + printf("Lock #%d %s\n", lockid, + did_get ? "granted" : "released"); + held += did_get ? 1 : -1; + break; + case DB_LOCK_NOTGRANTED: + dbenv->err(dbenv, ret, NULL); + break; + case DB_LOCK_DEADLOCK: + dbenv->err(dbenv, ret, + "lock_%s", did_get ? "get" : "put"); + break; + default: + dbenv->err(dbenv, ret, + "lock_%s", did_get ? "get" : "put"); + (void)dbenv->close(dbenv, 0); + exit (1); + } + } + + printf("\nClosing lock region %ld locks held\n", held); + + if (locks != NULL) + free(locks); + + if ((ret = dbenv->close(dbenv, 0)) != 0) { + fprintf(stderr, + "%s: dbenv->close: %s\n", progname, db_strerror(ret)); + return (1); + } + return (0); +} + +/* + * db_init -- + * Initialize the environment. + */ +void +db_init(home, maxlocks, do_unlink) + char *home; + u_int32_t maxlocks; + int do_unlink; +{ + int ret; + + if ((ret = db_env_create(&dbenv, 0)) != 0) { + fprintf(stderr, "%s: db_env_create: %s\n", + progname, db_strerror(ret)); + exit (1); + } + + if (do_unlink) { + if ((ret = dbenv->remove(dbenv, home, DB_FORCE)) != 0) { + fprintf(stderr, "%s: dbenv->remove: %s\n", + progname, db_strerror(ret)); + exit (1); + } + if ((ret = db_env_create(&dbenv, 0)) != 0) { + fprintf(stderr, "%s: db_env_create: %s\n", + progname, db_strerror(ret)); + exit (1); + } + } + + dbenv->set_errfile(dbenv, stderr); + dbenv->set_errpfx(dbenv, progname); + if (maxlocks != 0) + dbenv->set_lk_max_locks(dbenv, maxlocks); + + if ((ret = + dbenv->open(dbenv, home, DB_CREATE | DB_INIT_LOCK, 0)) != 0) { + dbenv->err(dbenv, ret, NULL); + (void)dbenv->close(dbenv, 0); + exit(1); + } +} + +void +usage() +{ + (void)fprintf(stderr, + "usage: %s [-u] [-h home] [-m maxlocks]\n", progname); + exit(1); +} diff --git a/db/examples_c/ex_mpool.c b/db/examples_c/ex_mpool.c new file mode 100644 index 000000000..376c66478 --- /dev/null +++ b/db/examples_c/ex_mpool.c @@ -0,0 +1,280 @@ +/*- + * See the file LICENSE for redistribution information. + * + * Copyright (c) 1997, 1998, 1999, 2000 + * Sleepycat Software. All rights reserved. + * + * $Id: ex_mpool.c,v 11.13 2000/10/27 20:32:00 dda Exp $ + */ + +#include "db_config.h" + +#ifndef NO_SYSTEM_INCLUDES +#include <sys/types.h> + +#if TIME_WITH_SYS_TIME +#include <sys/time.h> +#include <time.h> +#else +#if HAVE_SYS_TIME_H +#include <sys/time.h> +#else +#include <time.h> +#endif +#endif + +#include <errno.h> +#include <fcntl.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#endif + +#include <db.h> + +int init __P((char *, int, int, char *)); +int run __P((int, int, int, int, char *)); +int run_mpool __P((int, int, int, int, char *)); +#ifdef HAVE_VXWORKS +int ex_mpool __P((void)); +#define MPOOL "/vxtmp/vxtmp/mpool" /* File. */ +#define ERROR_RETURN ERROR +#define VXSHM_KEY 12 +#else +int main __P((int, char *[])); +void usage __P((char *)); +#define MPOOL "mpool" /* File. */ +#define ERROR_RETURN 1 +#endif + +#ifndef HAVE_VXWORKS +int +main(argc, argv) + int argc; + char *argv[]; +{ + extern char *optarg; + extern int optind; + int cachesize, ch, hits, npages, pagesize; + char *progname; + + cachesize = 20 * 1024; + hits = 1000; + npages = 50; + pagesize = 1024; + progname = argv[0]; + while ((ch = getopt(argc, argv, "c:h:n:p:")) != EOF) + switch (ch) { + case 'c': + if ((cachesize = atoi(optarg)) < 20 * 1024) + usage(progname); + break; + case 'h': + if ((hits = atoi(optarg)) <= 0) + usage(progname); + break; + case 'n': + if ((npages = atoi(optarg)) <= 0) + usage(progname); + break; + case 'p': + if ((pagesize = atoi(optarg)) <= 0) + usage(progname); + break; + case '?': + default: + usage(progname); + } + argc -= optind; + argv += optind; + + return (run_mpool(pagesize, cachesize, hits, npages, progname)); +} + +void +usage(progname) + char *progname; +{ + (void)fprintf(stderr, + "usage: %s [-c cachesize] [-h hits] [-n npages] [-p pagesize]\n", + progname); + exit(1); +} +#else +int +ex_mpool() +{ + char *progname = "ex_mpool"; /* Program name. */ + int cachesize, ch, hits, npages, pagesize; + + cachesize = 20 * 1024; + hits = 1000; + npages = 50; + pagesize = 1024; + + return (run_mpool(pagesize, cachesize, hits, npages, progname)); +} +#endif + +int +run_mpool(pagesize, cachesize, hits, npages, progname) + int pagesize, cachesize, hits, npages; + char *progname; +{ + int ret; + + /* Initialize the file. */ + if ((ret = init(MPOOL, pagesize, npages, progname)) != 0) + return (ret); + + /* Get the pages. */ + if ((ret = run(hits, cachesize, pagesize, npages, progname)) != 0) + return (ret); + + return (0); +} + +/* + * init -- + * Create a backing file. + */ +int +init(file, pagesize, npages, progname) + char *file, *progname; + int pagesize, npages; +{ + int cnt, flags, fd; + char *p; + + /* + * Create a file with the right number of pages, and store a page + * number on each page. + */ + flags = O_CREAT | O_RDWR | O_TRUNC; +#ifdef DB_WIN32 + flags |= O_BINARY; +#endif + if ((fd = open(file, flags, 0666)) < 0) { + fprintf(stderr, + "%s: %s: %s\n", progname, file, strerror(errno)); + return (ERROR_RETURN); + } + if ((p = (char *)malloc(pagesize)) == NULL) { + fprintf(stderr, "%s: %s\n", progname, strerror(ENOMEM)); + return (ERROR_RETURN); + } + + /* The pages are numbered from 0. */ + for (cnt = 0; cnt <= npages; ++cnt) { + *(int *)p = cnt; + if (write(fd, p, pagesize) != pagesize) { + fprintf(stderr, + "%s: %s: %s\n", progname, file, strerror(errno)); + return (ERROR_RETURN); + } + } + + (void)close(fd); + free(p); + return (0); +} + +/* + * run -- + * Get a set of pages. + */ +int +run(hits, cachesize, pagesize, npages, progname) + int hits, cachesize, pagesize, npages; + char *progname; +{ + DB_ENV *dbenv; + DB_MPOOLFILE *dbmfp; + db_pgno_t pageno; + int cnt, ret; + void *p; + + printf("%s: cachesize: %d; pagesize: %d; N pages: %d\n", + progname, cachesize, pagesize, npages); + + /* + * Open a memory pool, specify a cachesize, output error messages + * to stderr. + */ + if ((ret = db_env_create(&dbenv, 0)) != 0) { + fprintf(stderr, + "%s: db_env_create: %s\n", progname, db_strerror(ret)); + return (ERROR_RETURN); + } + dbenv->set_errfile(dbenv, stderr); + dbenv->set_errpfx(dbenv, progname); +#ifdef HAVE_VXWORKS + if ((ret = dbenv->set_shm_key(dbenv, VXSHM_KEY)) != 0) { + dbenv->err(dbenv, ret, "set_shm_key"); + return (ERROR_RETURN); + } +#endif + + /* Set the cachesize. */ + if ((ret = dbenv->set_cachesize(dbenv, 0, cachesize, 0)) != 0) { + dbenv->err(dbenv, ret, "set_cachesize"); + goto err1; + } + + /* Open the environment. */ + if ((ret = dbenv->open( + dbenv, NULL, DB_CREATE | DB_INIT_MPOOL, 0)) != 0) { + dbenv->err(dbenv, ret, "open"); + goto err1; + } + + /* Open the file in the environment. */ + if ((ret = + memp_fopen(dbenv, MPOOL, 0, 0, pagesize, NULL, &dbmfp)) != 0) { + dbenv->err(dbenv, ret, "memp_fopen: %s", MPOOL); + goto err1; + } + + printf("retrieve %d random pages... ", hits); + + srand((u_int)time(NULL)); + for (cnt = 0; cnt < hits; ++cnt) { + pageno = (rand() % npages) + 1; + if ((ret = memp_fget(dbmfp, &pageno, 0, &p)) != 0) { + dbenv->err(dbenv, ret, + "unable to retrieve page %lu", (u_long)pageno); + goto err2; + } + if (*(db_pgno_t *)p != pageno) { + dbenv->errx(dbenv, + "wrong page retrieved (%lu != %d)", + (u_long)pageno, *(int *)p); + goto err2; + } + if ((ret = memp_fput(dbmfp, p, 0)) != 0) { + dbenv->err(dbenv, ret, + "unable to return page %lu", (u_long)pageno); + goto err2; + } + } + + printf("successful.\n"); + + /* Close the file. */ + if ((ret = memp_fclose(dbmfp)) != 0) { + dbenv->err(dbenv, ret, "memp_fclose"); + goto err1; + } + + /* Close the pool. */ + if ((ret = dbenv->close(dbenv, 0)) != 0) { + fprintf(stderr, + "%s: db_env_create: %s\n", progname, db_strerror(ret)); + return (ERROR_RETURN); + } + return (0); + +err2: (void)memp_fclose(dbmfp); +err1: (void)dbenv->close(dbenv, 0); + return (ERROR_RETURN); +} diff --git a/db/examples_c/ex_thread.c b/db/examples_c/ex_thread.c new file mode 100644 index 000000000..93812ade7 --- /dev/null +++ b/db/examples_c/ex_thread.c @@ -0,0 +1,604 @@ +/*- + * See the file LICENSE for redistribution information. + * + * Copyright (c) 1997, 1998, 1999, 2000 + * Sleepycat Software. All rights reserved. + * + * $Id: ex_thread.c,v 11.9 2000/05/31 15:10:04 bostic Exp $ + */ + +#include "db_config.h" + +#ifndef NO_SYSTEM_INCLUDES +#include <sys/types.h> + +#if TIME_WITH_SYS_TIME +#include <sys/time.h> +#include <time.h> +#else +#if HAVE_SYS_TIME_H +#include <sys/time.h> +#else +#include <time.h> +#endif +#endif + +#include <errno.h> +#include <pthread.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#endif + +#include <db.h> + +/* + * NB: This application is written using POSIX 1003.1b-1993 pthreads + * interfaces, which may not be portable to your system. + */ +extern int sched_yield __P((void)); /* Pthread yield function. */ + +DB_ENV *db_init __P((char *)); +void *deadlock __P((void *)); +void fatal __P((char *, int, int)); +int main __P((int, char *[])); +int reader __P((int)); +void stats __P((void)); +void *trickle __P((void *)); +void *tstart __P((void *)); +void usage __P((void)); +void word __P((void)); +int writer __P((int)); + +struct _statistics { + int aborted; /* Write. */ + int aborts; /* Read/write. */ + int adds; /* Write. */ + int deletes; /* Write. */ + int txns; /* Write. */ + int found; /* Read. */ + int notfound; /* Read. */ +} *perf; + +const char + *progname = "ex_thread"; /* Program name. */ + +#define DATABASE "access.db" /* Database name. */ +#define WORDLIST "../test/wordlist" /* Dictionary. */ + +/* + * We can seriously increase the number of collisions and transaction + * aborts by yielding the scheduler after every DB call. Specify the + * -p option to do this. + */ +int punish; /* -p */ +int nlist; /* -n */ +int nreaders; /* -r */ +int verbose; /* -v */ +int nwriters; /* -w */ + +DB *dbp; /* Database handle. */ +DB_ENV *dbenv; /* Database environment. */ +int nthreads; /* Total threads. */ +char **list; /* Word list. */ + +/* + * ex_thread -- + * Run a simple threaded application of some numbers of readers and + * writers competing for a set of words. + * + * Example UNIX shell script to run this program: + * % rm -rf TESTDIR + * % mkdir TESTDIR + * % ex_thread -h TESTDIR + */ +int +main(argc, argv) + int argc; + char *argv[]; +{ + extern char *optarg; + extern int errno, optind; + pthread_t *tids; + int ch, i, ret; + char *home; + void *retp; + + nlist = 1000; + nreaders = nwriters = 4; + home = "TESTDIR"; + while ((ch = getopt(argc, argv, "h:pn:r:vw:")) != EOF) + switch (ch) { + case 'h': + home = optarg; + break; + case 'p': + punish = 1; + break; + case 'n': + nlist = atoi(optarg); + break; + case 'r': + nreaders = atoi(optarg); + break; + case 'v': + verbose = 1; + break; + case 'w': + nwriters = atoi(optarg); + break; + case '?': + default: + usage(); + } + argc -= optind; + argv += optind; + + /* Initialize the random number generator. */ + srand(getpid() | time(NULL)); + + /* Build the key list. */ + word(); + + /* Remove the previous database. */ + (void)unlink(DATABASE); + + /* Initialize the database environment. */ + dbenv = db_init(home); + + /* Initialize the database. */ + if ((ret = db_create(&dbp, dbenv, 0)) != 0) { + dbenv->err(dbenv, ret, "db_create"); + (void)dbenv->close(dbenv, 0); + return (1); + } + if ((ret = dbp->set_pagesize(dbp, 1024)) != 0) { + dbp->err(dbp, ret, "set_pagesize"); + goto err; + } + if ((ret = dbp->open(dbp, + DATABASE, NULL, DB_BTREE, DB_CREATE | DB_THREAD, 0664)) != 0) { + dbp->err(dbp, ret, "%s: open", DATABASE); + goto err; + } + + nthreads = nreaders + nwriters + 2; + printf("Running: readers %d, writers %d\n", nreaders, nwriters); + fflush(stdout); + + /* Create statistics structures, offset by 1. */ + if ((perf = calloc(nreaders + nwriters + 1, sizeof(*perf))) == NULL) + fatal(NULL, errno, 1); + + /* Create thread ID structures. */ + if ((tids = malloc(nthreads * sizeof(pthread_t))) == NULL) + fatal(NULL, errno, 1); + + /* Create reader/writer threads. */ + for (i = 0; i < nreaders + nwriters; ++i) + if (pthread_create(&tids[i], NULL, tstart, (void *)i)) + fatal("pthread_create", errno, 1); + + /* Create buffer pool trickle thread. */ + if (pthread_create(&tids[i], NULL, trickle, &i)) + fatal("pthread_create", errno, 1); + ++i; + + /* Create deadlock detector thread. */ + if (pthread_create(&tids[i], NULL, deadlock, &i)) + fatal("pthread_create", errno, 1); + + /* Wait for the threads. */ + for (i = 0; i < nthreads; ++i) + (void)pthread_join(tids[i], &retp); + +err: (void)dbp->close(dbp, 0); + (void)dbenv->close(dbenv, 0); + + return (0); +} + +int +reader(id) + int id; +{ + DBT key, data; + int n, ret; + char buf[64]; + + /* + * DBT's must use local memory or malloc'd memory if the DB handle + * is accessed in a threaded fashion. + */ + memset(&key, 0, sizeof(DBT)); + memset(&data, 0, sizeof(DBT)); + data.flags = DB_DBT_MALLOC; + + /* + * Read-only threads do not require transaction protection, unless + * there's a need for repeatable reads. + */ + for (;;) { + /* Pick a key at random, and look it up. */ + n = rand() % nlist; + key.data = list[n]; + key.size = strlen(key.data); + + if (verbose) { + sprintf(buf, "reader: %d: list entry %d\n", id, n); + write(STDOUT_FILENO, buf, strlen(buf)); + } + + switch (ret = dbp->get(dbp, NULL, &key, &data, 0)) { + case DB_LOCK_DEADLOCK: /* Deadlock. */ + ++perf[id].aborts; + break; + case 0: /* Success. */ + ++perf[id].found; + free(data.data); + break; + case DB_NOTFOUND: /* Not found. */ + ++perf[id].notfound; + break; + default: + sprintf(buf, + "reader %d: dbp->get: %s", id, (char *)key.data); + fatal(buf, ret, 0); + } + } + return (0); +} + +int +writer(id) + int id; +{ + DBT key, data; + DB_TXN *tid; + time_t now, then; + int n, ret; + char buf[256], dbuf[10000]; + + time(&now); + then = now; + + /* + * DBT's must use local memory or malloc'd memory if the DB handle + * is accessed in a threaded fashion. + */ + memset(&key, 0, sizeof(DBT)); + memset(&data, 0, sizeof(DBT)); + data.data = dbuf; + data.ulen = sizeof(dbuf); + data.flags = DB_DBT_USERMEM; + + for (;;) { + /* Pick a random key. */ + n = rand() % nlist; + key.data = list[n]; + key.size = strlen(key.data); + + if (verbose) { + sprintf(buf, "writer: %d: list entry %d\n", id, n); + write(STDOUT_FILENO, buf, strlen(buf)); + } + + /* Abort and retry. */ + if (0) { +retry: if ((ret = txn_abort(tid)) != 0) + fatal("txn_abort", ret, 1); + ++perf[id].aborts; + ++perf[id].aborted; + } + + /* Thread #1 prints out the stats every 20 seconds. */ + if (id == 1) { + time(&now); + if (now - then >= 20) { + stats(); + then = now; + } + } + + /* Begin the transaction. */ + if ((ret = txn_begin(dbenv, NULL, &tid, 0)) != 0) + fatal("txn_begin", ret, 1); + + /* + * Get the key. If it doesn't exist, add it. If it does + * exist, delete it. + */ + switch (ret = dbp->get(dbp, tid, &key, &data, 0)) { + case DB_LOCK_DEADLOCK: + goto retry; + case 0: + goto delete; + case DB_NOTFOUND: + goto add; + } + + sprintf(buf, "writer: %d: dbp->get", id); + fatal(buf, ret, 1); + /* NOTREACHED */ + +delete: /* Delete the key. */ + switch (ret = dbp->del(dbp, tid, &key, 0)) { + case DB_LOCK_DEADLOCK: + goto retry; + case 0: + ++perf[id].deletes; + goto commit; + } + + sprintf(buf, "writer: %d: dbp->del", id); + fatal(buf, ret, 1); + /* NOTREACHED */ + +add: /* Add the key. 1 data item in 30 is an overflow item. */ + data.size = 20 + rand() % 128; + if (rand() % 30 == 0) + data.size += 8192; + + switch (ret = dbp->put(dbp, tid, &key, &data, 0)) { + case DB_LOCK_DEADLOCK: + goto retry; + case 0: + ++perf[id].adds; + goto commit; + default: + sprintf(buf, "writer: %d: dbp->put", id); + fatal(buf, ret, 1); + } + +commit: /* The transaction finished, commit it. */ + if ((ret = txn_commit(tid, 0)) != 0) + fatal("txn_commit", ret, 1); + + /* + * Every time the thread completes 20 transactions, show + * our progress. + */ + if (++perf[id].txns % 20 == 0) { + sprintf(buf, +"writer: %2d: adds: %4d: deletes: %4d: aborts: %4d: txns: %4d\n", + id, perf[id].adds, perf[id].deletes, + perf[id].aborts, perf[id].txns); + write(STDOUT_FILENO, buf, strlen(buf)); + } + + /* + * If this thread was aborted more than 5 times before + * the transaction finished, complain. + */ + if (perf[id].aborted > 5) { + sprintf(buf, +"writer: %2d: adds: %4d: deletes: %4d: aborts: %4d: txns: %4d: ABORTED: %2d\n", + id, perf[id].adds, perf[id].deletes, + perf[id].aborts, perf[id].txns, perf[id].aborted); + write(STDOUT_FILENO, buf, strlen(buf)); + } + perf[id].aborted = 0; + } + return (0); +} + +/* + * stats -- + * Display reader/writer thread statistics. To display the statistics + * for the mpool trickle or deadlock threads, use db_stat(1). + */ +void +stats() +{ + int id; + char *p, buf[8192]; + + p = buf + sprintf(buf, "-------------\n"); + for (id = 0; id < nreaders + nwriters;) + if (id++ < nwriters) + p += sprintf(p, + "writer: %2d: adds: %4d: deletes: %4d: aborts: %4d: txns: %4d\n", + id, perf[id].adds, + perf[id].deletes, perf[id].aborts, perf[id].txns); + else + p += sprintf(p, + "reader: %2d: found: %5d: notfound: %5d: aborts: %4d\n", + id, perf[id].found, + perf[id].notfound, perf[id].aborts); + p += sprintf(p, "-------------\n"); + + write(STDOUT_FILENO, buf, p - buf); +} + +/* + * db_init -- + * Initialize the environment. + */ +DB_ENV * +db_init(home) + char *home; +{ + DB_ENV *dbenv; + int ret; + + if (punish) { + (void)db_env_set_pageyield(1); + (void)db_env_set_func_yield(sched_yield); + } + + if ((ret = db_env_create(&dbenv, 0)) != 0) { + fprintf(stderr, + "%s: db_env_create: %s\n", progname, db_strerror(ret)); + exit (1); + } + dbenv->set_errfile(dbenv, stderr); + dbenv->set_errpfx(dbenv, progname); + (void)dbenv->set_cachesize(dbenv, 0, 100 * 1024, 0); + (void)dbenv->set_lg_max(dbenv, 200000); + + if ((ret = dbenv->open(dbenv, home, + DB_CREATE | DB_INIT_LOCK | DB_INIT_LOG | + DB_INIT_MPOOL | DB_INIT_TXN | DB_THREAD, 0)) != 0) { + dbenv->err(dbenv, ret, NULL); + (void)dbenv->close(dbenv, 0); + exit (1); + } + return (dbenv); +} + +/* + * tstart -- + * Thread start function for readers and writers. + */ +void * +tstart(arg) + void *arg; +{ + pthread_t tid; + u_int id; + + id = (u_int)arg + 1; + + tid = pthread_self(); + + if (id <= (u_int)nwriters) { + printf("write thread %d starting: tid: %lu\n", id, (u_long)tid); + fflush(stdout); + writer(id); + } else { + printf("read thread %d starting: tid: %lu\n", id, (u_long)tid); + fflush(stdout); + reader(id); + } + + /* NOTREACHED */ + return (NULL); +} + +/* + * deadlock -- + * Thread start function for lock_detect(). + */ +void * +deadlock(arg) + void *arg; +{ + struct timeval t; + pthread_t tid; + + arg = arg; /* XXX: shut the compiler up. */ + tid = pthread_self(); + + printf("deadlock thread starting: tid: %lu\n", (u_long)tid); + fflush(stdout); + + t.tv_sec = 0; + t.tv_usec = 100000; + for (;;) { + (void)lock_detect(dbenv, + DB_LOCK_CONFLICT, DB_LOCK_YOUNGEST, NULL); + + /* Check every 100ms. */ + (void)select(0, NULL, NULL, NULL, &t); + } + + /* NOTREACHED */ + return (NULL); +} + +/* + * trickle -- + * Thread start function for memp_trickle(). + */ +void * +trickle(arg) + void *arg; +{ + pthread_t tid; + int wrote; + char buf[64]; + + arg = arg; /* XXX: shut the compiler up. */ + tid = pthread_self(); + + printf("trickle thread starting: tid: %lu\n", (u_long)tid); + fflush(stdout); + + for (;;) { + (void)memp_trickle(dbenv, 10, &wrote); + if (verbose) { + sprintf(buf, "trickle: wrote %d\n", wrote); + write(STDOUT_FILENO, buf, strlen(buf)); + } + if (wrote == 0) { + sleep(1); + sched_yield(); + } + } + + /* NOTREACHED */ + return (NULL); +} + +/* + * word -- + * Build the dictionary word list. + */ +void +word() +{ + FILE *fp; + int cnt; + char buf[256]; + + if ((fp = fopen(WORDLIST, "r")) == NULL) + fatal(WORDLIST, errno, 1); + + if ((list = malloc(nlist * sizeof(char *))) == NULL) + fatal(NULL, errno, 1); + + for (cnt = 0; cnt < nlist; ++cnt) { + if (fgets(buf, sizeof(buf), fp) == NULL) + break; + if ((list[cnt] = strdup(buf)) == NULL) + fatal(NULL, errno, 1); + } + nlist = cnt; /* In case nlist was larger than possible. */ +} + +/* + * fatal -- + * Report a fatal error and quit. + */ +void +fatal(msg, err, syserr) + char *msg; + int err, syserr; +{ + fprintf(stderr, "%s: ", progname); + if (msg != NULL) { + fprintf(stderr, "%s", msg); + if (syserr) + fprintf(stderr, ": "); + } + if (syserr) + fprintf(stderr, "%s", strerror(err)); + fprintf(stderr, "\n"); + exit (1); + + /* NOTREACHED */ +} + +/* + * usage -- + * Usage message. + */ +void +usage() +{ + (void)fprintf(stderr, + "usage: %s [-pv] [-h home] [-n words] [-r readers] [-w writers]\n", + progname); + exit(1); +} diff --git a/db/examples_c/ex_tpcb.c b/db/examples_c/ex_tpcb.c new file mode 100644 index 000000000..2fd11510a --- /dev/null +++ b/db/examples_c/ex_tpcb.c @@ -0,0 +1,811 @@ +/*- + * See the file LICENSE for redistribution information. + * + * Copyright (c) 1997, 1998, 1999, 2000 + * Sleepycat Software. All rights reserved. + * + * $Id: ex_tpcb.c,v 11.21 2000/10/27 20:32:00 dda Exp $ + */ + +#include "db_config.h" + +#ifndef NO_SYSTEM_INCLUDES +#include <sys/types.h> + +#if TIME_WITH_SYS_TIME +#include <sys/time.h> +#include <time.h> +#else +#if HAVE_SYS_TIME_H +#include <sys/time.h> +#else +#include <time.h> +#endif +#endif + +#include <errno.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#endif + +#ifdef DB_WIN32 +#include <sys/types.h> +#include <sys/timeb.h> +#endif + +#include <db.h> + +typedef enum { ACCOUNT, BRANCH, TELLER } FTYPE; + +DB_ENV *db_init __P((char *, char *, int, int, int)); +int hpopulate __P((DB *, int, int, int, int)); +int populate __P((DB *, u_int32_t, u_int32_t, int, char *)); +u_int32_t random_id __P((FTYPE, int, int, int)); +u_int32_t random_int __P((u_int32_t, u_int32_t)); +int tp_populate __P((DB_ENV *, int, int, int, int, int)); +int tp_run __P((DB_ENV *, int, int, int, int, int)); +int tp_txn __P((DB_ENV *, DB *, DB *, DB *, DB *, int, int, int, int)); + +#ifdef HAVE_VXWORKS +#define ERROR_RETURN ERROR +#define HOME "/vxtmp/vxtmp/TESTDIR" +#define VXSHM_KEY 13 +int ex_tpcb_init __P(()); +int ex_tpcb __P(()); +#else +#define ERROR_RETURN 1 +void invarg __P((char *, int, char *)); +int main __P((int, char *[])); +void usage __P((char *)); +#endif + +/* + * This program implements a basic TPC/B driver program. To create the + * TPC/B database, run with the -i (init) flag. The number of records + * with which to populate the account, history, branch, and teller tables + * is specified by the a, s, b, and t flags respectively. To run a TPC/B + * test, use the n flag to indicate a number of transactions to run (note + * that you can run many of these processes in parallel to simulate a + * multiuser test run). + */ +#define TELLERS_PER_BRANCH 10 +#define ACCOUNTS_PER_TELLER 10000 +#define HISTORY_PER_BRANCH 2592000 + +/* + * The default configuration that adheres to TPCB scaling rules requires + * nearly 3 GB of space. To avoid requiring that much space for testing, + * we set the parameters much lower. If you want to run a valid 10 TPS + * configuration, define VALID_SCALING. + */ +#ifdef VALID_SCALING +#define ACCOUNTS 1000000 +#define BRANCHES 10 +#define TELLERS 100 +#define HISTORY 25920000 +#endif + +#ifdef TINY +#define ACCOUNTS 1000 +#define BRANCHES 10 +#define TELLERS 100 +#define HISTORY 10000 +#endif + +#ifdef VERY_TINY +#define ACCOUNTS 500 +#define BRANCHES 10 +#define TELLERS 50 +#define HISTORY 5000 +#endif + +#if !defined(VALID_SCALING) && !defined(TINY) && !defined(VERY_TINY) +#define ACCOUNTS 100000 +#define BRANCHES 10 +#define TELLERS 100 +#define HISTORY 259200 +#endif + +#define HISTORY_LEN 100 +#define RECLEN 100 +#define BEGID 1000000 + +typedef struct _defrec { + u_int32_t id; + u_int32_t balance; + u_int8_t pad[RECLEN - sizeof(u_int32_t) - sizeof(u_int32_t)]; +} defrec; + +typedef struct _histrec { + u_int32_t aid; + u_int32_t bid; + u_int32_t tid; + u_int32_t amount; + u_int8_t pad[RECLEN - 4 * sizeof(u_int32_t)]; +} histrec; + +#ifdef HAVE_VXWORKS +int +ex_tpcb_init() +{ + DB_ENV *dbenv; + int accounts, branches, ret, seed, t_ret, tellers, history, verbose; + char *home; + char *progname = "ex_tpcb_init"; /* Program name. */ + + verbose = 1; + if ((dbenv = db_init(HOME, progname, 0, 1, 0)) == NULL) + return (ERROR_RETURN); + + accounts = ACCOUNTS; + branches = BRANCHES; + tellers = TELLERS; + history = HISTORY; + + if ((ret = tp_populate(dbenv, accounts, branches, history, tellers, + verbose)) != OK) + fprintf(stderr, "%s: %s\n", progname, db_strerror(ret)); + if ((t_ret = dbenv->close(dbenv, 0)) != 0) { + fprintf(stderr, "%s: %s\n", progname, db_strerror(ret)); + return (ERROR_RETURN); + } + + return (ret == 0 ? t_ret : ret); +} + +int +ex_tpcb() +{ + DB_ENV *dbenv; + int accounts, branches, seed, tellers, history; + int ch, mpool, ntxns, ret, t_ret, txn_no_sync, verbose; + char *progname = "ex_tpcb"; /* Program name. */ + + accounts = ACCOUNTS; + branches = BRANCHES; + tellers = TELLERS; + history = HISTORY; + + txn_no_sync = 0; + mpool = 0; + ntxns = 20; + verbose = 1; + seed = (int)((u_int)getpid() | time(NULL)); + + srand((u_int)seed); + + /* Initialize the database environment. */ + if ((dbenv = db_init(HOME, progname, mpool, 0, + txn_no_sync ? DB_TXN_NOSYNC : 0)) == NULL) + return (ERROR_RETURN); + + if (verbose) + printf("%ld Accounts, %ld Branches, %ld Tellers, %ld History\n", + (long)accounts, (long)branches, + (long)tellers, (long)history); + + if ((ret = tp_run(dbenv, ntxns, accounts, branches, tellers, verbose)) + != OK) + fprintf(stderr, "tp_run failed\n"); + + if ((t_ret = dbenv->close(dbenv, 0)) != 0) { + fprintf(stderr, "%s: %s\n", progname, db_strerror(ret)); + return (ERROR_RETURN); + } + return (ret == 0 ? t_ret : ret); +} +#else +int +main(argc, argv) + int argc; + char *argv[]; +{ + extern char *optarg; + extern int optind; + DB_ENV *dbenv; + int accounts, branches, seed, tellers, history; + int ch, iflag, mpool, ntxns, ret, txn_no_sync, verbose; + char *home, *progname; + + home = "TESTDIR"; + progname = "ex_tpcb"; + accounts = branches = history = tellers = 0; + txn_no_sync = 0; + mpool = ntxns = 0; + verbose = 0; + iflag = 0; + seed = (int)((u_int)getpid() | time(NULL)); + while ((ch = getopt(argc, argv, "a:b:c:fh:in:S:s:t:v")) != EOF) + switch (ch) { + case 'a': /* Number of account records */ + if ((accounts = atoi(optarg)) <= 0) + invarg(progname, ch, optarg); + break; + case 'b': /* Number of branch records */ + if ((branches = atoi(optarg)) <= 0) + invarg(progname, ch, optarg); + break; + case 'c': /* Cachesize in bytes */ + if ((mpool = atoi(optarg)) <= 0) + invarg(progname, ch, optarg); + break; + case 'f': /* Fast mode: no txn sync. */ + txn_no_sync = 1; + break; + case 'h': /* DB home. */ + home = optarg; + break; + case 'i': /* Initialize the test. */ + iflag = 1; + break; + case 'n': /* Number of transactions */ + if ((ntxns = atoi(optarg)) <= 0) + invarg(progname, ch, optarg); + break; + case 'S': /* Random number seed. */ + if ((seed = atoi(optarg)) <= 0) + invarg(progname, ch, optarg); + break; + case 's': /* Number of history records */ + if ((history = atoi(optarg)) <= 0) + invarg(progname, ch, optarg); + break; + case 't': /* Number of teller records */ + if ((tellers = atoi(optarg)) <= 0) + invarg(progname, ch, optarg); + break; + case 'v': /* Verbose option. */ + verbose = 1; + break; + case '?': + default: + usage(progname); + } + argc -= optind; + argv += optind; + + srand((u_int)seed); + + /* Initialize the database environment. */ + if ((dbenv = db_init(home, + progname, mpool, iflag, txn_no_sync ? DB_TXN_NOSYNC : 0)) == NULL) + return (1); + + accounts = accounts == 0 ? ACCOUNTS : accounts; + branches = branches == 0 ? BRANCHES : branches; + tellers = tellers == 0 ? TELLERS : tellers; + history = history == 0 ? HISTORY : history; + + if (verbose) + printf("%ld Accounts, %ld Branches, %ld Tellers, %ld History\n", + (long)accounts, (long)branches, + (long)tellers, (long)history); + + if (iflag) { + if (ntxns != 0) + usage(progname); + tp_populate(dbenv, + accounts, branches, history, tellers, verbose); + } else { + if (ntxns == 0) + usage(progname); + tp_run(dbenv, ntxns, accounts, branches, tellers, verbose); + } + + if ((ret = dbenv->close(dbenv, 0)) != 0) { + fprintf(stderr, "%s: dbenv->close failed: %s\n", + progname, db_strerror(ret)); + return (1); + } + + return (0); +} + +void +invarg(progname, arg, str) + char *progname; + int arg; + char *str; +{ + (void)fprintf(stderr, + "%s: invalid argument for -%c: %s\n", progname, arg, str); + exit (1); +} + +void +usage(progname) + char *progname; +{ + char *a1, *a2; + + a1 = "[-fv] [-a accounts] [-b branches]\n"; + a2 = "\t[-c cache_size] [-h home] [-S seed] [-s history] [-t tellers]"; + (void)fprintf(stderr, "usage: %s -i %s %s\n", progname, a1, a2); + (void)fprintf(stderr, + " %s -n transactions %s %s\n", progname, a1, a2); + exit(1); +} +#endif + +/* + * db_init -- + * Initialize the environment. + */ +DB_ENV * +db_init(home, prefix, cachesize, initializing, flags) + char *home, *prefix; + int cachesize, initializing, flags; +{ + DB_ENV *dbenv; + u_int32_t local_flags; + int ret; + + if ((ret = db_env_create(&dbenv, 0)) != 0) { + dbenv->err(dbenv, ret, "db_env_create"); + return (NULL); + } + dbenv->set_errfile(dbenv, stderr); + dbenv->set_errpfx(dbenv, prefix); +#ifdef HAVE_VXWORKS + if ((ret = dbenv->set_shm_key(dbenv, VXSHM_KEY)) != 0) { + dbenv->err(dbenv, ret, "set_shm_key"); + return (NULL); + } +#endif + (void)dbenv->set_cachesize(dbenv, 0, + cachesize == 0 ? 4 * 1024 * 1024 : (u_int32_t)cachesize, 0); + + local_flags = flags | DB_CREATE | (initializing ? DB_INIT_MPOOL : + DB_INIT_TXN | DB_INIT_LOCK | DB_INIT_LOG | DB_INIT_MPOOL); + if ((ret = dbenv->open(dbenv, home, local_flags, 0)) != 0) { + dbenv->err(dbenv, ret, "DBENV->open: %s", home); + (void)dbenv->close(dbenv, 0); + return (NULL); + } + return (dbenv); +} + +/* + * Initialize the database to the specified number of accounts, branches, + * history records, and tellers. + */ +int +tp_populate(env, accounts, branches, history, tellers, verbose) + DB_ENV *env; + int accounts, branches, history, tellers, verbose; +{ + DB *dbp; + char dbname[100]; + u_int32_t balance, idnum, oflags; + u_int32_t end_anum, end_bnum, end_tnum; + u_int32_t start_anum, start_bnum, start_tnum; + int ret; + + idnum = BEGID; + balance = 500000; +#ifdef HAVE_VXWORKS + oflags = DB_CREATE; +#else + oflags = DB_CREATE | DB_TRUNCATE; +#endif + + if ((ret = db_create(&dbp, env, 0)) != 0) { + env->err(env, ret, "db_create"); + return (ERROR_RETURN); + } + (void)dbp->set_h_nelem(dbp, (u_int32_t)accounts); + + snprintf(dbname, sizeof(dbname), "account"); + if ((ret = dbp->open(dbp, dbname, NULL, + DB_HASH, oflags, 0644)) != 0) { + env->err(env, ret, "DB->open: account"); + return (ERROR_RETURN); + } + + start_anum = idnum; + populate(dbp, idnum, balance, accounts, "account"); + idnum += accounts; + end_anum = idnum - 1; + if ((ret = dbp->close(dbp, 0)) != 0) { + env->err(env, ret, "DB->close: account"); + return (ERROR_RETURN); + } + if (verbose) + printf("Populated accounts: %ld - %ld\n", + (long)start_anum, (long)end_anum); + + /* + * Since the number of branches is very small, we want to use very + * small pages and only 1 key per page, i.e., key-locking instead + * of page locking. + */ + if ((ret = db_create(&dbp, env, 0)) != 0) { + env->err(env, ret, "db_create"); + return (ERROR_RETURN); + } + (void)dbp->set_h_ffactor(dbp, 1); + (void)dbp->set_h_nelem(dbp, (u_int32_t)branches); + (void)dbp->set_pagesize(dbp, 512); + snprintf(dbname, sizeof(dbname), "branch"); + if ((ret = dbp->open(dbp, dbname, NULL, + DB_HASH, oflags, 0644)) != 0) { + env->err(env, ret, "DB->open: branch"); + return (ERROR_RETURN); + } + start_bnum = idnum; + populate(dbp, idnum, balance, branches, "branch"); + idnum += branches; + end_bnum = idnum - 1; + if ((ret = dbp->close(dbp, 0)) != 0) { + env->err(env, ret, "DB->close: branch"); + return (ERROR_RETURN); + } + if (verbose) + printf("Populated branches: %ld - %ld\n", + (long)start_bnum, (long)end_bnum); + + /* + * In the case of tellers, we also want small pages, but we'll let + * the fill factor dynamically adjust itself. + */ + if ((ret = db_create(&dbp, env, 0)) != 0) { + env->err(env, ret, "db_create"); + return (ERROR_RETURN); + } + (void)dbp->set_h_ffactor(dbp, 0); + (void)dbp->set_h_nelem(dbp, (u_int32_t)tellers); + (void)dbp->set_pagesize(dbp, 512); + snprintf(dbname, sizeof(dbname), "teller"); + if ((ret = dbp->open(dbp, dbname, NULL, + DB_HASH, oflags, 0644)) != 0) { + env->err(env, ret, "DB->open: teller"); + return (ERROR_RETURN); + } + + start_tnum = idnum; + populate(dbp, idnum, balance, tellers, "teller"); + idnum += tellers; + end_tnum = idnum - 1; + if ((ret = dbp->close(dbp, 0)) != 0) { + env->err(env, ret, "DB->close: teller"); + return (ERROR_RETURN); + } + if (verbose) + printf("Populated tellers: %ld - %ld\n", + (long)start_tnum, (long)end_tnum); + + if ((ret = db_create(&dbp, env, 0)) != 0) { + env->err(env, ret, "db_create"); + return (ERROR_RETURN); + } + (void)dbp->set_re_len(dbp, HISTORY_LEN); + snprintf(dbname, sizeof(dbname), "history"); + if ((ret = dbp->open(dbp, dbname, NULL, + DB_RECNO, oflags, 0644)) != 0) { + env->err(env, ret, "DB->open: history"); + return (ERROR_RETURN); + } + + hpopulate(dbp, history, accounts, branches, tellers); + if ((ret = dbp->close(dbp, 0)) != 0) { + env->err(env, ret, "DB->close: history"); + return (ERROR_RETURN); + } + return (0); +} + +int +populate(dbp, start_id, balance, nrecs, msg) + DB *dbp; + u_int32_t start_id, balance; + int nrecs; + char *msg; +{ + DBT kdbt, ddbt; + defrec drec; + int i, ret; + + kdbt.flags = 0; + kdbt.data = &drec.id; + kdbt.size = sizeof(u_int32_t); + ddbt.flags = 0; + ddbt.data = &drec; + ddbt.size = sizeof(drec); + memset(&drec.pad[0], 1, sizeof(drec.pad)); + + for (i = 0; i < nrecs; i++) { + drec.id = start_id + (u_int32_t)i; + drec.balance = balance; + if ((ret = + (dbp->put)(dbp, NULL, &kdbt, &ddbt, DB_NOOVERWRITE)) != 0) { + dbp->err(dbp, + ret, "Failure initializing %s file\n", msg); + return (ERROR_RETURN); + } + } + return (0); +} + +int +hpopulate(dbp, history, accounts, branches, tellers) + DB *dbp; + int history, accounts, branches, tellers; +{ + DBT kdbt, ddbt; + histrec hrec; + db_recno_t key; + int i, ret; + + memset(&kdbt, 0, sizeof(kdbt)); + memset(&ddbt, 0, sizeof(ddbt)); + ddbt.data = &hrec; + ddbt.size = sizeof(hrec); + kdbt.data = &key; + kdbt.size = sizeof(key); + memset(&hrec.pad[0], 1, sizeof(hrec.pad)); + hrec.amount = 10; + + for (i = 1; i <= history; i++) { + hrec.aid = random_id(ACCOUNT, accounts, branches, tellers); + hrec.bid = random_id(BRANCH, accounts, branches, tellers); + hrec.tid = random_id(TELLER, accounts, branches, tellers); + if ((ret = dbp->put(dbp, NULL, &kdbt, &ddbt, DB_APPEND)) != 0) { + dbp->err(dbp, ret, "dbp->put"); + return (ERROR_RETURN); + } + } + return (0); +} + +u_int32_t +random_int(lo, hi) + u_int32_t lo, hi; +{ + u_int32_t ret; + int t; + +#ifndef RAND_MAX +#define RAND_MAX 0x7fffffff +#endif + t = rand(); + ret = (u_int32_t)(((double)t / ((double)(RAND_MAX) + 1)) * + (hi - lo + 1)); + ret += lo; + return (ret); +} + +u_int32_t +random_id(type, accounts, branches, tellers) + FTYPE type; + int accounts, branches, tellers; +{ + u_int32_t min, max, num; + + max = min = BEGID; + num = accounts; + switch(type) { + case TELLER: + min += branches; + num = tellers; + /* FALLTHROUGH */ + case BRANCH: + if (type == BRANCH) + num = branches; + min += accounts; + /* FALLTHROUGH */ + case ACCOUNT: + max = min + num - 1; + } + return (random_int(min, max)); +} + +int +tp_run(dbenv, n, accounts, branches, tellers, verbose) + DB_ENV *dbenv; + int n, accounts, branches, tellers, verbose; +{ + DB *adb, *bdb, *hdb, *tdb; + char dbname[100]; + double gtps, itps; + int failed, ifailed, ret, txns; + time_t starttime, curtime, lasttime; +#ifndef DB_WIN32 + pid_t pid; + + pid = getpid(); +#else + int pid; + + pid = 0; +#endif + + /* + * Open the database files. + */ + if ((ret = db_create(&adb, dbenv, 0)) != 0) { + dbenv->err(dbenv, ret, "db_create"); + return (ERROR_RETURN); + } + snprintf(dbname, sizeof(dbname), "account"); + if ((ret = adb->open(adb, dbname, NULL, DB_UNKNOWN, 0, 0)) != 0) { + dbenv->err(dbenv, ret, "DB->open: account"); + return (ERROR_RETURN); + } + + if ((ret = db_create(&bdb, dbenv, 0)) != 0) { + dbenv->err(dbenv, ret, "db_create"); + return (ERROR_RETURN); + } + snprintf(dbname, sizeof(dbname), "branch"); + if ((ret = bdb->open(bdb, dbname, NULL, DB_UNKNOWN, 0, 0)) != 0) { + dbenv->err(dbenv, ret, "DB->open: branch"); + return (ERROR_RETURN); + } + + if ((ret = db_create(&tdb, dbenv, 0)) != 0) { + dbenv->err(dbenv, ret, "db_create"); + return (ERROR_RETURN); + } + snprintf(dbname, sizeof(dbname), "teller"); + if ((ret = tdb->open(tdb, dbname, NULL, DB_UNKNOWN, 0, 0)) != 0) { + dbenv->err(dbenv, ret, "DB->open: teller"); + return (ERROR_RETURN); + } + + if ((ret = db_create(&hdb, dbenv, 0)) != 0) { + dbenv->err(dbenv, ret, "db_create"); + return (ERROR_RETURN); + } + snprintf(dbname, sizeof(dbname), "history"); + if ((ret = hdb->open(hdb, dbname, NULL, DB_UNKNOWN, 0, 0)) != 0) { + dbenv->err(dbenv, ret, "DB->open: history"); + return (ERROR_RETURN); + } + + txns = failed = ifailed = 0; + starttime = time(NULL); + lasttime = starttime; + while (n-- > 0) { + txns++; + ret = tp_txn(dbenv, adb, bdb, tdb, hdb, + accounts, branches, tellers, verbose); + if (ret != 0) { + failed++; + ifailed++; + } + if (n % 5000 == 0) { + curtime = time(NULL); + gtps = (double)(txns - failed) / (curtime - starttime); + itps = (double)(5000 - ifailed) / (curtime - lasttime); + printf("[%d] %d txns %d failed ", (int)pid, + txns, failed); + printf("%6.2f TPS (gross) %6.2f TPS (interval)\n", + gtps, itps); + lasttime = curtime; + ifailed = 0; + } + } + + (void)adb->close(adb, 0); + (void)bdb->close(bdb, 0); + (void)tdb->close(tdb, 0); + (void)hdb->close(hdb, 0); + + printf("%ld transactions begun %ld failed\n", (long)txns, (long)failed); + return (0); +} + +/* + * XXX Figure out the appropriate way to pick out IDs. + */ +int +tp_txn(dbenv, adb, bdb, tdb, hdb, accounts, branches, tellers, verbose) + DB_ENV *dbenv; + DB *adb, *bdb, *tdb, *hdb; + int accounts, branches, tellers, verbose; +{ + DBC *acurs, *bcurs, *tcurs; + DBT d_dbt, d_histdbt, k_dbt, k_histdbt; + DB_TXN *t; + db_recno_t key; + defrec rec; + histrec hrec; + int account, branch, teller; + + t = NULL; + acurs = bcurs = tcurs = NULL; + + /* + * XXX We could move a lot of this into the driver to make this + * faster. + */ + account = random_id(ACCOUNT, accounts, branches, tellers); + branch = random_id(BRANCH, accounts, branches, tellers); + teller = random_id(TELLER, accounts, branches, tellers); + + memset(&d_histdbt, 0, sizeof(d_histdbt)); + + memset(&k_histdbt, 0, sizeof(k_histdbt)); + k_histdbt.data = &key; + k_histdbt.size = sizeof(key); + + memset(&k_dbt, 0, sizeof(k_dbt)); + k_dbt.size = sizeof(int); + + memset(&d_dbt, 0, sizeof(d_dbt)); + d_dbt.flags = DB_DBT_USERMEM; + d_dbt.data = &rec; + d_dbt.ulen = sizeof(rec); + + hrec.aid = account; + hrec.bid = branch; + hrec.tid = teller; + hrec.amount = 10; + /* Request 0 bytes since we're just positioning. */ + d_histdbt.flags = DB_DBT_PARTIAL; + + /* START TIMING */ + if (txn_begin(dbenv, NULL, &t, 0) != 0) + goto err; + + if (adb->cursor(adb, t, &acurs, 0) != 0 || + bdb->cursor(bdb, t, &bcurs, 0) != 0 || + tdb->cursor(tdb, t, &tcurs, 0) != 0) + goto err; + + /* Account record */ + k_dbt.data = &account; + if (acurs->c_get(acurs, &k_dbt, &d_dbt, DB_SET) != 0) + goto err; + rec.balance += 10; + if (acurs->c_put(acurs, &k_dbt, &d_dbt, DB_CURRENT) != 0) + goto err; + + /* Branch record */ + k_dbt.data = &branch; + if (bcurs->c_get(bcurs, &k_dbt, &d_dbt, DB_SET) != 0) + goto err; + rec.balance += 10; + if (bcurs->c_put(bcurs, &k_dbt, &d_dbt, DB_CURRENT) != 0) + goto err; + + /* Teller record */ + k_dbt.data = &teller; + if (tcurs->c_get(tcurs, &k_dbt, &d_dbt, DB_SET) != 0) + goto err; + rec.balance += 10; + if (tcurs->c_put(tcurs, &k_dbt, &d_dbt, DB_CURRENT) != 0) + goto err; + + /* History record */ + d_histdbt.flags = 0; + d_histdbt.data = &hrec; + d_histdbt.ulen = sizeof(hrec); + if (hdb->put(hdb, t, &k_histdbt, &d_histdbt, DB_APPEND) != 0) + goto err; + + if (acurs->c_close(acurs) != 0 || bcurs->c_close(bcurs) != 0 || + tcurs->c_close(tcurs) != 0) + goto err; + + if (txn_commit(t, 0) != 0) + goto err; + + /* END TIMING */ + return (0); + +err: if (acurs != NULL) + (void)acurs->c_close(acurs); + if (bcurs != NULL) + (void)bcurs->c_close(bcurs); + if (tcurs != NULL) + (void)tcurs->c_close(tcurs); + if (t != NULL) + (void)txn_abort(t); + + if (verbose) + printf("Transaction A=%ld B=%ld T=%ld failed\n", + (long)account, (long)branch, (long)teller); + return (-1); +} diff --git a/db/examples_c/ex_tpcb.h b/db/examples_c/ex_tpcb.h new file mode 100644 index 000000000..ef90bc532 --- /dev/null +++ b/db/examples_c/ex_tpcb.h @@ -0,0 +1,39 @@ +/*- + * See the file LICENSE for redistribution information. + * + * Copyright (c) 1996, 1997, 1998, 1999, 2000 + * Sleepycat Software. All rights reserved. + * + * $Id: ex_tpcb.h,v 11.4 2000/05/17 19:21:02 bostic Exp $ + */ + +#ifndef _TPCB_H_ +#define _TPCB_H_ + +typedef enum { ACCOUNT, BRANCH, TELLER } FTYPE; + +#define TELLERS_PER_BRANCH 100 +#define ACCOUNTS_PER_TELLER 1000 + +#define ACCOUNTS 1000000 +#define BRANCHES 10 +#define TELLERS 1000 +#define HISTORY 1000000 +#define HISTORY_LEN 100 +#define RECLEN 100 +#define BEGID 1000000 + +typedef struct _defrec { + u_int32_t id; + u_int32_t balance; + u_int8_t pad[RECLEN - sizeof(u_int32_t) - sizeof(u_int32_t)]; +} defrec; + +typedef struct _histrec { + u_int32_t aid; + u_int32_t bid; + u_int32_t tid; + u_int32_t amount; + u_int8_t pad[RECLEN - 4 * sizeof(u_int32_t)]; +} histrec; +#endif /* _TPCB_H_ */ |