]> CyberLeo.Net >> Repos - FreeBSD/releng/10.0.git/blob - contrib/subversion/subversion/libsvn_fs_base/trail.h
- Copy stable/10 (r259064) to releng/10.0 as part of the
[FreeBSD/releng/10.0.git] / contrib / subversion / subversion / libsvn_fs_base / trail.h
1 /* trail.h : internal interface to backing out of aborted Berkeley DB txns
2  *
3  * ====================================================================
4  *    Licensed to the Apache Software Foundation (ASF) under one
5  *    or more contributor license agreements.  See the NOTICE file
6  *    distributed with this work for additional information
7  *    regarding copyright ownership.  The ASF licenses this file
8  *    to you under the Apache License, Version 2.0 (the
9  *    "License"); you may not use this file except in compliance
10  *    with the License.  You may obtain a copy of the License at
11  *
12  *      http://www.apache.org/licenses/LICENSE-2.0
13  *
14  *    Unless required by applicable law or agreed to in writing,
15  *    software distributed under the License is distributed on an
16  *    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17  *    KIND, either express or implied.  See the License for the
18  *    specific language governing permissions and limitations
19  *    under the License.
20  * ====================================================================
21  */
22
23 #ifndef SVN_LIBSVN_FS_TRAIL_H
24 #define SVN_LIBSVN_FS_TRAIL_H
25
26 #define SVN_WANT_BDB
27 #include "svn_private_config.h"
28
29 #include <apr_pools.h>
30 #include "svn_fs.h"
31 #include "fs.h"
32
33 #ifdef __cplusplus
34 extern "C" {
35 #endif /* __cplusplus */
36
37 \f
38 /* "How do I get a trail object?  All these functions in the
39    filesystem expect them, and I can't find a function that returns
40    one."
41
42    Well, there isn't a function that returns a trail.  All trails come
43    from svn_fs_base__retry_txn.  Here's how to use that:
44
45    When using Berkeley DB transactions to protect the integrity of a
46    database, there are several things you need to keep in mind:
47
48    - Any Berkeley DB operation you perform as part of a Berkeley DB
49      transaction may return DB_LOCK_DEADLOCK, meaning that your
50      operation interferes with some other transaction in progress.
51      When this happens, you must abort the transaction, which undoes
52      all the changes you've made so far, and try it again.  So every
53      piece of code you ever write to bang on the DB needs to be
54      wrapped up in a retry loop.
55
56    - If, while you're doing your database operations, you also change
57      some in-memory data structures, then you may want to revert those
58      changes if the transaction deadlocks and needs to be retried.
59
60    - If you get a `real' error (i.e., something other than
61      DB_LOCK_DEADLOCK), you must abort your DB transaction, to release
62      its locks and return the database to its previous state.
63      Similarly, you may want to unroll some changes you've made to
64      in-memory data structures.
65
66    - Since a transaction insulates you from database changes made by
67      other processes, it's often possible to cache information about
68      database contents while the transaction lasts.  However, this
69      cache may become stale once your transaction is over.  So you may
70      need to clear your cache once the transaction completes, either
71      successfully or unsuccessfully.
72
73    The `svn_fs_base__retry_txn' function and its friends help you manage
74    some of that, in one nice package.
75
76    To use it, write your code in a function like this:
77
78        static svn_error_t *
79        txn_body_do_my_thing (void *baton,
80                              trail_t *trail)
81        {
82          ...
83          Do everything which needs to be protected by a Berkeley DB
84          transaction here.  Use TRAIL->db_txn as your Berkeley DB
85          transaction, and do your allocation in TRAIL->pool.  Pass
86          TRAIL on through to any functions which require one.
87
88          If a Berkeley DB operation returns DB_LOCK_DEADLOCK, just
89          return that using the normal Subversion error mechanism
90          (using DB_ERR, for example); don't write a retry loop.  If you
91          encounter some other kind of error, return it in the normal
92          fashion.
93          ...
94        }
95
96    Now, call svn_fs_base__retry_txn, passing a pointer to your function as
97    an argument:
98
99        err = svn_fs_base__retry_txn (fs, txn_body_do_my_thing, baton, pool);
100
101    This will simply invoke your function `txn_body_do_my_thing',
102    passing BATON through unchanged, and providing a fresh TRAIL
103    object, containing a pointer to the filesystem object, a Berkeley
104    DB transaction and an APR pool -- a subpool of POOL -- you should
105    use.
106
107    If your function returns a Subversion error wrapping a Berkeley DB
108    DB_LOCK_DEADLOCK error, `svn_fs_base__retry_txn' will abort the trail's
109    Berkeley DB transaction for you (thus undoing any database changes
110    you've made), free the trail's subpool (thus undoing any allocation
111    you may have done), and try the whole thing again with a new trail,
112    containing a new Berkeley DB transaction and pool.
113
114    If your function returns any other kind of Subversion error,
115    `svn_fs_base__retry_txn' will abort the trail's Berkeley DB transaction,
116    free the subpool, and return your error to its caller.
117
118    If, heavens forbid, your function actually succeeds, returning
119    SVN_NO_ERROR, `svn_fs_base__retry_txn' commits the trail's Berkeley DB
120    transaction, thus making your DB changes permanent, leaves the
121    trail's pool alone so all the objects it contains are still
122    around (unless you request otherwise), and returns SVN_NO_ERROR.
123
124
125    Keep the amount of work done in a trail small. C-Mike Pilato said to me:
126
127    I want to draw your attention to something that you may or may not realize
128    about designing for the BDB backend.  The 'trail' objects are (generally)
129    representative of Berkeley DB transactions -- that part I'm sure you know.
130    But you might not realize the value of keeping transactions as small as
131    possible.  Berkeley DB will accumulate locks (which I believe are
132    page-level, not as tight as row-level like you might hope) over the course
133    of a transaction, releasing those locks only at transaction commit/abort.
134    Berkeley DB backends are configured to have a maximum number of locks and
135    lockers allowed, and it's easier than you might think to hit the max-locks
136    thresholds (especially under high concurrency) and see an error (typically a
137    "Cannot allocate memory") result from that.
138
139    For example, in [a loop] you are writing a bunch of rows to the
140    `changes' table.  Could be 10.  Could be 100,000.  100,000 writes and
141    associated locks might be a problem or it might not.  But I use it as a way
142    to encourage you to think about reducing the amount of work you spend in any
143    one trail [...].
144 */
145
146 struct trail_t
147 {
148   /* A Berkeley DB transaction.  */
149   DB_TXN *db_txn;
150
151   /* The filesystem object with which this trail is associated. */
152   svn_fs_t *fs;
153
154   /* A pool to allocate things in as part of that transaction --- a
155      subpool of the one passed to `begin_trail'.  We destroy this pool
156      if we abort the transaction, and leave it around otherwise.  */
157   apr_pool_t *pool;
158
159 #if defined(SVN_FS__TRAIL_DEBUG)
160   struct trail_debug_t *trail_debug;
161 #endif
162 };
163 typedef struct trail_t trail_t;
164
165
166 /* Try a Berkeley DB transaction repeatedly until it doesn't deadlock.
167
168    That is:
169    - Begin a new Berkeley DB transaction, DB_TXN, in the filesystem FS.
170    - Allocate a subpool of POOL, TXN_POOL.
171    - Start a new trail, TRAIL, pointing to DB_TXN and TXN_POOL.
172    - Apply TXN_BODY to BATON and TRAIL.  TXN_BODY should try to do
173      some series of DB operations which needs to be atomic, using
174      TRAIL->db_txn as the transaction, and TRAIL->pool for allocation.
175      If a DB operation deadlocks, or if any other kind of error
176      happens, TXN_BODY should simply return with an appropriate
177      svn_error_t, E.
178    - If TXN_BODY returns SVN_NO_ERROR, then commit the transaction,
179      run any completion functions, and return SVN_NO_ERROR.  Do *not*
180      free TXN_POOL (unless DESTROY_TRAIL_POOL is set).
181    - If E is a Berkeley DB error indicating that a deadlock occurred,
182      abort the DB transaction and free TXN_POOL.  Then retry the whole
183      thing from the top.
184    - If E is any other kind of error, free TXN_POOL and return E.
185
186    One benefit of using this function is that it makes it easy to
187    ensure that whatever transactions a filesystem function starts, it
188    either aborts or commits before it returns.  If we don't somehow
189    complete all our transactions, later operations could deadlock.  */
190 svn_error_t *
191 svn_fs_base__retry_txn(svn_fs_t *fs,
192                        svn_error_t *(*txn_body)(void *baton,
193                                                 trail_t *trail),
194                        void *baton,
195                        svn_boolean_t destroy_trail_pool,
196                        apr_pool_t *pool);
197
198 svn_error_t *
199 svn_fs_base__retry_debug(svn_fs_t *fs,
200                          svn_error_t *(*txn_body)(void *baton,
201                                                   trail_t *trail),
202                          void *baton,
203                          svn_boolean_t destroy_trail_pool,
204                          apr_pool_t *pool,
205                          const char *txn_body_fn_name,
206                          const char *filename,
207                          int line);
208
209 #if defined(SVN_FS__TRAIL_DEBUG)
210 #define svn_fs_base__retry_txn(fs, txn_body, baton, destroy, pool) \
211   svn_fs_base__retry_debug(fs, txn_body, baton, destroy, pool,     \
212                            #txn_body, __FILE__, __LINE__)
213 #endif
214
215
216 /* Try an action repeatedly until it doesn't deadlock.  This is
217    exactly like svn_fs_base__retry_txn() (whose documentation you really
218    should read) except that no Berkeley DB transaction is created. */
219 svn_error_t *svn_fs_base__retry(svn_fs_t *fs,
220                                 svn_error_t *(*txn_body)(void *baton,
221                                                          trail_t *trail),
222                                 void *baton,
223                                 svn_boolean_t destroy_trail_pool,
224                                 apr_pool_t *pool);
225
226
227 /* Record that OPeration is being done on TABLE in the TRAIL. */
228 #if defined(SVN_FS__TRAIL_DEBUG)
229 void svn_fs_base__trail_debug(trail_t *trail, const char *table,
230                               const char *op);
231 #else
232 #define svn_fs_base__trail_debug(trail, table, operation)
233 #endif
234
235 #ifdef __cplusplus
236 }
237 #endif /* __cplusplus */
238
239 #endif /* SVN_LIBSVN_FS_TRAIL_H */