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