1251881Speter/* trail.h : internal interface to backing out of aborted Berkeley DB txns 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#ifndef SVN_LIBSVN_FS_TRAIL_H 24251881Speter#define SVN_LIBSVN_FS_TRAIL_H 25251881Speter 26251881Speter#define SVN_WANT_BDB 27251881Speter#include "svn_private_config.h" 28251881Speter 29251881Speter#include <apr_pools.h> 30251881Speter#include "svn_fs.h" 31251881Speter#include "fs.h" 32251881Speter 33251881Speter#ifdef __cplusplus 34251881Speterextern "C" { 35251881Speter#endif /* __cplusplus */ 36251881Speter 37251881Speter 38251881Speter/* "How do I get a trail object? All these functions in the 39251881Speter filesystem expect them, and I can't find a function that returns 40251881Speter one." 41251881Speter 42251881Speter Well, there isn't a function that returns a trail. All trails come 43251881Speter from svn_fs_base__retry_txn. Here's how to use that: 44251881Speter 45251881Speter When using Berkeley DB transactions to protect the integrity of a 46251881Speter database, there are several things you need to keep in mind: 47251881Speter 48251881Speter - Any Berkeley DB operation you perform as part of a Berkeley DB 49251881Speter transaction may return DB_LOCK_DEADLOCK, meaning that your 50251881Speter operation interferes with some other transaction in progress. 51251881Speter When this happens, you must abort the transaction, which undoes 52251881Speter all the changes you've made so far, and try it again. So every 53251881Speter piece of code you ever write to bang on the DB needs to be 54251881Speter wrapped up in a retry loop. 55251881Speter 56251881Speter - If, while you're doing your database operations, you also change 57251881Speter some in-memory data structures, then you may want to revert those 58251881Speter changes if the transaction deadlocks and needs to be retried. 59251881Speter 60251881Speter - If you get a `real' error (i.e., something other than 61251881Speter DB_LOCK_DEADLOCK), you must abort your DB transaction, to release 62251881Speter its locks and return the database to its previous state. 63251881Speter Similarly, you may want to unroll some changes you've made to 64251881Speter in-memory data structures. 65251881Speter 66251881Speter - Since a transaction insulates you from database changes made by 67251881Speter other processes, it's often possible to cache information about 68251881Speter database contents while the transaction lasts. However, this 69251881Speter cache may become stale once your transaction is over. So you may 70251881Speter need to clear your cache once the transaction completes, either 71251881Speter successfully or unsuccessfully. 72251881Speter 73251881Speter The `svn_fs_base__retry_txn' function and its friends help you manage 74251881Speter some of that, in one nice package. 75251881Speter 76251881Speter To use it, write your code in a function like this: 77251881Speter 78251881Speter static svn_error_t * 79251881Speter txn_body_do_my_thing (void *baton, 80251881Speter trail_t *trail) 81251881Speter { 82251881Speter ... 83251881Speter Do everything which needs to be protected by a Berkeley DB 84251881Speter transaction here. Use TRAIL->db_txn as your Berkeley DB 85251881Speter transaction, and do your allocation in TRAIL->pool. Pass 86251881Speter TRAIL on through to any functions which require one. 87251881Speter 88251881Speter If a Berkeley DB operation returns DB_LOCK_DEADLOCK, just 89251881Speter return that using the normal Subversion error mechanism 90251881Speter (using DB_ERR, for example); don't write a retry loop. If you 91251881Speter encounter some other kind of error, return it in the normal 92251881Speter fashion. 93251881Speter ... 94251881Speter } 95251881Speter 96251881Speter Now, call svn_fs_base__retry_txn, passing a pointer to your function as 97251881Speter an argument: 98251881Speter 99251881Speter err = svn_fs_base__retry_txn (fs, txn_body_do_my_thing, baton, pool); 100251881Speter 101251881Speter This will simply invoke your function `txn_body_do_my_thing', 102251881Speter passing BATON through unchanged, and providing a fresh TRAIL 103251881Speter object, containing a pointer to the filesystem object, a Berkeley 104251881Speter DB transaction and an APR pool -- a subpool of POOL -- you should 105251881Speter use. 106251881Speter 107251881Speter If your function returns a Subversion error wrapping a Berkeley DB 108251881Speter DB_LOCK_DEADLOCK error, `svn_fs_base__retry_txn' will abort the trail's 109251881Speter Berkeley DB transaction for you (thus undoing any database changes 110251881Speter you've made), free the trail's subpool (thus undoing any allocation 111251881Speter you may have done), and try the whole thing again with a new trail, 112251881Speter containing a new Berkeley DB transaction and pool. 113251881Speter 114251881Speter If your function returns any other kind of Subversion error, 115251881Speter `svn_fs_base__retry_txn' will abort the trail's Berkeley DB transaction, 116251881Speter free the subpool, and return your error to its caller. 117251881Speter 118251881Speter If, heavens forbid, your function actually succeeds, returning 119251881Speter SVN_NO_ERROR, `svn_fs_base__retry_txn' commits the trail's Berkeley DB 120251881Speter transaction, thus making your DB changes permanent, leaves the 121251881Speter trail's pool alone so all the objects it contains are still 122251881Speter around (unless you request otherwise), and returns SVN_NO_ERROR. 123251881Speter 124251881Speter 125251881Speter Keep the amount of work done in a trail small. C-Mike Pilato said to me: 126251881Speter 127251881Speter I want to draw your attention to something that you may or may not realize 128251881Speter about designing for the BDB backend. The 'trail' objects are (generally) 129251881Speter representative of Berkeley DB transactions -- that part I'm sure you know. 130251881Speter But you might not realize the value of keeping transactions as small as 131251881Speter possible. Berkeley DB will accumulate locks (which I believe are 132251881Speter page-level, not as tight as row-level like you might hope) over the course 133251881Speter of a transaction, releasing those locks only at transaction commit/abort. 134251881Speter Berkeley DB backends are configured to have a maximum number of locks and 135251881Speter lockers allowed, and it's easier than you might think to hit the max-locks 136251881Speter thresholds (especially under high concurrency) and see an error (typically a 137251881Speter "Cannot allocate memory") result from that. 138251881Speter 139251881Speter For example, in [a loop] you are writing a bunch of rows to the 140251881Speter `changes' table. Could be 10. Could be 100,000. 100,000 writes and 141251881Speter associated locks might be a problem or it might not. But I use it as a way 142251881Speter to encourage you to think about reducing the amount of work you spend in any 143251881Speter one trail [...]. 144251881Speter*/ 145251881Speter 146251881Speterstruct trail_t 147251881Speter{ 148251881Speter /* A Berkeley DB transaction. */ 149251881Speter DB_TXN *db_txn; 150251881Speter 151251881Speter /* The filesystem object with which this trail is associated. */ 152251881Speter svn_fs_t *fs; 153251881Speter 154251881Speter /* A pool to allocate things in as part of that transaction --- a 155251881Speter subpool of the one passed to `begin_trail'. We destroy this pool 156251881Speter if we abort the transaction, and leave it around otherwise. */ 157251881Speter apr_pool_t *pool; 158251881Speter 159251881Speter#if defined(SVN_FS__TRAIL_DEBUG) 160251881Speter struct trail_debug_t *trail_debug; 161251881Speter#endif 162251881Speter}; 163251881Spetertypedef struct trail_t trail_t; 164251881Speter 165251881Speter 166251881Speter/* Try a Berkeley DB transaction repeatedly until it doesn't deadlock. 167251881Speter 168251881Speter That is: 169251881Speter - Begin a new Berkeley DB transaction, DB_TXN, in the filesystem FS. 170251881Speter - Allocate a subpool of POOL, TXN_POOL. 171251881Speter - Start a new trail, TRAIL, pointing to DB_TXN and TXN_POOL. 172251881Speter - Apply TXN_BODY to BATON and TRAIL. TXN_BODY should try to do 173251881Speter some series of DB operations which needs to be atomic, using 174251881Speter TRAIL->db_txn as the transaction, and TRAIL->pool for allocation. 175251881Speter If a DB operation deadlocks, or if any other kind of error 176251881Speter happens, TXN_BODY should simply return with an appropriate 177251881Speter svn_error_t, E. 178251881Speter - If TXN_BODY returns SVN_NO_ERROR, then commit the transaction, 179251881Speter run any completion functions, and return SVN_NO_ERROR. Do *not* 180251881Speter free TXN_POOL (unless DESTROY_TRAIL_POOL is set). 181251881Speter - If E is a Berkeley DB error indicating that a deadlock occurred, 182251881Speter abort the DB transaction and free TXN_POOL. Then retry the whole 183251881Speter thing from the top. 184251881Speter - If E is any other kind of error, free TXN_POOL and return E. 185251881Speter 186251881Speter One benefit of using this function is that it makes it easy to 187251881Speter ensure that whatever transactions a filesystem function starts, it 188251881Speter either aborts or commits before it returns. If we don't somehow 189251881Speter complete all our transactions, later operations could deadlock. */ 190251881Spetersvn_error_t * 191251881Spetersvn_fs_base__retry_txn(svn_fs_t *fs, 192251881Speter svn_error_t *(*txn_body)(void *baton, 193251881Speter trail_t *trail), 194251881Speter void *baton, 195251881Speter svn_boolean_t destroy_trail_pool, 196251881Speter apr_pool_t *pool); 197251881Speter 198251881Spetersvn_error_t * 199251881Spetersvn_fs_base__retry_debug(svn_fs_t *fs, 200251881Speter svn_error_t *(*txn_body)(void *baton, 201251881Speter trail_t *trail), 202251881Speter void *baton, 203251881Speter svn_boolean_t destroy_trail_pool, 204251881Speter apr_pool_t *pool, 205251881Speter const char *txn_body_fn_name, 206251881Speter const char *filename, 207251881Speter int line); 208251881Speter 209251881Speter#if defined(SVN_FS__TRAIL_DEBUG) 210251881Speter#define svn_fs_base__retry_txn(fs, txn_body, baton, destroy, pool) \ 211251881Speter svn_fs_base__retry_debug(fs, txn_body, baton, destroy, pool, \ 212251881Speter #txn_body, __FILE__, __LINE__) 213251881Speter#endif 214251881Speter 215251881Speter 216251881Speter/* Try an action repeatedly until it doesn't deadlock. This is 217251881Speter exactly like svn_fs_base__retry_txn() (whose documentation you really 218251881Speter should read) except that no Berkeley DB transaction is created. */ 219251881Spetersvn_error_t *svn_fs_base__retry(svn_fs_t *fs, 220251881Speter svn_error_t *(*txn_body)(void *baton, 221251881Speter trail_t *trail), 222251881Speter void *baton, 223251881Speter svn_boolean_t destroy_trail_pool, 224251881Speter apr_pool_t *pool); 225251881Speter 226251881Speter 227251881Speter/* Record that OPeration is being done on TABLE in the TRAIL. */ 228251881Speter#if defined(SVN_FS__TRAIL_DEBUG) 229251881Spetervoid svn_fs_base__trail_debug(trail_t *trail, const char *table, 230251881Speter const char *op); 231251881Speter#else 232251881Speter#define svn_fs_base__trail_debug(trail, table, operation) 233251881Speter#endif 234251881Speter 235251881Speter#ifdef __cplusplus 236251881Speter} 237251881Speter#endif /* __cplusplus */ 238251881Speter 239251881Speter#endif /* SVN_LIBSVN_FS_TRAIL_H */ 240