diff options
Diffstat (limited to 'storage/bdb/txn/txn_recover.c')
-rw-r--r-- | storage/bdb/txn/txn_recover.c | 306 |
1 files changed, 306 insertions, 0 deletions
diff --git a/storage/bdb/txn/txn_recover.c b/storage/bdb/txn/txn_recover.c new file mode 100644 index 00000000000..732a82e5030 --- /dev/null +++ b/storage/bdb/txn/txn_recover.c @@ -0,0 +1,306 @@ +/*- + * See the file LICENSE for redistribution information. + * + * Copyright (c) 2001-2002 + * Sleepycat Software. All rights reserved. + */ + +#include "db_config.h" + +#ifndef lint +static const char revid[] = "$Id: txn_recover.c,v 1.36 2002/08/19 16:59:15 bostic Exp $"; +#endif /* not lint */ + +#ifndef NO_SYSTEM_INCLUDES +#include <sys/types.h> + +#include <string.h> +#endif + +#include "db_int.h" +#include "dbinc/txn.h" +#include "dbinc/db_page.h" +#include "dbinc/log.h" +#include "dbinc_auto/db_auto.h" +#include "dbinc_auto/crdel_auto.h" +#include "dbinc_auto/db_ext.h" + +/* + * __txn_continue + * Fill in the fields of the local transaction structure given + * the detail transaction structure. + * + * XXX + * I'm not sure that we work correctly with nested txns. + * + * PUBLIC: void __txn_continue __P((DB_ENV *, DB_TXN *, TXN_DETAIL *, size_t)); + */ +void +__txn_continue(env, txnp, td, off) + DB_ENV *env; + DB_TXN *txnp; + TXN_DETAIL *td; + size_t off; +{ + txnp->mgrp = env->tx_handle; + txnp->parent = NULL; + txnp->last_lsn = td->last_lsn; + txnp->txnid = td->txnid; + txnp->off = (roff_t)off; + + txnp->abort = __txn_abort; + txnp->commit = __txn_commit; + txnp->discard = __txn_discard; + txnp->id = __txn_id; + txnp->prepare = __txn_prepare; + + txnp->flags = 0; +} + +/* + * __txn_map_gid + * Return the txn that corresponds to this global ID. + * + * PUBLIC: int __txn_map_gid __P((DB_ENV *, + * PUBLIC: u_int8_t *, TXN_DETAIL **, size_t *)); + */ +int +__txn_map_gid(dbenv, gid, tdp, offp) + DB_ENV *dbenv; + u_int8_t *gid; + TXN_DETAIL **tdp; + size_t *offp; +{ + DB_TXNMGR *mgr; + DB_TXNREGION *tmr; + + mgr = dbenv->tx_handle; + tmr = mgr->reginfo.primary; + + /* + * Search the internal active transaction table to find the + * matching xid. If this is a performance hit, then we + * can create a hash table, but I doubt it's worth it. + */ + R_LOCK(dbenv, &mgr->reginfo); + for (*tdp = SH_TAILQ_FIRST(&tmr->active_txn, __txn_detail); + *tdp != NULL; + *tdp = SH_TAILQ_NEXT(*tdp, links, __txn_detail)) + if (memcmp(gid, (*tdp)->xid, sizeof((*tdp)->xid)) == 0) + break; + R_UNLOCK(dbenv, &mgr->reginfo); + + if (*tdp == NULL) + return (EINVAL); + + *offp = R_OFFSET(&mgr->reginfo, *tdp); + return (0); +} + +/* + * __txn_recover -- + * Public interface to retrieve the list of prepared, but not yet + * commited transactions. See __txn_get_prepared for details. This + * function and __db_xa_recover both wrap that one. + * + * PUBLIC: int __txn_recover + * PUBLIC: __P((DB_ENV *, DB_PREPLIST *, long, long *, u_int32_t)); + */ +int +__txn_recover(dbenv, preplist, count, retp, flags) + DB_ENV *dbenv; + DB_PREPLIST *preplist; + long count, *retp; + u_int32_t flags; +{ + PANIC_CHECK(dbenv); + ENV_REQUIRES_CONFIG( + dbenv, dbenv->tx_handle, "txn_recover", DB_INIT_TXN); + + if (F_ISSET((DB_TXNREGION *) + ((DB_TXNMGR *)dbenv->tx_handle)->reginfo.primary, + TXN_IN_RECOVERY)) { + __db_err(dbenv, "operation not permitted while in recovery"); + return (EINVAL); + } + return (__txn_get_prepared(dbenv, NULL, preplist, count, retp, flags)); +} + +/* + * __txn_get_prepared -- + * Returns a list of prepared (and for XA, heuristically completed) + * transactions (less than or equal to the count parameter). One of + * xids or txns must be set to point to an array of the appropriate type. + * The count parameter indicates the number of entries in the xids and/or + * txns array. The retp parameter will be set to indicate the number of + * entries returned in the xids/txns array. Flags indicates the operation, + * one of DB_FIRST or DB_NEXT. + * + * PUBLIC: int __txn_get_prepared __P((DB_ENV *, + * PUBLIC: XID *, DB_PREPLIST *, long, long *, u_int32_t)); + */ +int +__txn_get_prepared(dbenv, xids, txns, count, retp, flags) + DB_ENV *dbenv; + XID *xids; + DB_PREPLIST *txns; + long count; /* This is long for XA compatibility. */ + long *retp; + u_int32_t flags; +{ + DBT data; + DB_LOGC *logc; + DB_LSN min, open_lsn; + DB_PREPLIST *prepp; + DB_TXNMGR *mgr; + DB_TXNREGION *tmr; + TXN_DETAIL *td; + XID *xidp; + __txn_ckp_args *ckp_args; + long i; + int nrestores, open_files, ret, t_ret; + void *txninfo; + + *retp = 0; + + logc = NULL; + MAX_LSN(min); + prepp = txns; + xidp = xids; + nrestores = ret = 0; + open_files = 1; + + /* + * If we are starting a scan, then we traverse the active transaction + * list once making sure that all transactions are marked as not having + * been collected. Then on each pass, we mark the ones we collected + * so that if we cannot collect them all at once, we can finish up + * next time with a continue. + */ + + mgr = dbenv->tx_handle; + tmr = mgr->reginfo.primary; + + /* + * During this pass we need to figure out if we are going to need + * to open files. We need to open files if we've never collected + * before (in which case, none of the COLLECTED bits will be set) + * and the ones that we are collecting are restored (if they aren't + * restored, then we never crashed; just the main server did). + */ + R_LOCK(dbenv, &mgr->reginfo); + if (flags == DB_FIRST) { + for (td = SH_TAILQ_FIRST(&tmr->active_txn, __txn_detail); + td != NULL; + td = SH_TAILQ_NEXT(td, links, __txn_detail)) { + if (F_ISSET(td, TXN_RESTORED)) + nrestores++; + if (F_ISSET(td, TXN_COLLECTED)) + open_files = 0; + F_CLR(td, TXN_COLLECTED); + } + mgr->n_discards = 0; + } else + open_files = 0; + + /* Now begin collecting active transactions. */ + for (td = SH_TAILQ_FIRST(&tmr->active_txn, __txn_detail); + td != NULL && *retp < count; + td = SH_TAILQ_NEXT(td, links, __txn_detail)) { + if (td->status != TXN_PREPARED || F_ISSET(td, TXN_COLLECTED)) + continue; + + if (xids != NULL) { + xidp->formatID = td->format; + xidp->gtrid_length = td->gtrid; + xidp->bqual_length = td->bqual; + memcpy(xidp->data, td->xid, sizeof(td->xid)); + xidp++; + } + + if (txns != NULL) { + if ((ret = __os_calloc(dbenv, + 1, sizeof(DB_TXN), &prepp->txn)) != 0) + goto err; + __txn_continue(dbenv, + prepp->txn, td, R_OFFSET(&mgr->reginfo, td)); + F_SET(prepp->txn, TXN_MALLOC); + memcpy(prepp->gid, td->xid, sizeof(td->xid)); + prepp++; + } + + if (log_compare(&td->begin_lsn, &min) < 0) + min = td->begin_lsn; + + (*retp)++; + F_SET(td, TXN_COLLECTED); + } + R_UNLOCK(dbenv, &mgr->reginfo); + + /* + * Now link all the transactions into the transaction manager's list. + */ + if (txns != NULL) { + MUTEX_THREAD_LOCK(dbenv, mgr->mutexp); + for (i = 0; i < *retp; i++) + TAILQ_INSERT_TAIL(&mgr->txn_chain, txns[i].txn, links); + MUTEX_THREAD_UNLOCK(dbenv, mgr->mutexp); + } + + if (open_files && nrestores && *retp != 0 && !IS_MAX_LSN(min)) { + /* + * Figure out the last checkpoint before the smallest + * start_lsn in the region. + */ + F_SET((DB_LOG *)dbenv->lg_handle, DBLOG_RECOVER); + + if ((ret = dbenv->log_cursor(dbenv, &logc, 0)) != 0) + goto err; + + memset(&data, 0, sizeof(data)); + if ((ret = __txn_getckp(dbenv, &open_lsn)) == 0) + while (!IS_ZERO_LSN(open_lsn) && (ret = + logc->get(logc, &open_lsn, &data, DB_SET)) == 0 && + log_compare(&min, &open_lsn) < 0) { + /* Format the log record. */ + if ((ret = __txn_ckp_read(dbenv, + data.data, &ckp_args)) != 0) { + __db_err(dbenv, + "Invalid checkpoint record at [%lu][%lu]", + (u_long)open_lsn.file, + (u_long)open_lsn.offset); + goto err; + } + open_lsn = ckp_args->last_ckp; + __os_free(dbenv, ckp_args); + } + + /* + * There are three ways by which we may have gotten here. + * - We got a DB_NOTFOUND -- we need to read the first + * log record. + * - We found a checkpoint before min. We're done. + * - We found a checkpoint after min who's last_ckp is 0. We + * need to start at the beginning of the log. + */ + if ((ret == DB_NOTFOUND || IS_ZERO_LSN(open_lsn)) && + (ret = logc->get(logc, &open_lsn, &data, DB_FIRST)) != 0) { + __db_err(dbenv, "No log records"); + goto err; + } + + if ((ret = __db_txnlist_init(dbenv, 0, 0, NULL, &txninfo)) != 0) + goto err; + ret = __env_openfiles(dbenv, logc, + txninfo, &data, &open_lsn, NULL, 0, 0); + if (txninfo != NULL) + __db_txnlist_end(dbenv, txninfo); + } + +err: F_CLR((DB_LOG *)dbenv->lg_handle, DBLOG_RECOVER); + + if (logc != NULL && (t_ret = logc->close(logc, 0)) != 0 && ret == 0) + ret = t_ret; + + return (ret); +} |