Imported Upstream version 5.3.21
[platform/upstream/libdb.git] / docs / gsg_txn / CXX / inmem_txnexample_c.html
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">
4   <head>
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" />
12   </head>
13   <body>
14     <div xmlns="" class="navheader">
15       <div class="libver">
16         <p>Library Version 11.2.5.3</p>
17       </div>
18       <table width="100%" summary="Navigation header">
19         <tr>
20           <th colspan="3" align="center">In-Memory Transaction Example</th>
21         </tr>
22         <tr>
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>
26         </tr>
27       </table>
28       <hr />
29     </div>
30     <div class="sect1" lang="en" xml:lang="en">
31       <div class="titlepage">
32         <div>
33           <div>
34             <h2 class="title" style="clear: both"><a id="inmem_txnexample_c"></a>In-Memory Transaction Example</h2>
35           </div>
36         </div>
37       </div>
38       <p>
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.
42     </p>
43       <p>
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.
49     </p>
50       <p>
51         To do this:
52     </p>
53       <div class="itemizedlist">
54         <ul type="disc">
55           <li>
56             <p>
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.
62             </p>
63           </li>
64           <li>
65             <p>
66                 Configure your environment to back your regions from
67                 system memory instead of the filesystem.
68             </p>
69           </li>
70           <li>
71             <p>
72                 Configure your logging subsystem such that log files are kept
73                 entirely in-memory.
74             </p>
75           </li>
76           <li>
77             <p>
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.
80             </p>
81           </li>
82           <li>
83             <p>
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
86                 page to disk.
87             </p>
88           </li>
89           <li>
90             <p>
91                 Do not specify a file name when you open your database(s).
92             </p>
93           </li>
94         </ul>
95       </div>
96       <p>
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. 
101     </p>
102       <p>
103         For illustration purposes, we also modify this example so that 
104         uncommitted reads are no longer used to enable the 
105             
106             <code class="function">countRecords()</code>
107         function. Instead, we simply provide a transaction handle to
108             
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
114             this case.
115     </p>
116       <p>
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. 
123     </p>
124       <pre class="programlisting">// File TxnGuideInMemory.cpp
125
126 // We assume an ANSI-compatible compiler
127 #include &lt;db_cxx.h&gt;
128 #include &lt;pthread.h&gt;
129 #include &lt;iostream&gt;
130
131 // Run 5 writers threads at a time.
132 #define NUMWRITERS 5
133
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;
138
139 // Forward declarations
140 int countRecords(Db *, DbTxn *);
141 int openDb(Db **, const char *, const char *, DbEnv *, u_int32_t);
142 int usage(void);
143 void *writerThread(void *);  </pre>
144       <p>
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
147     the 
148          
149         <code class="literal">dbHomeDir</code> 
150     and 
151         
152         <code class="literal">fileName</code>
153     variables. We also remove all our <code class="function">getopt</code> code.
154 </p>
155       <pre class="programlisting">int
156 main(void)
157 {
158     // Initialize our handles
159     Db *dbp = NULL;
160     DbEnv *envp = NULL;
161
162     pthread_t writerThreads[NUMWRITERS];
163     int i;
164     u_int32_t envFlags;
165
166     // Application name
167     const char *progName = "TxnGuideInMemory";  </pre>
168       <p>
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
174         entirely in-memory.
175     </p>
176       <p>
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.
179     </p>
180       <p>
181         Note that we show the additional code here in
182         <strong class="userinput"><code>bold.</code></strong>
183     </p>
184       <pre class="programlisting">    // Env open flags
185     envFlags =
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
195
196     try {
197         // Create the environment 
198         envp = new DbEnv(0); </pre>
199       <p>
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.
204       </p>
205       <pre class="programlisting">
206         <strong class="userinput">
207           <code>        // Specify in-memory logging
208         envp-&gt;log_set_config(DB_LOG_IN_MEMORY, 1);
209
210         // Specify the size of the in-memory log buffer.
211         envp-&gt;set_lg_bsize(10 * 1024 * 1024);
212
213         // Specify the size of the in-memory cache
214         envp-&gt;set_cachesize(0, 10 * 1024 * 1024, 1); </code>
215         </strong>
216       </pre>
217       <p>
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.
221  </p>
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-&gt;set_lk_detect(DB_LOCK_MINWRITE);
227
228         // Open the environment
229         envp-&gt;open(<strong class="userinput"><code>NULL</code></strong>, envFlags, 0); </pre>
230       <p>
231         When we call 
232              
233             <span><code class="function">openDb()</code>,</span> 
234         which is what we use
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.
238     </p>
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.
243
244         // Open the database
245         openDb(&amp;dbp, progName, <strong class="userinput"><code>NULL</code></strong>,
246             envp, DB_DUPSORT);
247         </pre>
248       <p>
249     After that, our <code class="function">main()</code> function is unchanged,
250     except that when we 
251         
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.
254   </p>
255       <pre class="programlisting">        // Initialize a pthread mutex. Used to help provide thread ids.
256         (void)pthread_mutex_init(&amp;thread_num_lock, NULL);
257
258         // Start the writer threads.
259         for (i = 0; i &lt; NUMWRITERS; i++)
260             (void)pthread_create(
261                 &amp;writerThreads[i], NULL,
262                 writerThread,
263                 (void *)dbp);
264
265         // Join the writers
266         for (i = 0; i &lt; NUMWRITERS; i++)
267             (void)pthread_join(writerThreads[i], NULL);
268
269     } catch(DbException &amp;e) {
270         <strong class="userinput"><code>std::cerr &lt;&lt; "Error opening database environment: "</code></strong>
271                   &lt;&lt; std::endl;
272         std::cerr &lt;&lt; e.what() &lt;&lt; std::endl;
273         return (EXIT_FAILURE);
274     }
275
276     try {
277         // Close our database handle if it was opened.
278         if (dbp != NULL)
279             dbp-&gt;close(0);
280
281         // Close our environment if it was opened.
282         if (envp != NULL)
283             envp-&gt;close(0);
284     } catch(DbException &amp;e) {
285         std::cerr &lt;&lt; "Error closing database and environment."
286                   &lt;&lt; std::endl;
287         std::cerr &lt;&lt; e.what() &lt;&lt; std::endl;
288         return (EXIT_FAILURE);
289     }
290
291     // Final status message and return.
292
293     std::cout &lt;&lt; "I'm all done." &lt;&lt; std::endl;
294     return (EXIT_SUCCESS);
295 } </pre>
296       <p>
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.
307     </p>
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.
313 void *
314 writerThread(void *args)
315 {
316     Db *dbp = (Db *)args;
317     DbEnv *envp = dbp-&gt;get_env(dbp);
318
319     int j, thread_num;
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",
323                            "key 9", "key 10"};
324
325     // Get the thread number
326     (void)pthread_mutex_lock(&amp;thread_num_lock);
327     global_thread_num++;
328     thread_num = global_thread_num;
329     (void)pthread_mutex_unlock(&amp;thread_num_lock);
330
331     // Initialize the random number generator 
332     srand((u_int)pthread_self());
333
334     // Perform 50 transactions
335     for (int i=0; i&lt;50; i++) {
336         DbTxn *txn;
337         bool retry = true;
338         int retry_count = 0;
339         // while loop is used for deadlock retries
340         while (retry) {
341             // try block used for deadlock detection and
342             // general db exception handling
343             try {
344
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.
351
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.
357                 txn = NULL;
358                 envp-&gt;txn_begin(NULL, &amp;txn, 0);
359                 // Perform the database write for this transaction.
360                 for (j = 0; j &lt; 10; j++) {
361                     Dbt key, value;
362                     key.set_data(key_strings[j]);
363                     key.set_size((strlen(key_strings[j]) + 1) *
364                         sizeof(char));
365
366                     int payload = rand() + i;
367                     value.set_data(&amp;payload);
368                     value.set_size(sizeof(int));
369
370                     // Perform the database put
371                     dbp-&gt;put(txn, &amp;key, &amp;value, 0);
372                 }
373
374                 // countRecords runs a cursor over the entire database.
375                 // We do this to illustrate issues of deadlocking
376                 std::cout &lt;&lt; thread_num &lt;&lt;  " : Found "
377                           &lt;&lt;  countRecords(dbp, <strong class="userinput"><code>txn</code></strong>)
378                           &lt;&lt; " records in the database." &lt;&lt; std::endl;
379
380                 std::cout &lt;&lt; thread_num &lt;&lt;  " : committing txn : " &lt;&lt; i
381                           &lt;&lt; std::endl;
382
383                 // commit
384                 try {
385                     txn-&gt;commit(0);
386                     retry = false;
387                     txn = NULL;
388                 } catch (DbException &amp;e) {
389                     std::cout &lt;&lt; "Error on txn commit: "
390                               &lt;&lt; e.what() &lt;&lt; std::endl;
391                 }
392             } catch (DbDeadlockException &amp;de) {
393                 // First thing we MUST do is abort the transaction.
394                 if (txn != NULL)
395                     (void)txn-&gt;abort();
396
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 &lt; max_retries) {
401                     std::cout &lt;&lt; "############### Writer " &lt;&lt; thread_num
402                               &lt;&lt; ": Got DB_LOCK_DEADLOCK.\n"
403                               &lt;&lt; "Retrying write operation."
404                               &lt;&lt; std::endl;
405                     retry_count++;
406                     retry = true;
407                  } else {
408                     // Otherwise, just give up.
409                     std::cerr &lt;&lt; "Writer " &lt;&lt; thread_num
410                               &lt;&lt; ": Got DeadLockException and out of "
411                               &lt;&lt; "retries. Giving up." &lt;&lt; std::endl;
412                     retry = false;
413                  }
414            } catch (DbException &amp;e) {
415                 std::cerr &lt;&lt; "db put failed" &lt;&lt; std::endl;
416                 std::cerr &lt;&lt; e.what() &lt;&lt; std::endl;
417                 if (txn != NULL)
418                     txn-&gt;abort();
419                 retry = false;
420            } catch (std::exception &amp;ee) {
421             std::cerr &lt;&lt; "Unknown exception: " &lt;&lt; ee.what() &lt;&lt; std::endl;
422             return (0);
423           }
424         }
425     }
426     return (0);
427 } </pre>
428       <p>
429     Next we update 
430         
431         <span><code class="function">countRecords()</code>.</span>
432     The only difference
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
438     avoid confusion.
439 </p>
440       <pre class="programlisting">int
441 countRecords(Db *dbp, DbTxn *txn)
442 {
443
444     Dbc *cursorp = NULL;
445     int count = 0;
446
447     try {
448         // Get the cursor
449         dbp-&gt;cursor(txn, &amp;cursorp, <strong class="userinput"><code>0</code></strong>);
450
451         Dbt key, value;
452         while (cursorp-&gt;get(&amp;key, &amp;value, DB_NEXT) == 0) {
453             count++;
454         }
455     } catch (DbDeadlockException &amp;de) {
456         std::cerr &lt;&lt; "countRecords: got deadlock" &lt;&lt; std::endl;
457         cursorp-&gt;close();
458         throw de;
459     } catch (DbException &amp;e) {
460         std::cerr &lt;&lt; "countRecords error:" &lt;&lt; std::endl;
461         std::cerr &lt;&lt; e.what() &lt;&lt; std::endl;
462     }
463
464     if (cursorp != NULL) {
465         try {
466             cursorp-&gt;close();
467         } catch (DbException &amp;e) {
468             std::cerr &lt;&lt; "countRecords: cursor close failed:" &lt;&lt; std::endl;
469             std::cerr &lt;&lt; e.what() &lt;&lt; std::endl;
470         }
471     }
472
473     return (count);
474 } </pre>
475       <p>
476         Finally, we update 
477              
478             <span><code class="function">openDb()</code>.</span> 
479         This involves
480         removing <code class="literal">DB_READ_UNCOMMITTED</code> from the
481         open flags. 
482
483         
484     </p>
485       <pre class="programlisting">// Open a Berkeley DB database
486 int
487 openDb(Db **dbpp, const char *progname, const char *fileName,
488   DbEnv *envp, u_int32_t extraFlags)
489 {
490     int ret;
491     u_int32_t openFlags;
492
493     try {
494         Db *dbp = new Db(envp, 0);
495
496         // Point to the new'd Db
497         *dbpp = dbp;
498
499         if (extraFlags != 0)
500             ret = dbp-&gt;set_flags(extraFlags);
501
502         // Now open the database
503         <strong class="userinput"><code>openFlags = DB_CREATE   |      // Allow database creation
504                     DB_THREAD        |        
505                     DB_AUTO_COMMIT;    // Allow auto commit</code></strong>
506
507         dbp-&gt;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 &amp;e) {
514         std::cerr &lt;&lt; progname &lt;&lt; ": openDb: db open failed:" &lt;&lt; std::endl;
515         std::cerr &lt;&lt; e.what() &lt;&lt; std::endl;
516         return (EXIT_FAILURE);
517     }
518
519     return (EXIT_SUCCESS);
520 } </pre>
521       <p>
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:
525 </p>
526       <pre class="programlisting"><span class="emphasis"><em>DB_INSTALL</em></span>/examples_cxx/txn_guide</pre>
527     </div>
528     <div class="navfooter">
529       <hr />
530       <table width="100%" summary="Navigation footer">
531         <tr>
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>
535           </td>
536           <td width="40%" align="right"> </td>
537         </tr>
538         <tr>
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>
542           </td>
543           <td width="40%" align="right" valign="top"> </td>
544         </tr>
545       </table>
546     </div>
547   </body>
548 </html>