13
#define ENV_DIRECTORY "TXNAPP"
15
void add_cat(DB_ENV *, DB *, char *, ...);
16
void add_color(DB_ENV *, DB *, char *, int);
17
void add_fruit(DB_ENV *, DB *, char *, char *);
18
void *checkpoint_thread(void *);
19
void log_archlist(DB_ENV *);
20
void *logfile_thread(void *);
21
void db_open(DB_ENV *, DB **, char *, int);
22
void env_dir_create(void);
23
void env_open(DB_ENV **);
27
main(int argc, char *argv[])
31
DB *db_cats, *db_color, *db_fruit;
36
while ((ch = getopt(argc, argv, "")) != EOF)
48
/* Start a checkpoint thread. */
49
if ((ret = pthread_create(
50
&ptid, NULL, checkpoint_thread, (void *)dbenv)) != 0) {
52
"txnapp: failed spawning checkpoint thread: %s\n",
57
/* Start a logfile removal thread. */
58
if ((ret = pthread_create(
59
&ptid, NULL, logfile_thread, (void *)dbenv)) != 0) {
61
"txnapp: failed spawning log file removal thread: %s\n",
66
/* Open database: Key is fruit class; Data is specific type. */
67
db_open(dbenv, &db_fruit, "fruit", 0);
69
/* Open database: Key is a color; Data is an integer. */
70
db_open(dbenv, &db_color, "color", 0);
74
* Key is a name; Data is: company name, address, cat breeds.
76
db_open(dbenv, &db_cats, "cats", 1);
78
add_fruit(dbenv, db_fruit, "apple", "yellow delicious");
80
add_color(dbenv, db_color, "blue", 0);
81
add_color(dbenv, db_color, "blue", 3);
83
add_cat(dbenv, db_cats,
86
"394 E. Riding Dr., Carlisle, MA 01741, USA",
101
* If the directory exists, we're done. We do not further check
102
* the type of the file, DB will fail appropriately if it's the
105
if (stat(ENV_DIRECTORY, &sb) == 0)
108
/* Create the directory, read/write/access owner only. */
109
if (mkdir(ENV_DIRECTORY, S_IRWXU) != 0) {
111
"txnapp: mkdir: %s: %s\n", ENV_DIRECTORY, strerror(errno));
117
env_open(DB_ENV **dbenvp)
122
/* Create the environment handle. */
123
if ((ret = db_env_create(&dbenv, 0)) != 0) {
125
"txnapp: db_env_create: %s\n", db_strerror(ret));
129
/* Set up error handling. */
130
dbenv->set_errpfx(dbenv, "txnapp");
132
/* Do deadlock detection internally. */
133
if ((ret = dbenv->set_lk_detect(dbenv, DB_LOCK_DEFAULT)) != 0) {
134
dbenv->err(dbenv, ret, "set_lk_detect: DB_LOCK_DEFAULT");
139
* Open a transactional environment:
140
* create if it doesn't exist
141
* free-threaded handle
143
* read/write owner only
145
if ((ret = dbenv->open(dbenv, ENV_DIRECTORY,
146
DB_CREATE | DB_INIT_LOCK | DB_INIT_LOG |
147
DB_INIT_MPOOL | DB_INIT_TXN | DB_RECOVER | DB_THREAD,
148
S_IRUSR | S_IWUSR)) != 0) {
149
dbenv->err(dbenv, ret, "dbenv->open: %s", ENV_DIRECTORY);
157
checkpoint_thread(void *arg)
163
dbenv->errx(dbenv, "Checkpoint thread: %lu", (u_long)pthread_self());
165
/* Checkpoint once a minute. */
167
if ((ret = dbenv->txn_checkpoint(dbenv, 0, 0, 0)) != 0) {
168
dbenv->err(dbenv, ret, "checkpoint thread");
176
logfile_thread(void *arg)
180
char **begin, **list;
184
"Log file removal thread: %lu", (u_long)pthread_self());
186
/* Check once every 5 minutes. */
187
for (;; sleep(300)) {
188
/* Get the list of log files. */
190
dbenv->log_archive(dbenv, &list, DB_ARCH_ABS)) != 0) {
191
dbenv->err(dbenv, ret, "DB_ENV->log_archive");
195
/* Remove the log files. */
197
for (begin = list; *list != NULL; ++list)
198
if ((ret = remove(*list)) != 0) {
200
ret, "remove %s", *list);
210
log_archlist(DB_ENV *dbenv)
213
char **begin, **list;
215
/* Get the list of database files. */
216
if ((ret = dbenv->log_archive(dbenv,
217
&list, DB_ARCH_ABS | DB_ARCH_DATA)) != 0) {
218
dbenv->err(dbenv, ret, "DB_ENV->log_archive: DB_ARCH_DATA");
222
for (begin = list; *list != NULL; ++list)
223
printf("database file: %s\n", *list);
227
/* Get the list of log files. */
228
if ((ret = dbenv->log_archive(dbenv,
229
&list, DB_ARCH_ABS | DB_ARCH_LOG)) != 0) {
230
dbenv->err(dbenv, ret, "DB_ENV->log_archive: DB_ARCH_LOG");
234
for (begin = list; *list != NULL; ++list)
235
printf("log file: %s\n", *list);
241
db_open(DB_ENV *dbenv, DB **dbp, char *name, int dups)
246
/* Create the database handle. */
247
if ((ret = db_create(&db, dbenv, 0)) != 0) {
248
dbenv->err(dbenv, ret, "db_create");
252
/* Optionally, turn on duplicate data items. */
253
if (dups && (ret = db->set_flags(db, DB_DUP)) != 0) {
254
dbenv->err(dbenv, ret, "db->set_flags: DB_DUP");
259
* Open a database in the environment:
260
* create if it doesn't exist
261
* free-threaded handle
262
* read/write owner only
264
if ((ret = db->open(db, NULL, name, NULL,
265
DB_BTREE, DB_CREATE | DB_THREAD, S_IRUSR | S_IWUSR)) != 0) {
266
dbenv->err(dbenv, ret, "db->open: %s", name);
274
add_fruit(DB_ENV *dbenv, DB *db, char *fruit, char *name)
280
/* Initialization. */
281
memset(&key, 0, sizeof(key));
282
memset(&data, 0, sizeof(data));
284
key.size = strlen(fruit);
286
data.size = strlen(name);
289
/* Begin the transaction. */
290
if ((ret = dbenv->txn_begin(dbenv, NULL, &tid, 0)) != 0) {
291
dbenv->err(dbenv, ret, "DB_ENV->txn_begin");
295
/* Store the value. */
296
switch (ret = db->put(db, tid, &key, &data, 0)) {
298
/* Success: commit the change. */
299
if ((ret = tid->commit(tid, 0)) != 0) {
300
dbenv->err(dbenv, ret, "DB_TXN->commit");
304
case DB_LOCK_DEADLOCK:
305
/* Deadlock: retry the operation. */
306
if ((ret = tid->abort(tid)) != 0) {
307
dbenv->err(dbenv, ret, "DB_TXN->abort");
312
/* Error: run recovery. */
313
dbenv->err(dbenv, ret, "dbc->put: %s/%s", fruit, name);
320
add_color(DB_ENV *dbenv, DB *dbp, char *color, int increment)
327
/* Initialization. */
328
memset(&key, 0, sizeof(key));
330
key.size = strlen(color);
331
memset(&data, 0, sizeof(data));
332
data.flags = DB_DBT_MALLOC;
335
/* Begin the transaction. */
336
if ((ret = dbenv->txn_begin(dbenv, NULL, &tid, 0)) != 0) {
337
dbenv->err(dbenv, ret, "DB_ENV->txn_begin");
342
* Get the key. If it exists, we increment the value. If it
343
* doesn't exist, we create it.
345
switch (ret = dbp->get(dbp, tid, &key, &data, 0)) {
347
original = atoi(data.data);
349
case DB_LOCK_DEADLOCK:
350
/* Deadlock: retry the operation. */
351
if ((ret = tid->abort(tid)) != 0) {
352
dbenv->err(dbenv, ret, "DB_TXN->abort");
360
/* Error: run recovery. */
362
dbenv, ret, "dbc->get: %s/%d", color, increment);
365
if (data.data != NULL)
368
/* Create the new data item. */
369
(void)snprintf(buf, sizeof(buf), "%d", original + increment);
371
data.size = strlen(buf) + 1;
373
/* Store the new value. */
374
switch (ret = dbp->put(dbp, tid, &key, &data, 0)) {
376
/* Success: commit the change. */
377
if ((ret = tid->commit(tid, 0)) != 0) {
378
dbenv->err(dbenv, ret, "DB_TXN->commit");
382
case DB_LOCK_DEADLOCK:
383
/* Deadlock: retry the operation. */
384
if ((ret = tid->abort(tid)) != 0) {
385
dbenv->err(dbenv, ret, "DB_TXN->abort");
390
/* Error: run recovery. */
392
dbenv, ret, "dbc->put: %s/%d", color, increment);
399
add_cat(DB_ENV *dbenv, DB *db, char *name, ...)
408
/* Initialization. */
409
memset(&key, 0, sizeof(key));
410
memset(&data, 0, sizeof(data));
412
key.size = strlen(name);
414
retry: /* Begin the transaction. */
415
if ((ret = dbenv->txn_begin(dbenv, NULL, &tid, 0)) != 0) {
416
dbenv->err(dbenv, ret, "DB_ENV->txn_begin");
420
/* Delete any previously existing item. */
421
switch (ret = db->del(db, tid, &key, 0)) {
425
case DB_LOCK_DEADLOCK:
426
/* Deadlock: retry the operation. */
427
if ((ret = tid->abort(tid)) != 0) {
428
dbenv->err(dbenv, ret, "DB_TXN->abort");
433
dbenv->err(dbenv, ret, "db->del: %s", name);
437
/* Create a cursor. */
438
if ((ret = db->cursor(db, tid, &dbc, 0)) != 0) {
439
dbenv->err(dbenv, ret, "db->cursor");
443
/* Append the items, in order. */
445
while ((s = va_arg(ap, char *)) != NULL) {
447
data.size = strlen(s);
448
switch (ret = dbc->c_put(dbc, &key, &data, DB_KEYLAST)) {
451
case DB_LOCK_DEADLOCK:
454
/* Deadlock: retry the operation. */
455
if ((ret = dbc->c_close(dbc)) != 0) {
457
dbenv, ret, "dbc->c_close");
460
if ((ret = tid->abort(tid)) != 0) {
461
dbenv->err(dbenv, ret, "DB_TXN->abort");
466
/* Error: run recovery. */
467
dbenv->err(dbenv, ret, "dbc->put: %s/%s", name, s);
473
/* Success: commit the change. */
474
if ((ret = dbc->c_close(dbc)) != 0) {
475
dbenv->err(dbenv, ret, "dbc->c_close");
478
if ((ret = tid->commit(tid, 0)) != 0) {
479
dbenv->err(dbenv, ret, "DB_TXN->commit");
487
(void)fprintf(stderr, "usage: txnapp\n");