1 <?xml version="1.0" encoding="UTF-8" standalone="no"?>
2 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
3 <html xmlns="http://www.w3.org/1999/xhtml">
5 <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
6 <title>In-Memory Transaction Example</title>
7 <link rel="stylesheet" href="gettingStarted.css" type="text/css" />
8 <meta name="generator" content="DocBook XSL Stylesheets V1.73.2" />
9 <link rel="start" href="index.html" title="Getting Started with Berkeley DB Transaction Processing" />
10 <link rel="up" href="wrapup.html" title="Chapter 6. Summary and Examples" />
11 <link rel="prev" href="txnexample_c.html" title="Transaction Example" />
14 <div xmlns="" class="navheader">
16 <p>Library Version 11.2.5.3</p>
18 <table width="100%" summary="Navigation header">
20 <th colspan="3" align="center">In-Memory Transaction Example</th>
23 <td width="20%" align="left"><a accesskey="p" href="txnexample_c.html">Prev</a> </td>
24 <th width="60%" align="center">Chapter 6. Summary and Examples</th>
25 <td width="20%" align="right"> </td>
30 <div class="sect1" lang="en" xml:lang="en">
31 <div class="titlepage">
34 <h2 class="title" style="clear: both"><a id="inmem_txnexample_c"></a>In-Memory Transaction Example</h2>
39 DB is sometimes used for applications that simply need to cache
40 data retrieved from some other location (such as a remote database
41 server). DB is also often used in embedded systems.
44 In both cases, applications may want to use transactions for
45 atomicity, consistency, and isolation guarantees, but they may also want
46 to forgo the durability guarantee entirely. In doing so, they can keep
47 their DB environment and databases entirely in-memory so
48 as to avoid the performance impact of unneeded disk I/O.
53 <div class="itemizedlist">
57 Refrain from specifying a home directory when you open your
58 environment. The exception to this is if you are using the
59 <code class="literal">DB_CONFIG</code> configuration file — in
60 that case you must identify the environment's home
61 directory so that the configuration file can be found.
66 Configure your environment to back your regions from
67 system memory instead of the filesystem.
72 Configure your logging subsystem such that log files are kept
78 Increase the size of your in-memory log buffer so that it
79 is large enough to hold the largest set of concurrent write operations.
84 Increase the size of your in-memory cache so that it can
85 hold your entire data set. You do not want your cache to
91 Do not specify a file name when you open your database(s).
97 As an example, this section takes the transaction example provided
98 in <a class="xref" href="txnexample_c.html" title="Transaction Example">Transaction Example</a>
99 and it updates that example so that the environment, database, log
100 files, and regions are all kept entirely in-memory.
103 For illustration purposes, we also modify this example so that
104 uncommitted reads are no longer used to enable the
106 <code class="function">countRecords()</code>
107 function. Instead, we simply provide a transaction handle to
109 <code class="function">countRecords()</code>
110 so as to avoid the self-deadlock. Be aware that using a transaction handle here rather than
111 uncommitted reads will work just as well as if we had continued to use uncommitted reads. However,
112 the usage of the transaction handle here will
113 probably cause more deadlocks than using read-uncommitted does, because more locking is being performed in
117 To begin, we simplify the beginning of our example a bit. Because
118 we no longer need an environment home directory, we can remove all
119 the code that we used to determine path delimiters
120 and include the <code class="function">getopt</code> function. We can also
121 remove our <code class="function">usage()</code> function because we no
122 longer require any command line arguments.
124 <pre class="programlisting">// File TxnGuideInMemory.cpp
126 // We assume an ANSI-compatible compiler
127 #include <db_cxx.h>
128 #include <pthread.h>
129 #include <iostream>
131 // Run 5 writers threads at a time.
134 // Printing of pthread_t is implementation-specific, so we
135 // create our own thread IDs for reporting purposes.
136 int global_thread_num;
137 pthread_mutex_t thread_num_lock;
139 // Forward declarations
140 int countRecords(Db *, DbTxn *);
141 int openDb(Db **, const char *, const char *, DbEnv *, u_int32_t);
143 void *writerThread(void *); </pre>
145 Next, in our <code class="function">main()</code>, we also eliminate some
146 variables that this example no longer needs. In particular, we are able to remove
149 <code class="literal">dbHomeDir</code>
152 <code class="literal">fileName</code>
153 variables. We also remove all our <code class="function">getopt</code> code.
155 <pre class="programlisting">int
158 // Initialize our handles
162 pthread_t writerThreads[NUMWRITERS];
167 const char *progName = "TxnGuideInMemory"; </pre>
169 Next we create our environment as always. However, we add
170 <code class="literal">DB_PRIVATE</code> to our environment open flags. This
171 flag causes our environment to back regions using our
172 application's heap memory rather than by using the filesystem.
173 This is the first important step to keeping our DB data
177 We also remove the <code class="literal">DB_RECOVER</code> flag from the environment open flags. Because our databases,
178 logs, and regions are maintained in-memory, there will never be anything to recover.
181 Note that we show the additional code here in
182 <strong class="userinput"><code>bold.</code></strong>
184 <pre class="programlisting"> // Env open flags
186 DB_CREATE | // Create the environment if it does not exist
187 DB_INIT_LOCK | // Initialize the locking subsystem
188 DB_INIT_LOG | // Initialize the logging subsystem
189 DB_INIT_TXN | // Initialize the transactional subsystem. This
190 // also turns on logging.
191 DB_INIT_MPOOL | // Initialize the memory pool (in-memory cache)
192 <strong class="userinput"><code>DB_PRIVATE | // Region files are not backed by the filesystem.
193 // Instead, they are backed by heap memory.</code></strong>
194 DB_THREAD; // Cause the environment to be free-threaded
197 // Create the environment
198 envp = new DbEnv(0); </pre>
200 Now we configure our environment to keep the log files in memory,
201 increase the log buffer size to 10 MB, and increase our in-memory
202 cache to 10 MB. These values should be more than enough for our
203 application's workload.
205 <pre class="programlisting">
206 <strong class="userinput">
207 <code> // Specify in-memory logging
208 envp->log_set_config(DB_LOG_IN_MEMORY, 1);
210 // Specify the size of the in-memory log buffer.
211 envp->set_lg_bsize(10 * 1024 * 1024);
213 // Specify the size of the in-memory cache
214 envp->set_cachesize(0, 10 * 1024 * 1024, 1); </code>
218 Next, we open the environment and setup our lock detection. This is
219 identical to how the example previously worked, except that we do not
220 provide a location for the environment's home directory.
222 <pre class="programlisting"> // Indicate that we want db to internally perform deadlock
223 // detection. Also indicate that the transaction with
224 // the fewest number of write locks will receive the
225 // deadlock notification in the event of a deadlock.
226 envp->set_lk_detect(DB_LOCK_MINWRITE);
228 // Open the environment
229 envp->open(<strong class="userinput"><code>NULL</code></strong>, envFlags, 0); </pre>
233 <span><code class="function">openDb()</code>,</span>
235 to open our database, we no not provide a database filename for the
236 third parameter. When the filename is <code class="literal">NULL</code>, the database is not
237 backed by the filesystem.
239 <pre class="programlisting"> // If we had utility threads (for running checkpoints or
240 // deadlock detection, for example) we would spawn those
241 // here. However, for a simple example such as this,
242 // that is not required.
245 openDb(&dbp, progName, <strong class="userinput"><code>NULL</code></strong>,
249 After that, our <code class="function">main()</code> function is unchanged,
252 <span>check for exceptions on the database open,</span>
253 we change the error message string so as to not reference the database filename.
255 <pre class="programlisting"> // Initialize a pthread mutex. Used to help provide thread ids.
256 (void)pthread_mutex_init(&thread_num_lock, NULL);
258 // Start the writer threads.
259 for (i = 0; i < NUMWRITERS; i++)
260 (void)pthread_create(
261 &writerThreads[i], NULL,
266 for (i = 0; i < NUMWRITERS; i++)
267 (void)pthread_join(writerThreads[i], NULL);
269 } catch(DbException &e) {
270 <strong class="userinput"><code>std::cerr << "Error opening database environment: "</code></strong>
272 std::cerr << e.what() << std::endl;
273 return (EXIT_FAILURE);
277 // Close our database handle if it was opened.
281 // Close our environment if it was opened.
284 } catch(DbException &e) {
285 std::cerr << "Error closing database and environment."
287 std::cerr << e.what() << std::endl;
288 return (EXIT_FAILURE);
291 // Final status message and return.
293 std::cout << "I'm all done." << std::endl;
294 return (EXIT_SUCCESS);
297 That completes <code class="function">main()</code>. The bulk of our
298 <code class="function">writerThread()</code> function implementation is
299 unchanged from the initial transaction example, except that we now pass
300 <code class="function">countRecords</code> a transaction handle, rather than configuring our
301 application to perform uncommitted reads. Both mechanisms work well-enough
302 for preventing a self-deadlock. However, the individual count
303 in this example will tend to be lower than the counts seen in
304 the previous transaction example, because
305 <code class="function">countRecords()</code> can no longer see records
306 created but not yet committed by other threads.
308 <pre class="programlisting">// A function that performs a series of writes to a
309 // Berkeley DB database. The information written
310 // to the database is largely nonsensical, but the
311 // mechanism of transactional commit/abort and
312 // deadlock detection is illustrated here.
314 writerThread(void *args)
316 Db *dbp = (Db *)args;
317 DbEnv *envp = dbp->get_env(dbp);
320 int max_retries = 20; // Max retry on a deadlock
321 char *key_strings[] = {"key 1", "key 2", "key 3", "key 4",
322 "key 5", "key 6", "key 7", "key 8",
325 // Get the thread number
326 (void)pthread_mutex_lock(&thread_num_lock);
328 thread_num = global_thread_num;
329 (void)pthread_mutex_unlock(&thread_num_lock);
331 // Initialize the random number generator
332 srand((u_int)pthread_self());
334 // Perform 50 transactions
335 for (int i=0; i<50; i++) {
339 // while loop is used for deadlock retries
341 // try block used for deadlock detection and
342 // general db exception handling
345 // Begin our transaction. We group multiple writes in
346 // this thread under a single transaction so as to
347 // (1) show that you can atomically perform multiple
348 // writes at a time, and (2) to increase the chances
349 // of a deadlock occurring so that we can observe our
350 // deadlock detection at work.
352 // Normally we would want to avoid the potential for
353 // deadlocks, so for this workload the correct thing
354 // would be to perform our puts with auto commit. But
355 // that would excessively simplify our example, so we
356 // do the "wrong" thing here instead.
358 envp->txn_begin(NULL, &txn, 0);
359 // Perform the database write for this transaction.
360 for (j = 0; j < 10; j++) {
362 key.set_data(key_strings[j]);
363 key.set_size((strlen(key_strings[j]) + 1) *
366 int payload = rand() + i;
367 value.set_data(&payload);
368 value.set_size(sizeof(int));
370 // Perform the database put
371 dbp->put(txn, &key, &value, 0);
374 // countRecords runs a cursor over the entire database.
375 // We do this to illustrate issues of deadlocking
376 std::cout << thread_num << " : Found "
377 << countRecords(dbp, <strong class="userinput"><code>txn</code></strong>)
378 << " records in the database." << std::endl;
380 std::cout << thread_num << " : committing txn : " << i
388 } catch (DbException &e) {
389 std::cout << "Error on txn commit: "
390 << e.what() << std::endl;
392 } catch (DbDeadlockException &de) {
393 // First thing we MUST do is abort the transaction.
395 (void)txn->abort();
397 // Now we decide if we want to retry the operation.
398 // If we have retried less than max_retries,
399 // increment the retry count and goto retry.
400 if (retry_count < max_retries) {
401 std::cout << "############### Writer " << thread_num
402 << ": Got DB_LOCK_DEADLOCK.\n"
403 << "Retrying write operation."
408 // Otherwise, just give up.
409 std::cerr << "Writer " << thread_num
410 << ": Got DeadLockException and out of "
411 << "retries. Giving up." << std::endl;
414 } catch (DbException &e) {
415 std::cerr << "db put failed" << std::endl;
416 std::cerr << e.what() << std::endl;
420 } catch (std::exception &ee) {
421 std::cerr << "Unknown exception: " << ee.what() << std::endl;
431 <span><code class="function">countRecords()</code>.</span>
433 here is that we no longer specify <code class="literal">DB_READ_UNCOMMITTED</code> when
434 we open our cursor. Note that even this minor change is not required.
435 If we do not configure our database to support uncommitted reads,
436 <code class="literal">DB_READ_UNCOMMITTED</code> on the cursor open will be silently
437 ignored. However, we remove the flag anyway from the cursor open so as to
440 <pre class="programlisting">int
441 countRecords(Db *dbp, DbTxn *txn)
449 dbp->cursor(txn, &cursorp, <strong class="userinput"><code>0</code></strong>);
452 while (cursorp->get(&key, &value, DB_NEXT) == 0) {
455 } catch (DbDeadlockException &de) {
456 std::cerr << "countRecords: got deadlock" << std::endl;
459 } catch (DbException &e) {
460 std::cerr << "countRecords error:" << std::endl;
461 std::cerr << e.what() << std::endl;
464 if (cursorp != NULL) {
467 } catch (DbException &e) {
468 std::cerr << "countRecords: cursor close failed:" << std::endl;
469 std::cerr << e.what() << std::endl;
478 <span><code class="function">openDb()</code>.</span>
480 removing <code class="literal">DB_READ_UNCOMMITTED</code> from the
485 <pre class="programlisting">// Open a Berkeley DB database
487 openDb(Db **dbpp, const char *progname, const char *fileName,
488 DbEnv *envp, u_int32_t extraFlags)
494 Db *dbp = new Db(envp, 0);
496 // Point to the new'd Db
500 ret = dbp->set_flags(extraFlags);
502 // Now open the database
503 <strong class="userinput"><code>openFlags = DB_CREATE | // Allow database creation
505 DB_AUTO_COMMIT; // Allow auto commit</code></strong>
507 dbp->open(NULL, // Txn pointer
508 fileName, // File name
509 NULL, // Logical db name
510 DB_BTREE, // Database type (using btree)
511 openFlags, // Open flags
512 0); // File mode. Using defaults
513 } catch (DbException &e) {
514 std::cerr << progname << ": openDb: db open failed:" << std::endl;
515 std::cerr << e.what() << std::endl;
516 return (EXIT_FAILURE);
519 return (EXIT_SUCCESS);
522 This completes our in-memory transactional example. If you would like to
523 experiment with this code, you can find the example in the following
524 location in your DB distribution:
526 <pre class="programlisting"><span class="emphasis"><em>DB_INSTALL</em></span>/examples_cxx/txn_guide</pre>
528 <div class="navfooter">
530 <table width="100%" summary="Navigation footer">
532 <td width="40%" align="left"><a accesskey="p" href="txnexample_c.html">Prev</a> </td>
533 <td width="20%" align="center">
534 <a accesskey="u" href="wrapup.html">Up</a>
536 <td width="40%" align="right"> </td>
539 <td width="40%" align="left" valign="top">Transaction Example </td>
540 <td width="20%" align="center">
541 <a accesskey="h" href="index.html">Home</a>
543 <td width="40%" align="right" valign="top"> </td>