Mark many merge tests as skip-against-old-server.
[svn.git] / subversion / libsvn_fs_base / trail.c
blob4e9f60c021e4fb10af73b41058d382ffdebcc2ec
1 /* trail.c : backing out of aborted Berkeley DB transactions
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 #define APU_WANT_DB
19 #include <apu_want.h>
21 #include <apr_pools.h>
22 #include "svn_pools.h"
23 #include "svn_fs.h"
24 #include "fs.h"
25 #include "err.h"
26 #include "bdb/bdb-err.h"
27 #include "bdb/bdb_compat.h"
28 #include "trail.h"
29 #include "../libsvn_fs/fs-loader.h"
32 #if defined(SVN_FS__TRAIL_DEBUG)
34 struct trail_debug_t
36 struct trail_debug_t *prev;
37 const char *table;
38 const char *op;
41 void
42 svn_fs_base__trail_debug(trail_t *trail, const char *table, const char *op)
44 struct trail_debug_t *trail_debug;
46 trail_debug = apr_palloc(trail->pool, sizeof(*trail_debug));
47 trail_debug->prev = trail->trail_debug;
48 trail_debug->table = table;
49 trail_debug->op = op;
50 trail->trail_debug = trail_debug;
53 static void
54 print_trail_debug(trail_t *trail,
55 const char *txn_body_fn_name,
56 const char *filename, int line)
58 struct trail_debug_t *trail_debug;
60 fprintf(stderr, "(%s, %s, %u, %u): ",
61 txn_body_fn_name, filename, line, trail->db_txn ? 1 : 0);
63 trail_debug = trail->trail_debug;
64 while (trail_debug)
66 fprintf(stderr, "(%s, %s) ", trail_debug->table, trail_debug->op);
67 trail_debug = trail_debug->prev;
69 fprintf(stderr, "\n");
71 #else
72 #define print_trail_debug(trail, txn_body_fn_name, filename, line)
73 #endif /* defined(SVN_FS__TRAIL_DEBUG) */
76 static svn_error_t *
77 begin_trail(trail_t **trail_p,
78 svn_fs_t *fs,
79 svn_boolean_t use_txn,
80 apr_pool_t *pool)
82 base_fs_data_t *bfd = fs->fsap_data;
83 trail_t *trail = apr_pcalloc(pool, sizeof(*trail));
85 trail->pool = svn_pool_create(pool);
86 trail->fs = fs;
87 if (use_txn)
89 /* [*]
90 If we're already inside a trail operation, abort() -- this is
91 a coding problem (and will likely hang the repository anyway). */
92 if (bfd->in_txn_trail)
93 abort();
95 SVN_ERR(BDB_WRAP(fs, "beginning Berkeley DB transaction",
96 bfd->bdb->env->txn_begin(bfd->bdb->env, 0,
97 &trail->db_txn, 0)));
98 bfd->in_txn_trail = TRUE;
100 else
102 trail->db_txn = NULL;
105 *trail_p = trail;
106 return SVN_NO_ERROR;
110 static svn_error_t *
111 abort_trail(trail_t *trail)
113 svn_fs_t *fs = trail->fs;
114 base_fs_data_t *bfd = fs->fsap_data;
116 if (trail->db_txn)
118 /* [**]
119 We have to reset the in_txn_trail flag *before* calling
120 DB_TXN->abort(). If we did it the other way around, the next
121 call to begin_trail() (e.g., as part of a txn retry) would
122 cause an abort, even though there's strictly speaking no
123 programming error involved (see comment [*] above).
125 In any case, if aborting the txn fails, restarting it will
126 most likely fail for the same reason, and so it's better to
127 see the returned error than to abort. An obvious example is
128 when DB_TXN->abort() returns DB_RUNRECOVERY. */
129 bfd->in_txn_trail = FALSE;
130 SVN_ERR(BDB_WRAP(fs, "aborting Berkeley DB transaction",
131 trail->db_txn->abort(trail->db_txn)));
133 svn_pool_destroy(trail->pool);
135 return SVN_NO_ERROR;
139 static svn_error_t *
140 commit_trail(trail_t *trail)
142 int db_err;
143 svn_fs_t *fs = trail->fs;
144 base_fs_data_t *bfd = fs->fsap_data;
146 /* According to the example in the Berkeley DB manual, txn_commit
147 doesn't return DB_LOCK_DEADLOCK --- all deadlocks are reported
148 earlier. */
149 if (trail->db_txn)
151 /* See comment [**] in abort_trail() above.
152 An error during txn commit will abort the transaction anyway. */
153 bfd->in_txn_trail = FALSE;
154 SVN_ERR(BDB_WRAP(fs, "committing Berkeley DB transaction",
155 trail->db_txn->commit(trail->db_txn, 0)));
158 /* Do a checkpoint here, if enough has gone on.
159 The checkpoint parameters below are pretty arbitrary. Perhaps
160 there should be an svn_fs_berkeley_mumble function to set them. */
161 db_err = bfd->bdb->env->txn_checkpoint(bfd->bdb->env, 1024, 5, 0);
163 /* Pre-4.1 Berkeley documentation says:
165 The DB_ENV->txn_checkpoint function returns a non-zero error
166 value on failure, 0 on success, and returns DB_INCOMPLETE if
167 there were pages that needed to be written to complete the
168 checkpoint but that DB_ENV->memp_sync was unable to write
169 immediately.
171 It's safe to ignore DB_INCOMPLETE if we get it while
172 checkpointing. (Post-4.1 Berkeley doesn't have DB_INCOMPLETE
173 anymore, so it's not an issue there.) */
174 if (db_err)
176 #if SVN_BDB_HAS_DB_INCOMPLETE
177 if (db_err != DB_INCOMPLETE)
178 #endif /* SVN_BDB_HAS_DB_INCOMPLETE */
180 return svn_fs_bdb__wrap_db
181 (fs, "checkpointing after Berkeley DB transaction", db_err);
185 return SVN_NO_ERROR;
189 static svn_error_t *
190 do_retry(svn_fs_t *fs,
191 svn_error_t *(*txn_body)(void *baton, trail_t *trail),
192 void *baton,
193 svn_boolean_t use_txn,
194 apr_pool_t *pool,
195 const char *txn_body_fn_name,
196 const char *filename,
197 int line)
199 for (;;)
201 trail_t *trail;
202 svn_error_t *svn_err, *err;
203 svn_boolean_t deadlocked = FALSE;
205 SVN_ERR(begin_trail(&trail, fs, use_txn, pool));
207 /* Do the body of the transaction. */
208 svn_err = (*txn_body)(baton, trail);
210 if (! svn_err)
212 /* The transaction succeeded! Commit it. */
213 SVN_ERR(commit_trail(trail));
215 if (use_txn)
216 print_trail_debug(trail, txn_body_fn_name, filename, line);
218 return SVN_NO_ERROR;
221 /* Search for a deadlock error on the stack. */
222 for (err = svn_err; err; err = err->child)
223 if (err->apr_err == SVN_ERR_FS_BERKELEY_DB_DEADLOCK)
224 deadlocked = TRUE;
226 /* Is this a real error, or do we just need to retry? */
227 if (! deadlocked)
229 /* Ignore any error returns. The first error is more valuable. */
230 svn_error_clear(abort_trail(trail));
231 return svn_err;
234 svn_error_clear(svn_err);
236 /* We deadlocked. Abort the transaction, and try again. */
237 SVN_ERR(abort_trail(trail));
242 svn_error_t *
243 svn_fs_base__retry_debug(svn_fs_t *fs,
244 svn_error_t *(*txn_body)(void *baton, trail_t *trail),
245 void *baton,
246 apr_pool_t *pool,
247 const char *txn_body_fn_name,
248 const char *filename,
249 int line)
251 return do_retry(fs, txn_body, baton, TRUE, pool,
252 txn_body_fn_name, filename, line);
256 #if defined(SVN_FS__TRAIL_DEBUG)
257 #undef svn_fs_base__retry_txn
258 #endif
260 svn_error_t *
261 svn_fs_base__retry_txn(svn_fs_t *fs,
262 svn_error_t *(*txn_body)(void *baton, trail_t *trail),
263 void *baton,
264 apr_pool_t *pool)
266 return do_retry(fs, txn_body, baton, TRUE, pool,
267 "unknown", "", 0);
271 svn_error_t *
272 svn_fs_base__retry(svn_fs_t *fs,
273 svn_error_t *(*txn_body)(void *baton, trail_t *trail),
274 void *baton,
275 apr_pool_t *pool)
277 return do_retry(fs, txn_body, baton, FALSE, pool,
278 NULL, NULL, 0);