1251881Speter/* trail.c : backing out of aborted Berkeley DB transactions 2251881Speter * 3251881Speter * ==================================================================== 4251881Speter * Licensed to the Apache Software Foundation (ASF) under one 5251881Speter * or more contributor license agreements. See the NOTICE file 6251881Speter * distributed with this work for additional information 7251881Speter * regarding copyright ownership. The ASF licenses this file 8251881Speter * to you under the Apache License, Version 2.0 (the 9251881Speter * "License"); you may not use this file except in compliance 10251881Speter * with the License. You may obtain a copy of the License at 11251881Speter * 12251881Speter * http://www.apache.org/licenses/LICENSE-2.0 13251881Speter * 14251881Speter * Unless required by applicable law or agreed to in writing, 15251881Speter * software distributed under the License is distributed on an 16251881Speter * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 17251881Speter * KIND, either express or implied. See the License for the 18251881Speter * specific language governing permissions and limitations 19251881Speter * under the License. 20251881Speter * ==================================================================== 21251881Speter */ 22251881Speter 23251881Speter#define SVN_WANT_BDB 24251881Speter#include "svn_private_config.h" 25251881Speter 26251881Speter#include <apr_pools.h> 27251881Speter#include "svn_pools.h" 28251881Speter#include "svn_fs.h" 29251881Speter#include "fs.h" 30251881Speter#include "err.h" 31251881Speter#include "bdb/bdb-err.h" 32251881Speter#include "bdb/bdb_compat.h" 33251881Speter#include "trail.h" 34251881Speter#include "../libsvn_fs/fs-loader.h" 35251881Speter 36251881Speter 37251881Speter#if defined(SVN_FS__TRAIL_DEBUG) 38251881Speter 39251881Speterstruct trail_debug_t 40251881Speter{ 41251881Speter struct trail_debug_t *prev; 42251881Speter const char *table; 43251881Speter const char *op; 44251881Speter}; 45251881Speter 46251881Spetervoid 47251881Spetersvn_fs_base__trail_debug(trail_t *trail, const char *table, const char *op) 48251881Speter{ 49251881Speter struct trail_debug_t *trail_debug; 50251881Speter 51251881Speter trail_debug = apr_palloc(trail->pool, sizeof(*trail_debug)); 52251881Speter trail_debug->prev = trail->trail_debug; 53251881Speter trail_debug->table = table; 54251881Speter trail_debug->op = op; 55251881Speter trail->trail_debug = trail_debug; 56251881Speter} 57251881Speter 58251881Speterstatic void 59251881Speterprint_trail_debug(trail_t *trail, 60251881Speter const char *txn_body_fn_name, 61251881Speter const char *filename, int line) 62251881Speter{ 63251881Speter struct trail_debug_t *trail_debug; 64251881Speter 65251881Speter fprintf(stderr, "(%s, %s, %u, %u): ", 66251881Speter txn_body_fn_name, filename, line, trail->db_txn ? 1 : 0); 67251881Speter 68251881Speter trail_debug = trail->trail_debug; 69251881Speter while (trail_debug) 70251881Speter { 71251881Speter fprintf(stderr, "(%s, %s) ", trail_debug->table, trail_debug->op); 72251881Speter trail_debug = trail_debug->prev; 73251881Speter } 74251881Speter fprintf(stderr, "\n"); 75251881Speter} 76251881Speter#else 77251881Speter#define print_trail_debug(trail, txn_body_fn_name, filename, line) 78251881Speter#endif /* defined(SVN_FS__TRAIL_DEBUG) */ 79251881Speter 80251881Speter 81251881Speterstatic svn_error_t * 82251881Speterbegin_trail(trail_t **trail_p, 83251881Speter svn_fs_t *fs, 84251881Speter svn_boolean_t use_txn, 85251881Speter apr_pool_t *pool) 86251881Speter{ 87251881Speter base_fs_data_t *bfd = fs->fsap_data; 88251881Speter trail_t *trail = apr_pcalloc(pool, sizeof(*trail)); 89251881Speter 90251881Speter trail->pool = svn_pool_create(pool); 91251881Speter trail->fs = fs; 92251881Speter if (use_txn) 93251881Speter { 94251881Speter /* [*] 95251881Speter If we're already inside a trail operation, abort() -- this is 96251881Speter a coding problem (and will likely hang the repository anyway). */ 97251881Speter SVN_ERR_ASSERT(! bfd->in_txn_trail); 98251881Speter 99251881Speter SVN_ERR(BDB_WRAP(fs, N_("beginning Berkeley DB transaction"), 100251881Speter bfd->bdb->env->txn_begin(bfd->bdb->env, 0, 101251881Speter &trail->db_txn, 0))); 102251881Speter bfd->in_txn_trail = TRUE; 103251881Speter } 104251881Speter else 105251881Speter { 106251881Speter trail->db_txn = NULL; 107251881Speter } 108251881Speter 109251881Speter *trail_p = trail; 110251881Speter return SVN_NO_ERROR; 111251881Speter} 112251881Speter 113251881Speter 114251881Speterstatic svn_error_t * 115251881Speterabort_trail(trail_t *trail) 116251881Speter{ 117251881Speter svn_fs_t *fs = trail->fs; 118251881Speter base_fs_data_t *bfd = fs->fsap_data; 119251881Speter 120251881Speter if (trail->db_txn) 121251881Speter { 122251881Speter /* [**] 123251881Speter We have to reset the in_txn_trail flag *before* calling 124251881Speter DB_TXN->abort(). If we did it the other way around, the next 125251881Speter call to begin_trail() (e.g., as part of a txn retry) would 126251881Speter cause an abort, even though there's strictly speaking no 127251881Speter programming error involved (see comment [*] above). 128251881Speter 129251881Speter In any case, if aborting the txn fails, restarting it will 130251881Speter most likely fail for the same reason, and so it's better to 131251881Speter see the returned error than to abort. An obvious example is 132251881Speter when DB_TXN->abort() returns DB_RUNRECOVERY. */ 133251881Speter bfd->in_txn_trail = FALSE; 134251881Speter SVN_ERR(BDB_WRAP(fs, N_("aborting Berkeley DB transaction"), 135251881Speter trail->db_txn->abort(trail->db_txn))); 136251881Speter } 137251881Speter svn_pool_destroy(trail->pool); 138251881Speter 139251881Speter return SVN_NO_ERROR; 140251881Speter} 141251881Speter 142251881Speter 143251881Speterstatic svn_error_t * 144251881Spetercommit_trail(trail_t *trail) 145251881Speter{ 146251881Speter int db_err; 147251881Speter svn_fs_t *fs = trail->fs; 148251881Speter base_fs_data_t *bfd = fs->fsap_data; 149251881Speter 150251881Speter /* According to the example in the Berkeley DB manual, txn_commit 151251881Speter doesn't return DB_LOCK_DEADLOCK --- all deadlocks are reported 152251881Speter earlier. */ 153251881Speter if (trail->db_txn) 154251881Speter { 155251881Speter /* See comment [**] in abort_trail() above. 156251881Speter An error during txn commit will abort the transaction anyway. */ 157251881Speter bfd->in_txn_trail = FALSE; 158251881Speter SVN_ERR(BDB_WRAP(fs, N_("committing Berkeley DB transaction"), 159251881Speter trail->db_txn->commit(trail->db_txn, 0))); 160251881Speter } 161251881Speter 162251881Speter /* Do a checkpoint here, if enough has gone on. 163251881Speter The checkpoint parameters below are pretty arbitrary. Perhaps 164251881Speter there should be an svn_fs_berkeley_mumble function to set them. */ 165251881Speter db_err = bfd->bdb->env->txn_checkpoint(bfd->bdb->env, 1024, 5, 0); 166251881Speter 167251881Speter /* Pre-4.1 Berkeley documentation says: 168251881Speter 169251881Speter The DB_ENV->txn_checkpoint function returns a non-zero error 170251881Speter value on failure, 0 on success, and returns DB_INCOMPLETE if 171251881Speter there were pages that needed to be written to complete the 172251881Speter checkpoint but that DB_ENV->memp_sync was unable to write 173251881Speter immediately. 174251881Speter 175251881Speter It's safe to ignore DB_INCOMPLETE if we get it while 176251881Speter checkpointing. (Post-4.1 Berkeley doesn't have DB_INCOMPLETE 177251881Speter anymore, so it's not an issue there.) */ 178251881Speter if (db_err) 179251881Speter { 180251881Speter#if SVN_BDB_HAS_DB_INCOMPLETE 181251881Speter if (db_err != DB_INCOMPLETE) 182251881Speter#endif /* SVN_BDB_HAS_DB_INCOMPLETE */ 183251881Speter { 184251881Speter return svn_fs_bdb__wrap_db 185251881Speter (fs, "checkpointing after Berkeley DB transaction", db_err); 186251881Speter } 187251881Speter } 188251881Speter 189251881Speter return SVN_NO_ERROR; 190251881Speter} 191251881Speter 192251881Speter 193251881Speterstatic svn_error_t * 194251881Speterdo_retry(svn_fs_t *fs, 195251881Speter svn_error_t *(*txn_body)(void *baton, trail_t *trail), 196251881Speter void *baton, 197251881Speter svn_boolean_t use_txn, 198251881Speter svn_boolean_t destroy_trail_pool, 199251881Speter apr_pool_t *pool, 200251881Speter const char *txn_body_fn_name, 201251881Speter const char *filename, 202251881Speter int line) 203251881Speter{ 204251881Speter for (;;) 205251881Speter { 206251881Speter trail_t *trail; 207251881Speter svn_error_t *svn_err, *err; 208251881Speter svn_boolean_t deadlocked = FALSE; 209251881Speter 210251881Speter SVN_ERR(begin_trail(&trail, fs, use_txn, pool)); 211251881Speter 212251881Speter /* Do the body of the transaction. */ 213251881Speter svn_err = (*txn_body)(baton, trail); 214251881Speter 215251881Speter if (! svn_err) 216251881Speter { 217251881Speter /* The transaction succeeded! Commit it. */ 218251881Speter SVN_ERR(commit_trail(trail)); 219251881Speter 220251881Speter if (use_txn) 221251881Speter print_trail_debug(trail, txn_body_fn_name, filename, line); 222251881Speter 223251881Speter /* If our caller doesn't want us to keep trail memory 224251881Speter around, destroy our subpool. */ 225251881Speter if (destroy_trail_pool) 226251881Speter svn_pool_destroy(trail->pool); 227251881Speter 228251881Speter return SVN_NO_ERROR; 229251881Speter } 230251881Speter 231251881Speter /* Search for a deadlock error on the stack. */ 232251881Speter for (err = svn_err; err; err = err->child) 233251881Speter if (err->apr_err == SVN_ERR_FS_BERKELEY_DB_DEADLOCK) 234251881Speter deadlocked = TRUE; 235251881Speter 236251881Speter /* Is this a real error, or do we just need to retry? */ 237251881Speter if (! deadlocked) 238251881Speter { 239251881Speter /* Ignore any error returns. The first error is more valuable. */ 240251881Speter svn_error_clear(abort_trail(trail)); 241251881Speter return svn_err; 242251881Speter } 243251881Speter 244251881Speter svn_error_clear(svn_err); 245251881Speter 246251881Speter /* We deadlocked. Abort the transaction, and try again. */ 247251881Speter SVN_ERR(abort_trail(trail)); 248251881Speter } 249251881Speter} 250251881Speter 251251881Speter 252251881Spetersvn_error_t * 253251881Spetersvn_fs_base__retry_debug(svn_fs_t *fs, 254251881Speter svn_error_t *(*txn_body)(void *baton, trail_t *trail), 255251881Speter void *baton, 256251881Speter svn_boolean_t destroy_trail_pool, 257251881Speter apr_pool_t *pool, 258251881Speter const char *txn_body_fn_name, 259251881Speter const char *filename, 260251881Speter int line) 261251881Speter{ 262251881Speter return do_retry(fs, txn_body, baton, TRUE, destroy_trail_pool, pool, 263251881Speter txn_body_fn_name, filename, line); 264251881Speter} 265251881Speter 266251881Speter 267251881Speter#if defined(SVN_FS__TRAIL_DEBUG) 268251881Speter#undef svn_fs_base__retry_txn 269251881Speter#endif 270251881Speter 271251881Spetersvn_error_t * 272251881Spetersvn_fs_base__retry_txn(svn_fs_t *fs, 273251881Speter svn_error_t *(*txn_body)(void *baton, trail_t *trail), 274251881Speter void *baton, 275251881Speter svn_boolean_t destroy_trail_pool, 276251881Speter apr_pool_t *pool) 277251881Speter{ 278251881Speter return do_retry(fs, txn_body, baton, TRUE, destroy_trail_pool, pool, 279251881Speter "unknown", "", 0); 280251881Speter} 281251881Speter 282251881Speter 283251881Spetersvn_error_t * 284251881Spetersvn_fs_base__retry(svn_fs_t *fs, 285251881Speter svn_error_t *(*txn_body)(void *baton, trail_t *trail), 286251881Speter void *baton, 287251881Speter svn_boolean_t destroy_trail_pool, 288251881Speter apr_pool_t *pool) 289251881Speter{ 290251881Speter return do_retry(fs, txn_body, baton, FALSE, destroy_trail_pool, pool, 291251881Speter NULL, NULL, 0); 292251881Speter} 293