mirror of
https://github.com/berkeleydb/libdb.git
synced 2024-11-16 17:16:25 +00:00
621 lines
23 KiB
HTML
621 lines
23 KiB
HTML
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
|||
|
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
|||
|
<html xmlns="http://www.w3.org/1999/xhtml">
|
|||
|
<head>
|
|||
|
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
|
|||
|
<title>In-Memory Transaction Example</title>
|
|||
|
<link rel="stylesheet" href="gettingStarted.css" type="text/css" />
|
|||
|
<meta name="generator" content="DocBook XSL Stylesheets V1.73.2" />
|
|||
|
<link rel="start" href="index.html" title="Getting Started with Berkeley DB Transaction Processing" />
|
|||
|
<link rel="up" href="wrapup.html" title="Chapter 6. Summary and Examples" />
|
|||
|
<link rel="prev" href="txnexample_c.html" title="Transaction Example" />
|
|||
|
</head>
|
|||
|
<body>
|
|||
|
<div xmlns="" class="navheader">
|
|||
|
<div class="libver">
|
|||
|
<p>Library Version 11.2.5.2</p>
|
|||
|
</div>
|
|||
|
<table width="100%" summary="Navigation header">
|
|||
|
<tr>
|
|||
|
<th colspan="3" align="center">In-Memory Transaction Example</th>
|
|||
|
</tr>
|
|||
|
<tr>
|
|||
|
<td width="20%" align="left"><a accesskey="p" href="txnexample_c.html">Prev</a> </td>
|
|||
|
<th width="60%" align="center">Chapter 6. Summary and Examples</th>
|
|||
|
<td width="20%" align="right"> </td>
|
|||
|
</tr>
|
|||
|
</table>
|
|||
|
<hr />
|
|||
|
</div>
|
|||
|
<div class="sect1" lang="en" xml:lang="en">
|
|||
|
<div class="titlepage">
|
|||
|
<div>
|
|||
|
<div>
|
|||
|
<h2 class="title" style="clear: both"><a id="inmem_txnexample_c"></a>In-Memory Transaction Example</h2>
|
|||
|
</div>
|
|||
|
</div>
|
|||
|
</div>
|
|||
|
<p>
|
|||
|
DB is sometimes used for applications that simply need to cache
|
|||
|
data retrieved from some other location (such as a remote database
|
|||
|
server). DB is also often used in embedded systems.
|
|||
|
</p>
|
|||
|
<p>
|
|||
|
In both cases, applications may want to use transactions for
|
|||
|
atomicity, consistency, and isolation guarantees, but they may also want
|
|||
|
to forgo the durability guarantee entirely. In doing so, they can keep
|
|||
|
their DB environment and databases entirely in-memory so
|
|||
|
as to avoid the performance impact of unneeded disk I/O.
|
|||
|
</p>
|
|||
|
<p>
|
|||
|
To do this:
|
|||
|
</p>
|
|||
|
<div class="itemizedlist">
|
|||
|
<ul type="disc">
|
|||
|
<li>
|
|||
|
<p>
|
|||
|
Refrain from specifying a home directory when you open your
|
|||
|
environment. The exception to this is if you are using the
|
|||
|
<code class="literal">DB_CONFIG</code> configuration file — in
|
|||
|
that case you must identify the environment's home
|
|||
|
directory so that the configuration file can be found.
|
|||
|
</p>
|
|||
|
</li>
|
|||
|
<li>
|
|||
|
<p>
|
|||
|
Configure your environment to back your regions from
|
|||
|
system memory instead of the filesystem.
|
|||
|
</p>
|
|||
|
</li>
|
|||
|
<li>
|
|||
|
<p>
|
|||
|
Configure your logging subsystem such that log files are kept
|
|||
|
entirely in-memory.
|
|||
|
</p>
|
|||
|
</li>
|
|||
|
<li>
|
|||
|
<p>
|
|||
|
Increase the size of your in-memory log buffer so that it
|
|||
|
is large enough to hold the largest set of concurrent write operations.
|
|||
|
</p>
|
|||
|
</li>
|
|||
|
<li>
|
|||
|
<p>
|
|||
|
Increase the size of your in-memory cache so that it can
|
|||
|
hold your entire data set. You do not want your cache to
|
|||
|
page to disk.
|
|||
|
</p>
|
|||
|
</li>
|
|||
|
<li>
|
|||
|
<p>
|
|||
|
Do not specify a file name when you open your database(s).
|
|||
|
</p>
|
|||
|
</li>
|
|||
|
</ul>
|
|||
|
</div>
|
|||
|
<p>
|
|||
|
As an example, this section takes the transaction example provided
|
|||
|
in <a class="xref" href="txnexample_c.html" title="Transaction Example">Transaction Example</a>
|
|||
|
and it updates that example so that the environment, database, log
|
|||
|
files, and regions are all kept entirely in-memory.
|
|||
|
</p>
|
|||
|
<p>
|
|||
|
For illustration purposes, we also modify this example so that
|
|||
|
uncommitted reads are no longer used to enable the
|
|||
|
<code class="function">count_records()</code>
|
|||
|
|
|||
|
function. Instead, we simply provide a transaction handle to
|
|||
|
<code class="function">count_records()</code>
|
|||
|
|
|||
|
so as to avoid the self-deadlock. Be aware that using a transaction handle here rather than
|
|||
|
uncommitted reads will work just as well as if we had continued to use uncommitted reads. However,
|
|||
|
the usage of the transaction handle here will
|
|||
|
probably cause more deadlocks than using read-uncommitted does, because more locking is being performed in
|
|||
|
this case.
|
|||
|
</p>
|
|||
|
<p>
|
|||
|
To begin, we simplify the beginning of our example a bit. Because
|
|||
|
we no longer need an environment home directory, we can remove all
|
|||
|
the code that we used to determine path delimiters
|
|||
|
and include the <code class="function">getopt</code> function. We can also
|
|||
|
remove our <code class="function">usage()</code> function because we no
|
|||
|
longer require any command line arguments.
|
|||
|
</p>
|
|||
|
<pre class="programlisting">/* File: txn_guide_inmemory.c */
|
|||
|
|
|||
|
/* We assume an ANSI-compatible compiler */
|
|||
|
#include <stdio.h>
|
|||
|
#include <stdlib.h>
|
|||
|
#include <string.h>
|
|||
|
#include <pthread.h>
|
|||
|
#include <db.h>
|
|||
|
|
|||
|
/* Run 5 writers threads at a time. */
|
|||
|
#define NUMWRITERS 5
|
|||
|
|
|||
|
/*
|
|||
|
* Printing of pthread_t is implementation-specific, so we
|
|||
|
* create our own thread IDs for reporting purposes.
|
|||
|
*/
|
|||
|
int global_thread_num;
|
|||
|
pthread_mutex_t thread_num_lock;
|
|||
|
|
|||
|
/* Forward declarations */
|
|||
|
int count_records(DB *, DB_TXN *);
|
|||
|
int open_db(DB **, const char *, const char *, DB_ENV *, u_int32_t);
|
|||
|
int writer_thread(void *); </pre>
|
|||
|
<p>
|
|||
|
Next, in our <code class="function">main()</code>, we also eliminate some
|
|||
|
variables that this example no longer needs. In particular, we are able to remove
|
|||
|
the
|
|||
|
<code class="literal">db_home_dir</code>
|
|||
|
|
|||
|
and
|
|||
|
<code class="literal">file_name</code>
|
|||
|
|
|||
|
variables. We also remove all our <code class="function">getopt</code> code.
|
|||
|
</p>
|
|||
|
<pre class="programlisting">int
|
|||
|
main(void)
|
|||
|
{
|
|||
|
/* Initialize our handles */
|
|||
|
DB *dbp = NULL;
|
|||
|
DB_ENV *envp = NULL;
|
|||
|
|
|||
|
pthread_t writer_threads[NUMWRITERS];
|
|||
|
int i, ret, ret_t;
|
|||
|
u_int32_t env_flags;
|
|||
|
|
|||
|
/* Application name */
|
|||
|
const char *prog_name = "txn_guide_inmemory"; </pre>
|
|||
|
<p>
|
|||
|
Next we create our environment as always. However, we add
|
|||
|
<code class="literal">DB_PRIVATE</code> to our environment open flags. This
|
|||
|
flag causes our environment to back regions using our
|
|||
|
application's heap memory rather than by using the filesystem.
|
|||
|
This is the first important step to keeping our DB data
|
|||
|
entirely in-memory.
|
|||
|
</p>
|
|||
|
<p>
|
|||
|
We also remove the <code class="literal">DB_RECOVER</code> flag from the environment open flags. Because our databases,
|
|||
|
logs, and regions are maintained in-memory, there will never be anything to recover.
|
|||
|
</p>
|
|||
|
<p>
|
|||
|
Note that we show the additional code here in
|
|||
|
<strong class="userinput"><code>bold.</code></strong>
|
|||
|
</p>
|
|||
|
<pre class="programlisting"> /* Create the environment */
|
|||
|
ret = db_env_create(&envp, 0);
|
|||
|
if (ret != 0) {
|
|||
|
fprintf(stderr, "Error creating environment handle: %s\n",
|
|||
|
db_strerror(ret));
|
|||
|
goto err;
|
|||
|
}
|
|||
|
|
|||
|
env_flags =
|
|||
|
DB_CREATE | /* Create the environment if it does not exist */
|
|||
|
DB_INIT_LOCK | /* Initialize the locking subsystem */
|
|||
|
DB_INIT_LOG | /* Initialize the logging subsystem */
|
|||
|
DB_INIT_TXN | /* Initialize the transactional subsystem. This
|
|||
|
* also turns on logging. */
|
|||
|
DB_INIT_MPOOL | /* Initialize the memory pool (in-memory cache) */
|
|||
|
<strong class="userinput"><code>DB_PRIVATE | /* Region files are not backed by the filesystem.
|
|||
|
* Instead, they are backed by heap memory. */</code></strong>
|
|||
|
DB_THREAD; /* Cause the environment to be free-threaded */ </pre>
|
|||
|
<p>
|
|||
|
Now we configure our environment to keep the log files in memory,
|
|||
|
increase the log buffer size to 10 MB, and increase our in-memory
|
|||
|
cache to 10 MB. These values should be more than enough for our
|
|||
|
application's workload.
|
|||
|
</p>
|
|||
|
<pre class="programlisting">
|
|||
|
<strong class="userinput">
|
|||
|
<code> /* Specify in-memory logging */
|
|||
|
ret = envp->log_set_config(envp, DB_LOG_IN_MEMORY, 1);
|
|||
|
if (ret != 0) {
|
|||
|
fprintf(stderr, "Error setting log subsystem to in-memory: %s\n",
|
|||
|
db_strerror(ret));
|
|||
|
goto err;
|
|||
|
}
|
|||
|
|
|||
|
/*
|
|||
|
* Specify the size of the in-memory log buffer.
|
|||
|
*/
|
|||
|
ret = envp->set_lg_bsize(envp, 10 * 1024 * 1024);
|
|||
|
if (ret != 0) {
|
|||
|
fprintf(stderr, "Error increasing the log buffer size: %s\n",
|
|||
|
db_strerror(ret));
|
|||
|
goto err;
|
|||
|
}
|
|||
|
|
|||
|
/*
|
|||
|
* Specify the size of the in-memory cache.
|
|||
|
*/
|
|||
|
ret = envp->set_cachesize(envp, 0,
|
|||
|
10 * 1024 * 1024, 1);
|
|||
|
if (ret != 0) {
|
|||
|
fprintf(stderr, "Error increasing the cache size: %s\n",
|
|||
|
db_strerror(ret));
|
|||
|
goto err;
|
|||
|
}</code>
|
|||
|
</strong>
|
|||
|
</pre>
|
|||
|
<p>
|
|||
|
Next, we open the environment and setup our lock detection. This is
|
|||
|
identical to how the example previously worked, except that we do not
|
|||
|
provide a location for the environment's home directory.
|
|||
|
</p>
|
|||
|
<pre class="programlisting"> /*
|
|||
|
* Indicate that we want db to perform lock detection internally.
|
|||
|
* Also indicate that the transaction with the fewest number of
|
|||
|
* write locks will receive the deadlock notification in
|
|||
|
* the event of a deadlock.
|
|||
|
*/
|
|||
|
ret = envp->set_lk_detect(envp, DB_LOCK_MINWRITE);
|
|||
|
if (ret != 0) {
|
|||
|
fprintf(stderr, "Error setting lock detect: %s\n",
|
|||
|
db_strerror(ret));
|
|||
|
goto err;
|
|||
|
}
|
|||
|
|
|||
|
/* Now actually open the environment */
|
|||
|
ret = envp->open(envp, <strong class="userinput"><code>NULL</code></strong>, env_flags, 0);
|
|||
|
if (ret != 0) {
|
|||
|
fprintf(stderr, "Error opening environment: %s\n",
|
|||
|
db_strerror(ret));
|
|||
|
goto err;
|
|||
|
} </pre>
|
|||
|
<p>
|
|||
|
When we call
|
|||
|
<span><code class="function">open_db()</code>,</span>
|
|||
|
|
|||
|
which is what we use
|
|||
|
to open our database, we no not provide a database filename for the
|
|||
|
third parameter. When the filename is <code class="literal">NULL</code>, the database is not
|
|||
|
backed by the filesystem.
|
|||
|
</p>
|
|||
|
<pre class="programlisting"> /*
|
|||
|
* If we had utility threads (for running checkpoints or
|
|||
|
* deadlock detection, for example) we would spawn those
|
|||
|
* here. However, for a simple example such as this,
|
|||
|
* that is not required.
|
|||
|
*/
|
|||
|
|
|||
|
/* Open the database */
|
|||
|
ret = open_db(&dbp, prog_name, <strong class="userinput"><code>NULL</code></strong>,
|
|||
|
envp, DB_DUPSORT);
|
|||
|
if (ret != 0)
|
|||
|
goto err; </pre>
|
|||
|
<p>
|
|||
|
After that, our <code class="function">main()</code> function is unchanged,
|
|||
|
except that when we
|
|||
|
<span>close the database,</span>
|
|||
|
|
|||
|
we change the error message string so as to not reference the database filename.
|
|||
|
</p>
|
|||
|
<pre class="programlisting"> /* Initialize a pthread mutex. Used to help provide thread ids. */
|
|||
|
(void)pthread_mutex_init(&thread_num_lock, NULL);
|
|||
|
|
|||
|
/* Start the writer threads. */
|
|||
|
for (i = 0; i < NUMWRITERS; i++)
|
|||
|
(void)pthread_create(
|
|||
|
&writer_threads[i], NULL, (void *)writer_thread, (void *)dbp);
|
|||
|
|
|||
|
/* Join the writers */
|
|||
|
for (i = 0; i < NUMWRITERS; i++)
|
|||
|
(void)pthread_join(writer_threads[i], NULL);
|
|||
|
|
|||
|
err:
|
|||
|
/* Close our database handle, if it was opened. */
|
|||
|
if (dbp != NULL) {
|
|||
|
ret_t = dbp->close(dbp, 0);
|
|||
|
if (ret_t != 0) {
|
|||
|
<strong class="userinput"><code>fprintf(stderr, "%s database close failed.\n",
|
|||
|
db_strerror(ret_t));</code></strong>
|
|||
|
ret = ret_t;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
/* Close our environment, if it was opened. */
|
|||
|
if (envp != NULL) {
|
|||
|
ret_t = envp->close(envp, 0);
|
|||
|
if (ret_t != 0) {
|
|||
|
fprintf(stderr, "environment close failed: %s\n",
|
|||
|
db_strerror(ret_t));
|
|||
|
ret = ret_t;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
/* Final status message and return. */
|
|||
|
printf("I'm all done.\n");
|
|||
|
return (ret == 0 ? EXIT_SUCCESS : EXIT_FAILURE);
|
|||
|
} </pre>
|
|||
|
<p>
|
|||
|
That completes <code class="function">main()</code>. The bulk of our
|
|||
|
<code class="function">writer_thread()</code> function implementation is
|
|||
|
unchanged from the initial transaction example, except that we no
|
|||
|
longer check for <code class="literal">DB_KEYEXISTS</code> in our
|
|||
|
<code class="methodname">DB->put()</code> return code. Because we are
|
|||
|
configuring for a completely in-memory database, there is no
|
|||
|
possibility that we can run this code against an existing database.
|
|||
|
Therefore, there is no way that <code class="literal">DB_KEYEXISTS</code>
|
|||
|
will be returned by <code class="methodname">DB->put()</code>.
|
|||
|
</p>
|
|||
|
<pre class="programlisting">/*
|
|||
|
* A function that performs a series of writes to a
|
|||
|
* Berkeley DB database. The information written
|
|||
|
* to the database is largely nonsensical, but the
|
|||
|
* mechanism of transactional commit/abort and
|
|||
|
* deadlock detection is illustrated here.
|
|||
|
*/
|
|||
|
int
|
|||
|
writer_thread(void *args)
|
|||
|
{
|
|||
|
DBT key, value;
|
|||
|
DB_TXN *txn;
|
|||
|
int i, j, payload, ret, thread_num;
|
|||
|
int retry_count, max_retries = 20; /* Max retry on a deadlock */
|
|||
|
char *key_strings[] = {"key 1", "key 2", "key 3", "key 4",
|
|||
|
"key 5", "key 6", "key 7", "key 8",
|
|||
|
"key 9", "key 10"};
|
|||
|
|
|||
|
DB *dbp = (DB *)args;
|
|||
|
DbEnv *envp = dbp-&gt;get_env();
|
|||
|
|
|||
|
/* Get the thread number */
|
|||
|
(void)pthread_mutex_lock(&thread_num_lock);
|
|||
|
global_thread_num++;
|
|||
|
thread_num = global_thread_num;
|
|||
|
(void)pthread_mutex_unlock(&thread_num_lock);
|
|||
|
|
|||
|
/* Initialize the random number generator */
|
|||
|
srand((u_int)pthread_self());
|
|||
|
|
|||
|
/* Write 50 times and then quit */
|
|||
|
for (i = 0; i < 50; i++) {
|
|||
|
retry_count = 0; /* Used for deadlock retries */
|
|||
|
|
|||
|
retry:
|
|||
|
ret = envp->txn_begin(envp, NULL, &txn, 0);
|
|||
|
if (ret != 0) {
|
|||
|
envp->err(envp, ret, "txn_begin failed");
|
|||
|
return (EXIT_FAILURE);
|
|||
|
}
|
|||
|
for (j = 0; j < 10; j++) {
|
|||
|
/* Set up our key and values DBTs */
|
|||
|
memset(&key, 0, sizeof(DBT));
|
|||
|
key.data = key_strings[j];
|
|||
|
key.size = (strlen(key_strings[j]) + 1) * sizeof(char);
|
|||
|
|
|||
|
memset(&value, 0, sizeof(DBT));
|
|||
|
payload = rand() + i;
|
|||
|
value.data = &payload;
|
|||
|
value.size = sizeof(int);
|
|||
|
|
|||
|
/* Perform the database put. */
|
|||
|
switch (ret = dbp->put(dbp, txn, &key, &value, 0)) {
|
|||
|
case 0:
|
|||
|
break;
|
|||
|
|
|||
|
/*
|
|||
|
* Here's where we perform deadlock detection. If
|
|||
|
* DB_LOCK_DEADLOCK is returned by the put operation,
|
|||
|
* then this thread has been chosen to break a deadlock.
|
|||
|
* It must abort its operation, and optionally retry the
|
|||
|
* put.
|
|||
|
*/
|
|||
|
case DB_LOCK_DEADLOCK:
|
|||
|
/*
|
|||
|
* First that we MUST do is abort the
|
|||
|
* transaction.
|
|||
|
*/
|
|||
|
(void)txn->abort(txn);
|
|||
|
/*
|
|||
|
* Now we decide if we want to retry the operation.
|
|||
|
* If we have retried less than max_retries,
|
|||
|
* increment the retry count and goto retry.
|
|||
|
*/
|
|||
|
if (retry_count < max_retries) {
|
|||
|
printf("Writer %i: Got DB_LOCK_DEADLOCK.\n",
|
|||
|
thread_num);
|
|||
|
printf("Writer %i: Retrying write operation.\n",
|
|||
|
thread_num);
|
|||
|
retry_count++;
|
|||
|
goto retry;
|
|||
|
}
|
|||
|
/*
|
|||
|
* Otherwise, just give up.
|
|||
|
*/
|
|||
|
printf("Writer %i: ", thread_num);
|
|||
|
printf("Got DB_LOCK_DEADLOCK and out of retries.\n");
|
|||
|
printf("Writer %i: Giving up.\n", thread_num);
|
|||
|
return (EXIT_FAILURE);
|
|||
|
/*
|
|||
|
* If a generic error occurs, we simply abort the
|
|||
|
* transaction and exit the thread completely.
|
|||
|
*/
|
|||
|
default:
|
|||
|
envp->err(envp, ret, "db put failed");
|
|||
|
ret = txn->abort(txn);
|
|||
|
if (ret != 0)
|
|||
|
envp->err(envp, ret, "txn abort failed");
|
|||
|
return (EXIT_FAILURE);
|
|||
|
} /** End case statement **/
|
|||
|
|
|||
|
} /** End for loop **/ </pre>
|
|||
|
<p>
|
|||
|
The only other change to <code class="function">writer_thread()</code>
|
|||
|
is that we pass <code class="function">count_records()</code> a
|
|||
|
transaction handle, rather than configuring our entire
|
|||
|
application for uncommitted reads. Both mechanisms work well-enough
|
|||
|
for preventing a self-deadlock. However, the individual count
|
|||
|
in this example will tend to be lower than the counts seen in
|
|||
|
the previous transaction example, because
|
|||
|
<code class="function">count_records()</code> can no longer see records
|
|||
|
created but not yet committed by other threads.
|
|||
|
</p>
|
|||
|
<pre class="programlisting"> /*
|
|||
|
* print the number of records found in the database.
|
|||
|
* See count_records() for usage information.
|
|||
|
*/
|
|||
|
printf("Thread %i. Record count: %i\n", thread_num,
|
|||
|
count_records(dbp, <strong class="userinput"><code>txn</code></strong>));
|
|||
|
|
|||
|
/*
|
|||
|
* If all goes well, we can commit the transaction and
|
|||
|
* loop to the next transaction.
|
|||
|
*/
|
|||
|
ret = txn->commit(txn, 0);
|
|||
|
if (ret != 0) {
|
|||
|
envp->err(envp, ret, "txn commit failed");
|
|||
|
return (EXIT_FAILURE);
|
|||
|
}
|
|||
|
}
|
|||
|
return (EXIT_SUCCESS);
|
|||
|
} </pre>
|
|||
|
<p>
|
|||
|
Next we update
|
|||
|
<span><code class="function">count_records()</code>.</span>
|
|||
|
|
|||
|
The only difference
|
|||
|
here is that we no longer specify <code class="literal">DB_READ_UNCOMMITTED</code> when
|
|||
|
we open our cursor. Note that even this minor change is not required.
|
|||
|
If we do not configure our database to support uncommitted reads,
|
|||
|
<code class="literal">DB_READ_UNCOMMITTED</code> on the cursor open will be silently
|
|||
|
ignored. However, we remove the flag anyway from the cursor open so as to
|
|||
|
avoid confusion.
|
|||
|
</p>
|
|||
|
<pre class="programlisting">int
|
|||
|
count_records(DB *dbp, DB_TXN *txn)
|
|||
|
{
|
|||
|
DBT key, value;
|
|||
|
DBC *cursorp;
|
|||
|
int count, ret;
|
|||
|
|
|||
|
cursorp = NULL;
|
|||
|
count = 0;
|
|||
|
|
|||
|
/* Get the cursor */
|
|||
|
ret = dbp->cursor(dbp, txn, &cursorp, <strong class="userinput"><code>0</code></strong>);
|
|||
|
if (ret != 0) {
|
|||
|
dbp->err(dbp, ret, "count_records: cursor open failed.");
|
|||
|
goto cursor_err;
|
|||
|
}
|
|||
|
|
|||
|
/* Get the key DBT used for the database read */
|
|||
|
memset(&key, 0, sizeof(DBT));
|
|||
|
memset(&value, 0, sizeof(DBT));
|
|||
|
do {
|
|||
|
ret = cursorp->get(cursorp, &key, &value, DB_NEXT);
|
|||
|
switch (ret) {
|
|||
|
case 0:
|
|||
|
count++;
|
|||
|
break;
|
|||
|
case DB_NOTFOUND:
|
|||
|
break;
|
|||
|
default:
|
|||
|
dbp->err(dbp, ret,
|
|||
|
"Count records unspecified error");
|
|||
|
goto cursor_err;
|
|||
|
}
|
|||
|
} while (ret == 0);
|
|||
|
|
|||
|
cursor_err:
|
|||
|
if (cursorp != NULL) {
|
|||
|
ret = cursorp->close(cursorp);
|
|||
|
if (ret != 0) {
|
|||
|
dbp->err(dbp, ret,
|
|||
|
"count_records: cursor close failed.");
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
return (count);
|
|||
|
}</pre>
|
|||
|
<p>
|
|||
|
Finally, we update
|
|||
|
<span><code class="function">open_db()</code>.</span>
|
|||
|
|
|||
|
This involves
|
|||
|
removing <code class="literal">DB_READ_UNCOMMITTED</code> from the
|
|||
|
open flags.
|
|||
|
|
|||
|
<span>We are also careful to change our database open error
|
|||
|
message to no longer use the <code class="literal">file_name</code>
|
|||
|
variable because that value will always be <code class="literal">NULL</code> for this example.</span>
|
|||
|
</p>
|
|||
|
<pre class="programlisting">/* Open a Berkeley DB database */
|
|||
|
int
|
|||
|
open_db(DB **dbpp, const char *progname, const char *file_name,
|
|||
|
DB_ENV *envp, u_int32_t extra_flags)
|
|||
|
{
|
|||
|
int ret;
|
|||
|
u_int32_t open_flags;
|
|||
|
DB *dbp;
|
|||
|
|
|||
|
/* Initialize the DB handle */
|
|||
|
ret = db_create(&dbp, envp, 0);
|
|||
|
if (ret != 0) {
|
|||
|
fprintf(stderr, "%s: %s\n", progname,
|
|||
|
db_strerror(ret));
|
|||
|
return (EXIT_FAILURE);
|
|||
|
}
|
|||
|
|
|||
|
/* Point to the memory malloc'd by db_create() */
|
|||
|
*dbpp = dbp;
|
|||
|
|
|||
|
if (extra_flags != 0) {
|
|||
|
ret = dbp->set_flags(dbp, extra_flags);
|
|||
|
if (ret != 0) {
|
|||
|
dbp->err(dbp, ret,
|
|||
|
"open_db: Attempt to set extra flags failed.");
|
|||
|
return (EXIT_FAILURE);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
/* Now open the database */
|
|||
|
<strong class="userinput"><code>open_flags = DB_CREATE | /* Allow database creation */
|
|||
|
DB_THREAD |
|
|||
|
DB_AUTO_COMMIT; /* Allow auto commit */</code></strong>
|
|||
|
|
|||
|
ret = dbp->open(dbp, /* Pointer to the database */
|
|||
|
NULL, /* Txn pointer */
|
|||
|
file_name, /* File name */
|
|||
|
NULL, /* Logical db name */
|
|||
|
DB_BTREE, /* Database type (using btree) */
|
|||
|
open_flags, /* Open flags */
|
|||
|
0); /* File mode. Using defaults */
|
|||
|
if (ret != 0) {
|
|||
|
<strong class="userinput"><code>dbp->err(dbp, ret, "Database open failed");
|
|||
|
return (EXIT_FAILURE);</code></strong>
|
|||
|
}
|
|||
|
return (EXIT_SUCCESS);
|
|||
|
} </pre>
|
|||
|
<p>
|
|||
|
This completes our in-memory transactional example. If you would like to
|
|||
|
experiment with this code, you can find the example in the following
|
|||
|
location in your DB distribution:
|
|||
|
</p>
|
|||
|
<pre class="programlisting"><span class="emphasis"><em>DB_INSTALL</em></span>/examples_c/txn_guide</pre>
|
|||
|
</div>
|
|||
|
<div class="navfooter">
|
|||
|
<hr />
|
|||
|
<table width="100%" summary="Navigation footer">
|
|||
|
<tr>
|
|||
|
<td width="40%" align="left"><a accesskey="p" href="txnexample_c.html">Prev</a> </td>
|
|||
|
<td width="20%" align="center">
|
|||
|
<a accesskey="u" href="wrapup.html">Up</a>
|
|||
|
</td>
|
|||
|
<td width="40%" align="right"> </td>
|
|||
|
</tr>
|
|||
|
<tr>
|
|||
|
<td width="40%" align="left" valign="top">Transaction Example </td>
|
|||
|
<td width="20%" align="center">
|
|||
|
<a accesskey="h" href="index.html">Home</a>
|
|||
|
</td>
|
|||
|
<td width="40%" align="right" valign="top"> </td>
|
|||
|
</tr>
|
|||
|
</table>
|
|||
|
</div>
|
|||
|
</body>
|
|||
|
</html>
|