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>Isolation</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="Berkeley DB Programmer's Reference Guide" /> 10 <link rel="up" href="transapp.html" title="Chapter 11. Berkeley DB Transactional Data Store Applications" /> 11 <link rel="prev" href="transapp_atomicity.html" title="Atomicity" /> 12 <link rel="next" href="transapp_read.html" title="Degrees of isolation" /> 13 </head> 14 <body> 15 <div class="navheader"> 16 <table width="100%" summary="Navigation header"> 17 <tr> 18 <th colspan="3" align="center">Isolation</th> 19 </tr> 20 <tr> 21 <td width="20%" align="left"><a accesskey="p" href="transapp_atomicity.html">Prev</a> </td> 22 <th width="60%" align="center">Chapter 11. 23 Berkeley DB Transactional Data Store Applications 24 </th> 25 <td width="20%" align="right"> <a accesskey="n" href="transapp_read.html">Next</a></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="transapp_inc"></a>Isolation</h2> 35 </div> 36 </div> 37 </div> 38 <p>The third reason listed for using transactions was <span class="emphasis"><em>isolation</em></span>. 39Consider an application suite in which multiple threads of control 40(multiple processes or threads in one or more processes) are changing 41the values associated with a key in one or more databases. Specifically, 42they are taking the current value, incrementing it, and then storing it 43back into the database.</p> 44 <p>Such an application requires isolation. Because we want to change a value 45in the database, we must make sure that after we read it, no other thread 46of control modifies it. For example, assume that both thread #1 and 47thread #2 are doing similar operations in the database, where thread #1 48is incrementing records by 3, and thread #2 is incrementing records by 495. We want to increment the record by a total of 8. If the operations 50interleave in the right (well, wrong) order, that is not what will 51happen:</p> 52 <pre class="programlisting">thread #1 <span class="bold"><strong>read</strong></span> record: the value is 2 53thread #2 <span class="bold"><strong>read</strong></span> record: the value is 2 54thread #2 <span class="bold"><strong>write</strong></span> record + 5 back into the database (new value 7) 55thread #1 <span class="bold"><strong>write</strong></span> record + 3 back into the database (new value 5)</pre> 56 <p>As you can see, instead of incrementing the record by a total of 8, 57we've incremented it only by 3 because thread #1 overwrote thread #2's 58change. By wrapping the operations in transactions, we ensure that this 59cannot happen. In a transaction, when the first thread reads the 60record, locks are acquired that will not be released until the 61transaction finishes, guaranteeing that all writers 62will block, waiting for the first thread's transaction to complete (or 63to be aborted).</p> 64 <p>Here is an example function that does transaction-protected increments 65on database records to ensure isolation:</p> 66 <pre class="programlisting">int 67main(int argc, char *argv) 68{ 69 extern int optind; 70 DB *db_cats, *db_color, *db_fruit; 71 DB_ENV *dbenv; 72 int ch; 73 74 while ((ch = getopt(argc, argv, "")) != EOF) 75 switch (ch) { 76 case '?': 77 default: 78 usage(); 79 } 80 argc -= optind; 81 argv += optind; 82 83 env_dir_create(); 84 env_open(&dbenv); 85 86 /* Open database: Key is fruit class; Data is specific type. */ 87 db_open(dbenv, &db_fruit, "fruit", 0); 88 89 /* Open database: Key is a color; Data is an integer. */ 90 db_open(dbenv, &db_color, "color", 0); 91 92 /* 93 * Open database: 94 * Key is a name; Data is: company name, cat breeds. 95 */ 96 db_open(dbenv, &db_cats, "cats", 1); 97 98 add_fruit(dbenv, db_fruit, "apple", "yellow delicious"); 99 100<span class="bold"><strong> add_color(dbenv, db_color, "blue", 0); 101 add_color(dbenv, db_color, "blue", 3);</strong></span> 102 103 return (0); 104} 105 106<span class="bold"><strong>int 107add_color(DB_ENV *dbenv, DB *dbp, char *color, int increment) 108{ 109 DBT key, data; 110 DB_TXN *tid; 111 int fail, original, ret, t_ret; 112 char buf64; 113 114 /* Initialization. */ 115 memset(&key, 0, sizeof(key)); 116 key.data = color; 117 key.size = strlen(color); 118 memset(&data, 0, sizeof(data)); 119 data.flags = DB_DBT_MALLOC; 120 121 for (fail = 0;;) { 122 /* Begin the transaction. */ 123 if ((ret = dbenv->txn_begin(dbenv, NULL, &tid, 0)) != 0) { 124 dbenv->err(dbenv, ret, "DB_ENV->txn_begin"); 125 exit (1); 126 } 127 128 /* 129 * Get the key. If it exists, we increment the value. If it 130 * doesn't exist, we create it. 131 */ 132 switch (ret = dbp->get(dbp, tid, &key, &data, DB_RMW)) { 133 case 0: 134 original = atoi(data.data); 135 break; 136 case DB_LOCK_DEADLOCK: 137 default: 138 /* Retry the operation. */ 139 if ((t_ret = tid->abort(tid)) != 0) { 140 dbenv->err(dbenv, t_ret, "DB_TXN->abort"); 141 exit (1); 142 } 143 if (fail++ == MAXIMUM_RETRY) 144 return (ret); 145 continue; 146 case DB_NOTFOUND: 147 original = 0; 148 break; 149 } 150 if (data.data != NULL) 151 free(data.data); 152 153 /* Create the new data item. */ 154 (void)snprintf(buf, sizeof(buf), "%d", original + increment); 155 data.data = buf; 156 data.size = strlen(buf) + 1; 157 158 /* Store the new value. */ 159 switch (ret = dbp->put(dbp, tid, &key, &data, 0)) { 160 case 0: 161 /* Success: commit the change. */ 162 if ((ret = tid->commit(tid, 0)) != 0) { 163 dbenv->err(dbenv, ret, "DB_TXN->commit"); 164 exit (1); 165 } 166 return (0); 167 case DB_LOCK_DEADLOCK: 168 default: 169 /* Retry the operation. */ 170 if ((t_ret = tid->abort(tid)) != 0) { 171 dbenv->err(dbenv, t_ret, "DB_TXN->abort"); 172 exit (1); 173 } 174 if (fail++ == MAXIMUM_RETRY) 175 return (ret); 176 break; 177 } 178 } 179}</strong></span></pre> 180 <p>The <a href="../api_reference/C/dbcget.html#dbcget_DB_RMW" class="olink">DB_RMW</a> flag in the <a href="../api_reference/C/dbget.html" class="olink">DB->get()</a> call specifies a write lock 181should be acquired on the key/data pair, instead of the more obvious read 182lock. We do this because the application expects to write the key/data 183pair in a subsequent operation, and the transaction is much more likely to 184deadlock if we first obtain a read lock and subsequently a write lock, than 185if we obtain the write lock initially.</p> 186 </div> 187 <div class="navfooter"> 188 <hr /> 189 <table width="100%" summary="Navigation footer"> 190 <tr> 191 <td width="40%" align="left"><a accesskey="p" href="transapp_atomicity.html">Prev</a> </td> 192 <td width="20%" align="center"> 193 <a accesskey="u" href="transapp.html">Up</a> 194 </td> 195 <td width="40%" align="right"> <a accesskey="n" href="transapp_read.html">Next</a></td> 196 </tr> 197 <tr> 198 <td width="40%" align="left" valign="top">Atomicity </td> 199 <td width="20%" align="center"> 200 <a accesskey="h" href="index.html">Home</a> 201 </td> 202 <td width="40%" align="right" valign="top"> Degrees of isolation</td> 203 </tr> 204 </table> 205 </div> 206 </body> 207</html> 208