1 /* trail.c : backing out of aborted Berkeley DB transactions
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
12 * http://www.apache.org/licenses/LICENSE-2.0
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
20 * ====================================================================
24 #include "svn_private_config.h"
26 #include <apr_pools.h>
27 #include "svn_pools.h"
31 #include "bdb/bdb-err.h"
32 #include "bdb/bdb_compat.h"
34 #include "../libsvn_fs/fs-loader.h"
37 #if defined(SVN_FS__TRAIL_DEBUG)
41 struct trail_debug_t *prev;
47 svn_fs_base__trail_debug(trail_t *trail, const char *table, const char *op)
49 struct trail_debug_t *trail_debug;
51 trail_debug = apr_palloc(trail->pool, sizeof(*trail_debug));
52 trail_debug->prev = trail->trail_debug;
53 trail_debug->table = table;
55 trail->trail_debug = trail_debug;
59 print_trail_debug(trail_t *trail,
60 const char *txn_body_fn_name,
61 const char *filename, int line)
63 struct trail_debug_t *trail_debug;
65 fprintf(stderr, "(%s, %s, %u, %u): ",
66 txn_body_fn_name, filename, line, trail->db_txn ? 1 : 0);
68 trail_debug = trail->trail_debug;
71 fprintf(stderr, "(%s, %s) ", trail_debug->table, trail_debug->op);
72 trail_debug = trail_debug->prev;
74 fprintf(stderr, "\n");
77 #define print_trail_debug(trail, txn_body_fn_name, filename, line)
78 #endif /* defined(SVN_FS__TRAIL_DEBUG) */
82 begin_trail(trail_t **trail_p,
84 svn_boolean_t use_txn,
87 base_fs_data_t *bfd = fs->fsap_data;
88 trail_t *trail = apr_pcalloc(pool, sizeof(*trail));
90 trail->pool = svn_pool_create(pool);
95 If we're already inside a trail operation, abort() -- this is
96 a coding problem (and will likely hang the repository anyway). */
97 SVN_ERR_ASSERT(! bfd->in_txn_trail);
99 SVN_ERR(BDB_WRAP(fs, N_("beginning Berkeley DB transaction"),
100 bfd->bdb->env->txn_begin(bfd->bdb->env, 0,
101 &trail->db_txn, 0)));
102 bfd->in_txn_trail = TRUE;
106 trail->db_txn = NULL;
115 abort_trail(trail_t *trail)
117 svn_fs_t *fs = trail->fs;
118 base_fs_data_t *bfd = fs->fsap_data;
123 We have to reset the in_txn_trail flag *before* calling
124 DB_TXN->abort(). If we did it the other way around, the next
125 call to begin_trail() (e.g., as part of a txn retry) would
126 cause an abort, even though there's strictly speaking no
127 programming error involved (see comment [*] above).
129 In any case, if aborting the txn fails, restarting it will
130 most likely fail for the same reason, and so it's better to
131 see the returned error than to abort. An obvious example is
132 when DB_TXN->abort() returns DB_RUNRECOVERY. */
133 bfd->in_txn_trail = FALSE;
134 SVN_ERR(BDB_WRAP(fs, N_("aborting Berkeley DB transaction"),
135 trail->db_txn->abort(trail->db_txn)));
137 svn_pool_destroy(trail->pool);
144 commit_trail(trail_t *trail)
147 svn_fs_t *fs = trail->fs;
148 base_fs_data_t *bfd = fs->fsap_data;
150 /* According to the example in the Berkeley DB manual, txn_commit
151 doesn't return DB_LOCK_DEADLOCK --- all deadlocks are reported
155 /* See comment [**] in abort_trail() above.
156 An error during txn commit will abort the transaction anyway. */
157 bfd->in_txn_trail = FALSE;
158 SVN_ERR(BDB_WRAP(fs, N_("committing Berkeley DB transaction"),
159 trail->db_txn->commit(trail->db_txn, 0)));
162 /* Do a checkpoint here, if enough has gone on.
163 The checkpoint parameters below are pretty arbitrary. Perhaps
164 there should be an svn_fs_berkeley_mumble function to set them. */
165 db_err = bfd->bdb->env->txn_checkpoint(bfd->bdb->env, 1024, 5, 0);
167 /* Pre-4.1 Berkeley documentation says:
169 The DB_ENV->txn_checkpoint function returns a non-zero error
170 value on failure, 0 on success, and returns DB_INCOMPLETE if
171 there were pages that needed to be written to complete the
172 checkpoint but that DB_ENV->memp_sync was unable to write
175 It's safe to ignore DB_INCOMPLETE if we get it while
176 checkpointing. (Post-4.1 Berkeley doesn't have DB_INCOMPLETE
177 anymore, so it's not an issue there.) */
180 #if SVN_BDB_HAS_DB_INCOMPLETE
181 if (db_err != DB_INCOMPLETE)
182 #endif /* SVN_BDB_HAS_DB_INCOMPLETE */
184 return svn_fs_bdb__wrap_db
185 (fs, "checkpointing after Berkeley DB transaction", db_err);
194 do_retry(svn_fs_t *fs,
195 svn_error_t *(*txn_body)(void *baton, trail_t *trail),
197 svn_boolean_t use_txn,
198 svn_boolean_t destroy_trail_pool,
200 const char *txn_body_fn_name,
201 const char *filename,
207 svn_error_t *svn_err, *err;
208 svn_boolean_t deadlocked = FALSE;
210 SVN_ERR(begin_trail(&trail, fs, use_txn, pool));
212 /* Do the body of the transaction. */
213 svn_err = (*txn_body)(baton, trail);
217 /* The transaction succeeded! Commit it. */
218 SVN_ERR(commit_trail(trail));
221 print_trail_debug(trail, txn_body_fn_name, filename, line);
223 /* If our caller doesn't want us to keep trail memory
224 around, destroy our subpool. */
225 if (destroy_trail_pool)
226 svn_pool_destroy(trail->pool);
231 /* Search for a deadlock error on the stack. */
232 for (err = svn_err; err; err = err->child)
233 if (err->apr_err == SVN_ERR_FS_BERKELEY_DB_DEADLOCK)
236 /* Is this a real error, or do we just need to retry? */
239 /* Ignore any error returns. The first error is more valuable. */
240 svn_error_clear(abort_trail(trail));
244 svn_error_clear(svn_err);
246 /* We deadlocked. Abort the transaction, and try again. */
247 SVN_ERR(abort_trail(trail));
253 svn_fs_base__retry_debug(svn_fs_t *fs,
254 svn_error_t *(*txn_body)(void *baton, trail_t *trail),
256 svn_boolean_t destroy_trail_pool,
258 const char *txn_body_fn_name,
259 const char *filename,
262 return do_retry(fs, txn_body, baton, TRUE, destroy_trail_pool, pool,
263 txn_body_fn_name, filename, line);
267 #if defined(SVN_FS__TRAIL_DEBUG)
268 #undef svn_fs_base__retry_txn
272 svn_fs_base__retry_txn(svn_fs_t *fs,
273 svn_error_t *(*txn_body)(void *baton, trail_t *trail),
275 svn_boolean_t destroy_trail_pool,
278 return do_retry(fs, txn_body, baton, TRUE, destroy_trail_pool, pool,
284 svn_fs_base__retry(svn_fs_t *fs,
285 svn_error_t *(*txn_body)(void *baton, trail_t *trail),
287 svn_boolean_t destroy_trail_pool,
290 return do_retry(fs, txn_body, baton, FALSE, destroy_trail_pool, pool,