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 * ====================================================================
21 #include <apr_pools.h>
22 #include "svn_pools.h"
26 #include "bdb/bdb-err.h"
27 #include "bdb/bdb_compat.h"
29 #include "../libsvn_fs/fs-loader.h"
32 #if defined(SVN_FS__TRAIL_DEBUG)
36 struct trail_debug_t
*prev
;
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
;
50 trail
->trail_debug
= trail_debug
;
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
;
66 fprintf(stderr
, "(%s, %s) ", trail_debug
->table
, trail_debug
->op
);
67 trail_debug
= trail_debug
->prev
;
69 fprintf(stderr
, "\n");
72 #define print_trail_debug(trail, txn_body_fn_name, filename, line)
73 #endif /* defined(SVN_FS__TRAIL_DEBUG) */
77 begin_trail(trail_t
**trail_p
,
79 svn_boolean_t use_txn
,
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
);
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
)
95 SVN_ERR(BDB_WRAP(fs
, "beginning Berkeley DB transaction",
96 bfd
->bdb
->env
->txn_begin(bfd
->bdb
->env
, 0,
98 bfd
->in_txn_trail
= TRUE
;
102 trail
->db_txn
= NULL
;
111 abort_trail(trail_t
*trail
)
113 svn_fs_t
*fs
= trail
->fs
;
114 base_fs_data_t
*bfd
= fs
->fsap_data
;
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
);
140 commit_trail(trail_t
*trail
)
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
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
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.) */
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
);
190 do_retry(svn_fs_t
*fs
,
191 svn_error_t
*(*txn_body
)(void *baton
, trail_t
*trail
),
193 svn_boolean_t use_txn
,
195 const char *txn_body_fn_name
,
196 const char *filename
,
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
);
212 /* The transaction succeeded! Commit it. */
213 SVN_ERR(commit_trail(trail
));
216 print_trail_debug(trail
, txn_body_fn_name
, filename
, line
);
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
)
226 /* Is this a real error, or do we just need to retry? */
229 /* Ignore any error returns. The first error is more valuable. */
230 svn_error_clear(abort_trail(trail
));
234 svn_error_clear(svn_err
);
236 /* We deadlocked. Abort the transaction, and try again. */
237 SVN_ERR(abort_trail(trail
));
243 svn_fs_base__retry_debug(svn_fs_t
*fs
,
244 svn_error_t
*(*txn_body
)(void *baton
, trail_t
*trail
),
247 const char *txn_body_fn_name
,
248 const char *filename
,
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
261 svn_fs_base__retry_txn(svn_fs_t
*fs
,
262 svn_error_t
*(*txn_body
)(void *baton
, trail_t
*trail
),
266 return do_retry(fs
, txn_body
, baton
, TRUE
, pool
,
272 svn_fs_base__retry(svn_fs_t
*fs
,
273 svn_error_t
*(*txn_body
)(void *baton
, trail_t
*trail
),
277 return do_retry(fs
, txn_body
, baton
, FALSE
, pool
,