1 /* trail.h : internal interface to backing out of aborted Berkeley DB txns
3 * ====================================================================
4 * Copyright (c) 2000-2004 CollabNet. All rights reserved.
6 * This software is licensed as described in the file COPYING, which
7 * you should have received as part of this distribution. The terms
8 * are also available at http://subversion.tigris.org/license-1.html.
9 * If newer versions of this license are posted there, you may use a
10 * newer version instead, at your option.
12 * This software consists of voluntary contributions made by many
13 * individuals. For exact contribution history, see the revision
14 * history and logs, available at http://subversion.tigris.org/.
15 * ====================================================================
18 #ifndef SVN_LIBSVN_FS_TRAIL_H
19 #define SVN_LIBSVN_FS_TRAIL_H
24 #include <apr_pools.h>
30 #endif /* __cplusplus */
33 /* "How do I get a trail object? All these functions in the
34 filesystem expect them, and I can't find a function that returns
37 Well, there isn't a function that returns a trail. All trails come
38 from svn_fs_base__retry_txn. Here's how to use that:
40 When using Berkeley DB transactions to protect the integrity of a
41 database, there are several things you need to keep in mind:
43 - Any Berkeley DB operation you perform as part of a Berkeley DB
44 transaction may return DB_LOCK_DEADLOCK, meaning that your
45 operation interferes with some other transaction in progress.
46 When this happens, you must abort the transaction, which undoes
47 all the changes you've made so far, and try it again. So every
48 piece of code you ever write to bang on the DB needs to be
49 wrapped up in a retry loop.
51 - If, while you're doing your database operations, you also change
52 some in-memory data structures, then you may want to revert those
53 changes if the transaction deadlocks and needs to be retried.
55 - If you get a `real' error (i.e., something other than
56 DB_LOCK_DEADLOCK), you must abort your DB transaction, to release
57 its locks and return the database to its previous state.
58 Similarly, you may want to unroll some changes you've made to
59 in-memory data structures.
61 - Since a transaction insulates you from database changes made by
62 other processes, it's often possible to cache information about
63 database contents while the transaction lasts. However, this
64 cache may become stale once your transaction is over. So you may
65 need to clear your cache once the transaction completes, either
66 successfully or unsuccessfully.
68 The `svn_fs_base__retry_txn' function and its friends help you manage
69 some of that, in one nice package.
71 To use it, write your code in a function like this:
74 txn_body_do_my_thing (void *baton,
78 Do everything which needs to be protected by a Berkeley DB
79 transaction here. Use TRAIL->db_txn as your Berkeley DB
80 transaction, and do your allocation in TRAIL->pool. Pass
81 TRAIL on through to any functions which require one.
83 If a Berkeley DB operation returns DB_LOCK_DEADLOCK, just
84 return that using the normal Subversion error mechanism
85 (using DB_ERR, for example); don't write a retry loop. If you
86 encounter some other kind of error, return it in the normal
91 Now, call svn_fs_base__retry_txn, passing a pointer to your function as
94 err = svn_fs_base__retry_txn (fs, txn_body_do_my_thing, baton, pool);
96 This will simply invoke your function `txn_body_do_my_thing',
97 passing BATON through unchanged, and providing a fresh TRAIL
98 object, containing a pointer to the filesystem object, a Berkeley
99 DB transaction and an APR pool -- a subpool of POOL -- you should
102 If your function returns a Subversion error wrapping a Berkeley DB
103 DB_LOCK_DEADLOCK error, `svn_fs_base__retry_txn' will abort the trail's
104 Berkeley DB transaction for you (thus undoing any database changes
105 you've made), free the trail's subpool (thus undoing any allocation
106 you may have done), and try the whole thing again with a new trail,
107 containing a new Berkeley DB transaction and pool.
109 If your function returns any other kind of Subversion error,
110 `svn_fs_base__retry_txn' will abort the trail's Berkeley DB transaction,
111 free the subpool, and return your error to its caller.
113 If, heavens forbid, your function actually succeeds, returning
114 SVN_NO_ERROR, `svn_fs_base__retry_txn' commits the trail's Berkeley DB
115 transaction, thus making your DB changes permanent, leaves the
116 trail's pool alone, so all the objects it contains are still
117 around, and returns SVN_NO_ERROR. */
121 /* A Berkeley DB transaction. */
124 /* The filesystem object with which this trail is associated. */
127 /* A pool to allocate things in as part of that transaction --- a
128 subpool of the one passed to `begin_trail'. We destroy this pool
129 if we abort the transaction, and leave it around otherwise. */
132 #if defined(SVN_FS__TRAIL_DEBUG)
133 struct trail_debug_t
*trail_debug
;
136 typedef struct trail_t trail_t
;
139 /* Try a Berkeley DB transaction repeatedly until it doesn't deadlock.
142 - Begin a new Berkeley DB transaction, DB_TXN, in the filesystem FS.
143 - Allocate a subpool of POOL, TXN_POOL.
144 - Start a new trail, TRAIL, pointing to DB_TXN and TXN_POOL.
145 - Apply TXN_BODY to BATON and TRAIL. TXN_BODY should try to do
146 some series of DB operations which needs to be atomic, using
147 TRAIL->db_txn as the transaction, and TRAIL->pool for allocation.
148 If a DB operation deadlocks, or if any other kind of error
149 happens, TXN_BODY should simply return with an appropriate
151 - If TXN_BODY returns SVN_NO_ERROR, then commit the transaction,
152 run any completion functions, and return SVN_NO_ERROR. Do *not*
154 - If E is a Berkeley DB error indicating that a deadlock occurred,
155 abort the DB transaction and free TXN_POOL. Then retry the whole
157 - If E is any other kind of error, free TXN_POOL and return E.
159 One benefit of using this function is that it makes it easy to
160 ensure that whatever transactions a filesystem function starts, it
161 either aborts or commits before it returns. If we don't somehow
162 complete all our transactions, later operations could deadlock. */
163 svn_error_t
*svn_fs_base__retry_txn(svn_fs_t
*fs
,
164 svn_error_t
*(*txn_body
)(void *baton
,
170 svn_fs_base__retry_debug(svn_fs_t
*fs
,
171 svn_error_t
*(*txn_body
)(void *baton
,
175 const char *txn_body_fn_name
,
176 const char *filename
,
179 #if defined(SVN_FS__TRAIL_DEBUG)
180 #define svn_fs_base__retry_txn(fs, txn_body, baton, pool) \
181 svn_fs_base__retry_debug(fs, txn_body, baton, pool, #txn_body,
186 /* Try an action repeatedly until it doesn't deadlock. This is
187 exactly like svn_fs_base__retry_txn() (whose documentation you really
188 should read) except that no Berkeley DB transaction is created. */
189 svn_error_t
*svn_fs_base__retry(svn_fs_t
*fs
,
190 svn_error_t
*(*txn_body
)(void *baton
,
196 /* Record that OPeration is being done on TABLE in the TRAIL. */
197 #if defined(SVN_FS__TRAIL_DEBUG)
198 void svn_fs_base__trail_debug(trail_t
*trail
, const char *table
,
201 #define svn_fs_base__trail_debug(trail, table, operation)
206 #endif /* __cplusplus */
208 #endif /* SVN_LIBSVN_FS_TRAIL_H */