summaryrefslogtreecommitdiff
path: root/db/docs/ref/transapp
diff options
context:
space:
mode:
authorjbj <devnull@localhost>2001-05-13 19:58:25 +0000
committerjbj <devnull@localhost>2001-05-13 19:58:25 +0000
commit2af040895b279eca8fb5a44240da7e42c8b4ac66 (patch)
tree1b74621cfd1f4531a39786c4b06a59bc0e92dbf7 /db/docs/ref/transapp
parent9b44f1b7168b70652f7fad2e0da6fdade4b965d7 (diff)
downloadlibrpm-tizen-2af040895b279eca8fb5a44240da7e42c8b4ac66.tar.gz
librpm-tizen-2af040895b279eca8fb5a44240da7e42c8b4ac66.tar.bz2
librpm-tizen-2af040895b279eca8fb5a44240da7e42c8b4ac66.zip
Initial revision
CVS patchset: 4782 CVS date: 2001/05/13 19:58:25
Diffstat (limited to 'db/docs/ref/transapp')
-rw-r--r--db/docs/ref/transapp/nested.html65
-rw-r--r--db/docs/ref/transapp/transapp.cs492
-rw-r--r--db/docs/ref/transapp/tune.html92
-rw-r--r--db/docs/ref/transapp/writetest.cs100
4 files changed, 749 insertions, 0 deletions
diff --git a/db/docs/ref/transapp/nested.html b/db/docs/ref/transapp/nested.html
new file mode 100644
index 000000000..902f57ed3
--- /dev/null
+++ b/db/docs/ref/transapp/nested.html
@@ -0,0 +1,65 @@
+<!--Id: nested.so,v 10.22 2001/03/02 21:01:22 bostic Exp -->
+<!--Copyright 1997-2001 by Sleepycat Software, Inc.-->
+<!--All rights reserved.-->
+<html>
+<head>
+<title>Berkeley DB Reference Guide: Nested transactions</title>
+<meta name="description" content="Berkeley DB: An embedded database programmatic toolkit.">
+<meta name="keywords" content="embedded,database,programmatic,toolkit,b+tree,btree,hash,hashing,transaction,transactions,locking,logging,access method,access methods,java,C,C++">
+</head>
+<body bgcolor=white>
+ <a name="2"><!--meow--></a>
+<table width="100%"><tr valign=top>
+<td><h3><dl><dt>Berkeley DB Reference Guide:<dd>Berkeley DB Transactional Data Store Applications</dl></h3></td>
+<td align=right><a href="../../ref/transapp/cursor.html"><img src="../../images/prev.gif" alt="Prev"></a><a href="../../reftoc.html"><img src="../../images/ref.gif" alt="Ref"></a><a href="../../ref/transapp/admin.html"><img src="../../images/next.gif" alt="Next"></a>
+</td></tr></table>
+<p>
+<h1 align=center>Nested transactions</h1>
+<p>Berkeley DB provides support for nested transactions. Nested transactions
+allow an application to decompose a large or long-running transaction
+into smaller units that may be independently aborted.
+<p>Normally, when beginning a transaction, the application will pass a NULL
+value for the parent argument to <a href="../../api_c/txn_begin.html">txn_begin</a>. If, however, the
+parent argument is a DB_TXN handle, the newly created transaction
+will be treated as a nested transaction within the parent. Transactions
+may nest arbitrarily deeply. For the purposes of this discussion,
+transactions created with a parent identifier will be called
+<i>child transactions</i>.
+<p>Once a transaction becomes a parent, as long as any of its child
+transactions are unresolved (that is, they have neither committed nor
+aborted), the parent may not issue any Berkeley DB calls except to begin more
+child transactions, or to commit or abort. For example, it may not
+issue any access method or cursor calls. After all of a parent's
+children have committed or aborted, the parent may again request
+operations on its own behalf.
+<p>The semantics of nested transactions are as follows. When a child
+transaction is begun, it inherits all the locks of its parent. This
+means that the child will never block waiting on a lock held by its
+parent. Further, locks held by two children of the same parent will
+also conflict. To make this concrete, consider the following set of
+transactions and lock acquisitions.
+<p>Transaction T1 is the parent transaction. It acquires a write lock on
+item A and then begins two child transactions: C1 and C2. C1 also
+wishes to acquire a write lock on A; this succeeds. If C2 attempts to
+acquire a write lock on A, it will block until C1 releases the lock, at
+which point it will succeed. Now, let's say that C1 acquires a write
+lock on B. If C2 now attempts to obtain a lock on B, it will block.
+However, let's now assume that C1 commits. Its locks are
+anti-inherited, which means they are given to T1, so T1 will now hold
+a lock on B. At this point, C2 would be unblocked and would then
+acquire a lock on B.
+<p>Child transactions are entirely subservient to their parent transaction.
+They may abort, undoing their operations regardless of the eventual fate
+of the parent. However, even if a child transaction commits, if its
+parent transaction is eventually aborted, the child's changes are undone
+and the child's transaction is effectively aborted. Any child
+transactions that are not yet resolved when the parent commits or aborts
+are resolved based on the parent's resolution -- committing if the
+parent commits and aborting if the parent aborts. Any child
+transactions that are not yet resolved when the parent prepares are also
+prepared.
+<table width="100%"><tr><td><br></td><td align=right><a href="../../ref/transapp/cursor.html"><img src="../../images/prev.gif" alt="Prev"></a><a href="../../reftoc.html"><img src="../../images/ref.gif" alt="Ref"></a><a href="../../ref/transapp/admin.html"><img src="../../images/next.gif" alt="Next"></a>
+</td></tr></table>
+<p><font size=1><a href="http://www.sleepycat.com">Copyright Sleepycat Software</a></font>
+</body>
+</html>
diff --git a/db/docs/ref/transapp/transapp.cs b/db/docs/ref/transapp/transapp.cs
new file mode 100644
index 000000000..82e4fb42f
--- /dev/null
+++ b/db/docs/ref/transapp/transapp.cs
@@ -0,0 +1,492 @@
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include <errno.h>
+#include <pthread.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <db.h>
+
+#define ENV_DIRECTORY "TXNAPP"
+
+void add_cat(DB_ENV *, DB *, char *, ...);
+void add_color(DB_ENV *, DB *, char *, int);
+void add_fruit(DB_ENV *, DB *, char *, char *);
+void *checkpoint_thread(void *);
+void log_archlist(DB_ENV *);
+void *logfile_thread(void *);
+void db_open(DB_ENV *, DB **, char *, int);
+void env_dir_create(void);
+void env_open(DB_ENV **);
+void usage(void);
+
+int
+main(int argc, char *argv[])
+{
+ extern char *optarg;
+ extern int optind;
+ DB *db_cats, *db_color, *db_fruit;
+ DB_ENV *dbenv;
+ pthread_t ptid;
+ int ch, ret;
+
+ while ((ch = getopt(argc, argv, "")) != EOF)
+ switch (ch) {
+ case '?':
+ default:
+ usage();
+ }
+ argc -= optind;
+ argv += optind;
+
+ env_dir_create();
+ env_open(&dbenv);
+
+ /* Start a checkpoint thread. */
+ if ((ret = pthread_create(
+ &ptid, NULL, checkpoint_thread, (void *)dbenv)) != 0) {
+ fprintf(stderr,
+ "txnapp: failed spawning checkpoint thread: %s\n",
+ strerror(ret));
+ exit (1);
+ }
+
+ /* Start a logfile removal thread. */
+ if ((ret = pthread_create(
+ &ptid, NULL, logfile_thread, (void *)dbenv)) != 0) {
+ fprintf(stderr,
+ "txnapp: failed spawning log file removal thread: %s\n",
+ strerror(ret));
+ exit (1);
+ }
+
+ /* Open database: Key is fruit class; Data is specific type. */
+ db_open(dbenv, &db_fruit, "fruit", 0);
+
+ /* Open database: Key is a color; Data is an integer. */
+ db_open(dbenv, &db_color, "color", 0);
+
+ /*
+ * Open database:
+ * Key is a name; Data is: company name, address, cat breeds.
+ */
+ db_open(dbenv, &db_cats, "cats", 1);
+
+ add_fruit(dbenv, db_fruit, "apple", "yellow delicious");
+
+ add_color(dbenv, db_color, "blue", 0);
+ add_color(dbenv, db_color, "blue", 3);
+
+ add_cat(dbenv, db_cats,
+ "Amy Adams",
+ "Sleepycat Software",
+ "394 E. Riding Dr., Carlisle, MA 01741, USA",
+ "abyssinian",
+ "bengal",
+ "chartreaux",
+ NULL);
+
+ return (0);
+}
+
+void
+env_dir_create()
+{
+ struct stat sb;
+
+ /*
+ * If the directory exists, we're done. We do not further check
+ * the type of the file, DB will fail appropriately if it's the
+ * wrong type.
+ */
+ if (stat(ENV_DIRECTORY, &sb) == 0)
+ return;
+
+ /* Create the directory, read/write/access owner only. */
+ if (mkdir(ENV_DIRECTORY, S_IRWXU) != 0) {
+ fprintf(stderr,
+ "txnapp: mkdir: %s: %s\n", ENV_DIRECTORY, strerror(errno));
+ exit (1);
+ }
+}
+
+void
+env_open(DB_ENV **dbenvp)
+{
+ DB_ENV *dbenv;
+ int ret;
+
+ /* Create the environment handle. */
+ if ((ret = db_env_create(&dbenv, 0)) != 0) {
+ fprintf(stderr,
+ "txnapp: db_env_create: %s\n", db_strerror(ret));
+ exit (1);
+ }
+
+ /* Set up error handling. */
+ dbenv->set_errpfx(dbenv, "txnapp");
+
+ /* Do deadlock detection internally. */
+ if ((ret = dbenv->set_lk_detect(dbenv, DB_LOCK_DEFAULT)) != 0) {
+ dbenv->err(dbenv, ret, "set_lk_detect: DB_LOCK_DEFAULT");
+ exit (1);
+ }
+
+ /*
+ * Open a transactional environment:
+ * create if it doesn't exist
+ * free-threaded handle
+ * run recovery
+ * read/write owner only
+ */
+ if ((ret = dbenv->open(dbenv, ENV_DIRECTORY,
+ DB_CREATE | DB_INIT_LOCK | DB_INIT_LOG |
+ DB_INIT_MPOOL | DB_INIT_TXN | DB_RECOVER | DB_THREAD,
+ S_IRUSR | S_IWUSR)) != 0) {
+ dbenv->err(dbenv, ret, "dbenv->open: %s", ENV_DIRECTORY);
+ exit (1);
+ }
+
+ *dbenvp = dbenv;
+}
+
+void *
+checkpoint_thread(void *arg)
+{
+ DB_ENV *dbenv;
+ int ret;
+
+ dbenv = arg;
+ dbenv->errx(dbenv, "Checkpoint thread: %lu", (u_long)pthread_self());
+
+ /* Checkpoint once a minute. */
+ for (;; sleep(60))
+ switch (ret = txn_checkpoint(dbenv, 0, 0, 0)) {
+ case 0:
+ case DB_INCOMPLETE:
+ break;
+ default:
+ dbenv->err(dbenv, ret, "checkpoint thread");
+ exit (1);
+ }
+
+ /* NOTREACHED */
+}
+
+void *
+logfile_thread(void *arg)
+{
+ DB_ENV *dbenv;
+ int ret;
+ char **begin, **list;
+
+ dbenv = arg;
+ dbenv->errx(dbenv,
+ "Log file removal thread: %lu", (u_long)pthread_self());
+
+ /* Check once every 5 minutes. */
+ for (;; sleep(300)) {
+ /* Get the list of log files. */
+ if ((ret = log_archive(dbenv, &list, DB_ARCH_ABS)) != 0) {
+ dbenv->err(dbenv, ret, "log_archive");
+ exit (1);
+ }
+
+ /* Remove the log files. */
+ if (list != NULL) {
+ for (begin = list; *list != NULL; ++list)
+ if ((ret = remove(*list)) != 0) {
+ dbenv->err(dbenv,
+ ret, "remove %s", *list);
+ exit (1);
+ }
+ free (begin);
+ }
+ }
+ /* NOTREACHED */
+}
+
+void
+log_archlist(DB_ENV *dbenv)
+{
+ int ret;
+ char **begin, **list;
+
+ /* Get the list of database files. */
+ if ((ret = log_archive(dbenv,
+ &list, DB_ARCH_ABS | DB_ARCH_DATA)) != 0) {
+ dbenv->err(dbenv, ret, "log_archive: DB_ARCH_DATA");
+ exit (1);
+ }
+ if (list != NULL) {
+ for (begin = list; *list != NULL; ++list)
+ printf("database file: %s\n", *list);
+ free (begin);
+ }
+
+ /* Get the list of log files. */
+ if ((ret = log_archive(dbenv,
+ &list, DB_ARCH_ABS | DB_ARCH_LOG)) != 0) {
+ dbenv->err(dbenv, ret, "log_archive: DB_ARCH_LOG");
+ exit (1);
+ }
+ if (list != NULL) {
+ for (begin = list; *list != NULL; ++list)
+ printf("log file: %s\n", *list);
+ free (begin);
+ }
+}
+
+void
+db_open(DB_ENV *dbenv, DB **dbp, char *name, int dups)
+{
+ DB *db;
+ int ret;
+
+ /* Create the database handle. */
+ if ((ret = db_create(&db, dbenv, 0)) != 0) {
+ dbenv->err(dbenv, ret, "db_create");
+ exit (1);
+ }
+
+ /* Optionally, turn on duplicate data items. */
+ if (dups && (ret = db->set_flags(db, DB_DUP)) != 0) {
+ dbenv->err(dbenv, ret, "db->set_flags: DB_DUP");
+ exit (1);
+ }
+
+ /*
+ * Open a database in the environment:
+ * create if it doesn't exist
+ * free-threaded handle
+ * read/write owner only
+ */
+ if ((ret = db->open(db, name, NULL,
+ DB_BTREE, DB_CREATE | DB_THREAD, S_IRUSR | S_IWUSR)) != 0) {
+ dbenv->err(dbenv, ret, "db->open: %s", name);
+ exit (1);
+ }
+
+ *dbp = db;
+}
+
+void
+add_fruit(DB_ENV *dbenv, DB *db, char *fruit, char *name)
+{
+ DBT key, data;
+ DB_TXN *tid;
+ int ret;
+
+ /* Initialization. */
+ memset(&key, 0, sizeof(key));
+ memset(&data, 0, sizeof(data));
+ key.data = fruit;
+ key.size = strlen(fruit);
+ data.data = name;
+ data.size = strlen(name);
+
+ for (;;) {
+ /* Begin the transaction. */
+ if ((ret = txn_begin(dbenv, NULL, &tid, 0)) != 0) {
+ dbenv->err(dbenv, ret, "txn_begin");
+ exit (1);
+ }
+
+ /* Store the value. */
+ switch (ret = db->put(db, tid, &key, &data, 0)) {
+ case 0:
+ /* Success: commit the change. */
+ if ((ret = txn_commit(tid, 0)) != 0) {
+ dbenv->err(dbenv, ret, "txn_commit");
+ exit (1);
+ }
+ return;
+ case DB_LOCK_DEADLOCK:
+ /* Deadlock: retry the operation. */
+ if ((ret = txn_abort(tid)) != 0) {
+ dbenv->err(dbenv, ret, "txn_abort");
+ exit (1);
+ }
+ break;
+ default:
+ /* Error: run recovery. */
+ dbenv->err(dbenv, ret, "dbc->put: %s/%s", fruit, name);
+ exit (1);
+ }
+ }
+}
+
+void
+add_color(DB_ENV *dbenv, DB *dbp, char *color, int increment)
+{
+ DBT key, data;
+ DB_TXN *tid;
+ int original, ret;
+ char buf[64];
+
+ /* Initialization. */
+ memset(&key, 0, sizeof(key));
+ key.data = color;
+ key.size = strlen(color);
+ memset(&data, 0, sizeof(data));
+ data.flags = DB_DBT_MALLOC;
+
+ for (;;) {
+ /* Begin the transaction. */
+ if ((ret = txn_begin(dbenv, NULL, &tid, 0)) != 0) {
+ dbenv->err(dbenv, ret, "txn_begin");
+ exit (1);
+ }
+
+ /*
+ * Get the key. If it exists, we increment the value. If it
+ * doesn't exist, we create it.
+ */
+ switch (ret = dbp->get(dbp, tid, &key, &data, 0)) {
+ case 0:
+ original = atoi(data.data);
+ break;
+ case DB_LOCK_DEADLOCK:
+ /* Deadlock: retry the operation. */
+ if ((ret = txn_abort(tid)) != 0) {
+ dbenv->err(dbenv, ret, "txn_abort");
+ exit (1);
+ }
+ continue;
+ case DB_NOTFOUND:
+ original = 0;
+ break;
+ default:
+ /* Error: run recovery. */
+ dbenv->err(
+ dbenv, ret, "dbc->get: %s/%d", color, increment);
+ exit (1);
+ }
+ if (data.data != NULL)
+ free(data.data);
+
+ /* Create the new data item. */
+ (void)snprintf(buf, sizeof(buf), "%d", original + increment);
+ data.data = buf;
+ data.size = strlen(buf) + 1;
+
+ /* Store the new value. */
+ switch (ret = dbp->put(dbp, tid, &key, &data, 0)) {
+ case 0:
+ /* Success: commit the change. */
+ if ((ret = txn_commit(tid, 0)) != 0) {
+ dbenv->err(dbenv, ret, "txn_commit");
+ exit (1);
+ }
+ return;
+ case DB_LOCK_DEADLOCK:
+ /* Deadlock: retry the operation. */
+ if ((ret = txn_abort(tid)) != 0) {
+ dbenv->err(dbenv, ret, "txn_abort");
+ exit (1);
+ }
+ break;
+ default:
+ /* Error: run recovery. */
+ dbenv->err(
+ dbenv, ret, "dbc->put: %s/%d", color, increment);
+ exit (1);
+ }
+ }
+}
+
+void
+add_cat(DB_ENV *dbenv, DB *db, char *name, ...)
+{
+ va_list ap;
+ DBC *dbc;
+ DBT key, data;
+ DB_TXN *tid;
+ int ret;
+ char *s;
+
+ /* Initialization. */
+ memset(&key, 0, sizeof(key));
+ memset(&data, 0, sizeof(data));
+ key.data = name;
+ key.size = strlen(name);
+
+retry: /* Begin the transaction. */
+ if ((ret = txn_begin(dbenv, NULL, &tid, 0)) != 0) {
+ dbenv->err(dbenv, ret, "txn_begin");
+ exit (1);
+ }
+
+ /* Delete any previously existing item. */
+ switch (ret = db->del(db, tid, &key, 0)) {
+ case 0:
+ case DB_NOTFOUND:
+ break;
+ case DB_LOCK_DEADLOCK:
+ /* Deadlock: retry the operation. */
+ if ((ret = txn_abort(tid)) != 0) {
+ dbenv->err(dbenv, ret, "txn_abort");
+ exit (1);
+ }
+ goto retry;
+ default:
+ dbenv->err(dbenv, ret, "db->del: %s", name);
+ exit (1);
+ }
+
+ /* Create a cursor. */
+ if ((ret = db->cursor(db, tid, &dbc, 0)) != 0) {
+ dbenv->err(dbenv, ret, "db->cursor");
+ exit (1);
+ }
+
+ /* Append the items, in order. */
+ va_start(ap, name);
+ while ((s = va_arg(ap, char *)) != NULL) {
+ data.data = s;
+ data.size = strlen(s);
+ switch (ret = dbc->c_put(dbc, &key, &data, DB_KEYLAST)) {
+ case 0:
+ break;
+ case DB_LOCK_DEADLOCK:
+ va_end(ap);
+
+ /* Deadlock: retry the operation. */
+ if ((ret = dbc->c_close(dbc)) != 0) {
+ dbenv->err(
+ dbenv, ret, "dbc->c_close");
+ exit (1);
+ }
+ if ((ret = txn_abort(tid)) != 0) {
+ dbenv->err(dbenv, ret, "txn_abort");
+ exit (1);
+ }
+ goto retry;
+ default:
+ /* Error: run recovery. */
+ dbenv->err(dbenv, ret, "dbc->put: %s/%s", name, s);
+ exit (1);
+ }
+ }
+ va_end(ap);
+
+ /* Success: commit the change. */
+ if ((ret = dbc->c_close(dbc)) != 0) {
+ dbenv->err(dbenv, ret, "dbc->c_close");
+ exit (1);
+ }
+ if ((ret = txn_commit(tid, 0)) != 0) {
+ dbenv->err(dbenv, ret, "txn_commit");
+ exit (1);
+ }
+}
+
+void
+usage()
+{
+ (void)fprintf(stderr, "usage: txnapp\n");
+ exit(1);
+}
diff --git a/db/docs/ref/transapp/tune.html b/db/docs/ref/transapp/tune.html
new file mode 100644
index 000000000..a8bf1b66c
--- /dev/null
+++ b/db/docs/ref/transapp/tune.html
@@ -0,0 +1,92 @@
+<!--Id: tune.so,v 11.8 2001/04/04 22:32:29 bostic Exp -->
+<!--Copyright 1997-2001 by Sleepycat Software, Inc.-->
+<!--All rights reserved.-->
+<html>
+<head>
+<title>Berkeley DB Reference Guide: Transaction tuning</title>
+<meta name="description" content="Berkeley DB: An embedded database programmatic toolkit.">
+<meta name="keywords" content="embedded,database,programmatic,toolkit,b+tree,btree,hash,hashing,transaction,transactions,locking,logging,access method,access methods,java,C,C++">
+</head>
+<body bgcolor=white>
+ <a name="2"><!--meow--></a> <a name="3"><!--meow--></a>
+<table width="100%"><tr valign=top>
+<td><h3><dl><dt>Berkeley DB Reference Guide:<dd>Berkeley DB Transactional Data Store Applications</dl></h3></td>
+<td align=right><a href="../../ref/transapp/reclimit.html"><img src="../../images/prev.gif" alt="Prev"></a><a href="../../reftoc.html"><img src="../../images/ref.gif" alt="Ref"></a><a href="../../ref/transapp/throughput.html"><img src="../../images/next.gif" alt="Next"></a>
+</td></tr></table>
+<p>
+<h1 align=center>Transaction tuning</h1>
+<p>There are a few different issues to consider when tuning the performance
+of Berkeley DB transactional applications. First, you should review
+<a href="../../ref/am_misc/tune.html">Access method tuning</a>, as the
+tuning issues for access method applications are applicable to
+transactional applications as well. The following are additional tuning
+issues for Berkeley DB transactional applications:
+<p><dl compact>
+<p><dt>access method<dd>Highly concurrent applications should use the Queue access method, where
+possible, as it provides finer-granularity of locking than the other
+access methods. Otherwise, applications usually see better concurrency
+when using the Btree access method than when using either the Hash or
+Recno access methods.
+<p><dt>record numbers<dd>Using record numbers outside of the Queue access method will often slow
+down concurrent applications as they limit the degree of concurrency
+available in the database.
+Using the Recno access method, or the Btree access
+method with retrieval by record number configured can slow applications
+down.
+<p><dt>Btree database size<dd>When using the Btree access method, applications supporting concurrent
+access may see excessive numbers of deadlocks in small databases. There
+are two different approaches to resolving this problem. First, as the
+Btree access method uses page-level locking, decreasing the database
+page size can result in fewer lock conflicts. Second, in the case of
+databases that are cyclically growing and shrinking, turning off reverse
+splits can leave the database with enough pages that there will be fewer
+lock conflicts.
+<p><dt>transactionally protected read operations<dd>Most applications do not need repeatable reads. Performing all read
+operations outside of transactions can often significantly increase
+application throughput. In addition, limiting the lifetime of
+non-transactional cursors will reduce the length of times locks are
+held, thereby improving concurrency.
+<p><dt><a href="../../api_c/db_open.html#DB_DIRTY_READ">DB_DIRTY_READ</a><dd>Consider using the <a href="../../api_c/db_open.html#DB_DIRTY_READ">DB_DIRTY_READ</a> flag for transactions, cursors
+or individual read operations. This flag allows read operations to
+potentially return data which has been modified but not yet committed,
+and can significantly increase application throughput in applications
+that do not require data be guaranteed to be permanent in the database.
+<p><dt><a href="../../api_c/dbc_get.html#DB_RMW">DB_RMW</a><dd>Consider using the <a href="../../api_c/dbc_get.html#DB_RMW">DB_RMW</a> flag to immediate acquire write locks
+when reading data items that will subsequently be modified. Although
+this flag may increase contention (because write locks are held longer
+than they would otherwise be), it may decrease the number of deadlocks
+that occur.
+<p><dt><a href="../../api_c/env_open.html#DB_TXN_NOSYNC">DB_TXN_NOSYNC</a><dd>By default, transactional commit in Berkeley DB implies durability, that is,
+all committed operations will be present in the database after
+recovery from any application or system failure. For applications not
+requiring that level of certainty, specifying the <a href="../../api_c/env_open.html#DB_TXN_NOSYNC">DB_TXN_NOSYNC</a>
+flag will often provide a significant performance improvement. In this
+case, the database will still be fully recoverable, but some number of
+committed transactions might be lost after system failure.
+<p><dt>large key/data items<dd>Transactional protections in Berkeley DB are guaranteed by before and after
+physical image logging. This means applications modifying large
+key/data items also write large log records, and, in the case of the
+default transaction commit, threads of control must wait until those
+log records have been flushed to disk. Applications supporting
+concurrent access should try and keep key/data items small wherever
+possible.
+<p><dt>log buffer size<dd>Berkeley DB internally maintains a buffer of log writes. The buffer is
+written to disk at transaction commit, by default, or, whenever it
+is filled. If it is consistently being filled before transaction
+commit, it will be written multiple times per transaction, costing
+application performance. In these cases, increasing the size of the
+log buffer can increase application throughput.
+<p><dt>trickle write<dd>In some applications, the cache is sufficiently active and dirty that
+readers frequently need to write a dirty page in order to have space in
+which to read a new page from the backing database file. You can use
+the <a href="../../utility/db_stat.html">db_stat</a> utility (or the statistics returned by the
+<a href="../../api_c/memp_stat.html">memp_stat</a> function) to see how often this is happening in your
+application's cache. In this case, using a separate thread of control
+and the <a href="../../api_c/memp_trickle.html">memp_trickle</a> interface to trickle-write pages can often
+increase the overall throughput of the application.
+</dl>
+<table width="100%"><tr><td><br></td><td align=right><a href="../../ref/transapp/reclimit.html"><img src="../../images/prev.gif" alt="Prev"></a><a href="../../reftoc.html"><img src="../../images/ref.gif" alt="Ref"></a><a href="../../ref/transapp/throughput.html"><img src="../../images/next.gif" alt="Next"></a>
+</td></tr></table>
+<p><font size=1><a href="http://www.sleepycat.com">Copyright Sleepycat Software</a></font>
+</body>
+</html>
diff --git a/db/docs/ref/transapp/writetest.cs b/db/docs/ref/transapp/writetest.cs
new file mode 100644
index 000000000..bde092cb6
--- /dev/null
+++ b/db/docs/ref/transapp/writetest.cs
@@ -0,0 +1,100 @@
+/*
+ * writetest --
+ *
+ * Id: writetest.cs,v 10.3 1999/11/19 17:21:06 bostic Exp
+ */
+#include <sys/types.h>
+
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+
+void usage __P((void));
+
+int
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ struct timeval start_time, end_time;
+ long usecs;
+ int bytes, ch, cnt, fd, ops;
+ char *fname, buf[100 * 1024];
+
+ bytes = 256;
+ fname = "testfile";
+ ops = 1000;
+ while ((ch = getopt(argc, argv, "b:f:o:")) != EOF)
+ switch (ch) {
+ case 'b':
+ if ((bytes = atoi(optarg)) > sizeof(buf)) {
+ fprintf(stderr,
+ "max -b option %d\n", sizeof(buf));
+ exit (1);
+ }
+ break;
+ case 'f':
+ fname = optarg;
+ break;
+ case 'o':
+ if ((ops = atoi(optarg)) <= 0) {
+ fprintf(stderr, "illegal -o option value\n");
+ exit (1);
+ }
+ break;
+ case '?':
+ default:
+ usage();
+ }
+ argc -= optind;
+ argv += optind;
+
+ (void)unlink(fname);
+ if ((fd = open(fname, O_RDWR | O_CREAT, 0666)) == -1) {
+ perror(fname);
+ exit (1);
+ }
+
+ memset(buf, 0, bytes);
+
+ printf("running: %d ops\n", ops);
+
+ (void)gettimeofday(&start_time, NULL);
+ for (cnt = 0; cnt < ops; ++cnt) {
+ if (write(fd, buf, bytes) != bytes) {
+ fprintf(stderr, "write: %s\n", strerror(errno));
+ exit (1);
+ }
+ if (lseek(fd, (off_t)0, SEEK_SET) == -1) {
+ fprintf(stderr, "lseek: %s\n", strerror(errno));
+ exit (1);
+ }
+ if (fsync(fd) != 0) {
+ fprintf(stderr, "fsync: %s\n", strerror(errno));
+ exit (1);
+ }
+ }
+ (void)gettimeofday(&end_time, NULL);
+
+ usecs = (end_time.tv_sec - start_time.tv_sec) * 1000000 +
+ end_time.tv_usec - start_time.tv_usec;
+ printf("Elapsed time: %ld.%06ld seconds\n",
+ usecs / 1000000, usecs % 1000000);
+ printf("%d ops: %7.2f ops per second\n",
+ ops, (float)1000000 * ops/usecs);
+
+ (void)unlink(fname);
+ exit (0);
+}
+
+void
+usage()
+{
+ (void)fprintf(stderr,
+ "usage: testfile [-b bytes] [-f file] [-o ops]\n");
+ exit(1);
+}