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-&gt;txn_begin(dbenv, NULL, &tid, 0)) != 0) {
102			dbenv-&gt;err(dbenv, ret, "DB_ENV-&gt;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-&gt;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-&gt;abort(tid)) != 0) {
118				dbenv-&gt;err(dbenv, t_ret, "DB_TXN-&gt;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-&gt;put(dbp, tid, &key, &data, 0)) {
138		case 0:
139			/* Success: commit the change. */
140			if ((ret = tid-&gt;commit(tid, 0)) != 0) {
141				dbenv-&gt;err(dbenv, ret, "DB_TXN-&gt;commit");
142				exit (1);
143			}
144			return (0);
145		case DB_LOCK_DEADLOCK:
146		default:
147			/* Retry the operation. */
148			if ((t_ret = tid-&gt;abort(tid)) != 0) {
149				dbenv-&gt;err(dbenv, t_ret, "DB_TXN-&gt;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-&gt;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