1<!--$Id: inc.so,v 1.17 2006/08/31 15:26:59 ubell Exp $--> 2<!--Copyright (c) 1997,2008 Oracle. All rights reserved.--> 3<!--See the file LICENSE for redistribution information.--> 4<html> 5<head> 6<title>Berkeley DB Reference Guide: Isolation</title> 7<meta name="description" content="Berkeley DB: An embedded database programmatic toolkit."> 8<meta name="keywords" content="embedded,database,programmatic,toolkit,btree,hash,hashing,transaction,transactions,locking,logging,access method,access methods,Java,C,C++"> 9</head> 10<body bgcolor=white> 11<table width="100%"><tr valign=top> 12<td><b><dl><dt>Berkeley DB Reference Guide:<dd>Berkeley DB Transactional Data Store Applications</dl></b></td> 13<td align=right><a href="../transapp/atomicity.html"><img src="../../images/prev.gif" alt="Prev"></a><a href="../toc.html"><img src="../../images/ref.gif" alt="Ref"></a><a href="../transapp/read.html"><img src="../../images/next.gif" alt="Next"></a> 14</td></tr></table> 15<p align=center><b>Isolation</b></p> 16<p>The third reason listed for using transactions was <i>isolation</i>. 17Consider an application suite in which multiple threads of control 18(multiple processes or threads in one or more processes) are changing 19the values associated with a key in one or more databases. Specifically, 20they are taking the current value, incrementing it, and then storing it 21back into the database.</p> 22<p>Such an application requires isolation. Because we want to change a value 23in the database, we must make sure that after we read it, no other thread 24of control modifies it. For example, assume that both thread #1 and 25thread #2 are doing similar operations in the database, where thread #1 26is incrementing records by 3, and thread #2 is incrementing records by 275. We want to increment the record by a total of 8. If the operations 28interleave in the right (well, wrong) order, that is not what will 29happen:</p> 30<blockquote><pre>thread #1 <b>read</b> record: the value is 2 31thread #2 <b>read</b> record: the value is 2 32thread #2 <b>write</b> record + 5 back into the database (new value 7) 33thread #1 <b>write</b> record + 3 back into the database (new value 5)</pre></blockquote> 34<p>As you can see, instead of incrementing the record by a total of 8, 35we've incremented it only by 3 because thread #1 overwrote thread #2's 36change. By wrapping the operations in transactions, we ensure that this 37cannot happen. In a transaction, when the first thread reads the 38record, locks are acquired that will not be released until the 39transaction finishes, guaranteeing that all writers 40will block, waiting for the first thread's transaction to complete (or 41to be aborted).</p> 42<p>Here is an example function that does transaction-protected increments 43on database records to ensure isolation:</p> 44<blockquote><pre>int 45main(int argc, char *argv) 46{ 47 extern int optind; 48 DB *db_cats, *db_color, *db_fruit; 49 DB_ENV *dbenv; 50 int ch; 51<p> 52 while ((ch = getopt(argc, argv, "")) != EOF) 53 switch (ch) { 54 case '?': 55 default: 56 usage(); 57 } 58 argc -= optind; 59 argv += optind; 60<p> 61 env_dir_create(); 62 env_open(&dbenv); 63<p> 64 /* Open database: Key is fruit class; Data is specific type. */ 65 db_open(dbenv, &db_fruit, "fruit", 0); 66<p> 67 /* Open database: Key is a color; Data is an integer. */ 68 db_open(dbenv, &db_color, "color", 0); 69<p> 70 /* 71 * Open database: 72 * Key is a name; Data is: company name, cat breeds. 73 */ 74 db_open(dbenv, &db_cats, "cats", 1); 75<p> 76 add_fruit(dbenv, db_fruit, "apple", "yellow delicious"); 77<p> 78<b> add_color(dbenv, db_color, "blue", 0); 79 add_color(dbenv, db_color, "blue", 3);</b> 80<p> 81 return (0); 82} 83<p> 84<b>int 85add_color(DB_ENV *dbenv, DB *dbp, char *color, int increment) 86{ 87 DBT key, data; 88 DB_TXN *tid; 89 int fail, original, ret, t_ret; 90 char buf64; 91<p> 92 /* Initialization. */ 93 memset(&key, 0, sizeof(key)); 94 key.data = color; 95 key.size = strlen(color); 96 memset(&data, 0, sizeof(data)); 97 data.flags = DB_DBT_MALLOC; 98<p> 99 for (fail = 0;;) { 100 /* Begin the transaction. */ 101 if ((ret = dbenv->txn_begin(dbenv, NULL, &tid, 0)) != 0) { 102 dbenv->err(dbenv, ret, "DB_ENV->txn_begin"); 103 exit (1); 104 } 105<p> 106 /* 107 * Get the key. If it exists, we increment the value. If it 108 * doesn't exist, we create it. 109 */ 110 switch (ret = dbp->get(dbp, tid, &key, &data, DB_RMW)) { 111 case 0: 112 original = atoi(data.data); 113 break; 114 case DB_LOCK_DEADLOCK: 115 default: 116 /* Retry the operation. */ 117 if ((t_ret = tid->abort(tid)) != 0) { 118 dbenv->err(dbenv, t_ret, "DB_TXN->abort"); 119 exit (1); 120 } 121 if (fail++ == MAXIMUM_RETRY) 122 return (ret); 123 continue; 124 case DB_NOTFOUND: 125 original = 0; 126 break; 127 } 128 if (data.data != NULL) 129 free(data.data); 130<p> 131 /* Create the new data item. */ 132 (void)snprintf(buf, sizeof(buf), "%d", original + increment); 133 data.data = buf; 134 data.size = strlen(buf) + 1; 135<p> 136 /* Store the new value. */ 137 switch (ret = dbp->put(dbp, tid, &key, &data, 0)) { 138 case 0: 139 /* Success: commit the change. */ 140 if ((ret = tid->commit(tid, 0)) != 0) { 141 dbenv->err(dbenv, ret, "DB_TXN->commit"); 142 exit (1); 143 } 144 return (0); 145 case DB_LOCK_DEADLOCK: 146 default: 147 /* Retry the operation. */ 148 if ((t_ret = tid->abort(tid)) != 0) { 149 dbenv->err(dbenv, t_ret, "DB_TXN->abort"); 150 exit (1); 151 } 152 if (fail++ == MAXIMUM_RETRY) 153 return (ret); 154 break; 155 } 156 } 157}</b></pre></blockquote> 158<p>The <a href="../../api_c/dbc_get.html#DB_RMW">DB_RMW</a> flag in the <a href="../../api_c/db_get.html">DB->get</a> call specifies a write lock 159should be acquired on the key/data pair, instead of the more obvious read 160lock. We do this because the application expects to write the key/data 161pair in a subsequent operation, and the transaction is much more likely to 162deadlock if we first obtain a read lock and subsequently a write lock, than 163if we obtain the write lock initially.</p> 164<table width="100%"><tr><td><br></td><td align=right><a href="../transapp/atomicity.html"><img src="../../images/prev.gif" alt="Prev"></a><a href="../toc.html"><img src="../../images/ref.gif" alt="Ref"></a><a href="../transapp/read.html"><img src="../../images/next.gif" alt="Next"></a> 165</td></tr></table> 166<p><font size=1>Copyright (c) 1996,2008 Oracle. All rights reserved.</font> 167</body> 168</html> 169